diff options
76 files changed, 3399 insertions, 1564 deletions
@@ -6707,6 +6707,8 @@ if [ "$CFG_JAVASCRIPTCORE_JIT" = "yes" ] || [ "$CFG_JAVASCRIPTCORE_JIT" = "auto" if [ $? != "0" ]; then CFG_JAVASCRIPTCORE_JIT=no fi + elif [ "$XPLATFORM" = "symbian-gcce" ]; then + CFG_JAVASCRIPTCORE_JIT=no fi fi @@ -7412,11 +7414,11 @@ EOF canBuildWebKit="no" canBuildQtConcurrent="no" ;; - symbian/*-gcce) + symbian-gcce) canBuildWebKit="no" canBuildQtConcurrent="no" ;; - symbian/*-armcc) + symbian-armcc) canBuildQtConcurrent="no" ;; esac diff --git a/mkspecs/common/symbian/symbian-makefile.conf b/mkspecs/common/symbian/symbian-makefile.conf index cffc859..b248117 100644 --- a/mkspecs/common/symbian/symbian-makefile.conf +++ b/mkspecs/common/symbian/symbian-makefile.conf @@ -33,7 +33,7 @@ QMAKE_LINK_OBJECT_SCRIPT = objects is_using_gnupoc { DEFINES *= __QT_PRODUCT_INCLUDE_IS_LOWERCASE__ } -QMAKE_SYMBIAN_INCLUDES = $$[QT_INSTALL_DATA]/mkspecs/common/symbian/symbianincludes.h +QMAKE_SYMBIAN_INCLUDES = $$IN_PWD/symbianincludes.h symbian-armcc { QMAKE_CFLAGS += --preinclude $$QMAKE_SYMBIAN_INCLUDES QMAKE_CXXFLAGS += --preinclude $$QMAKE_SYMBIAN_INCLUDES diff --git a/mkspecs/symbian-gcce/qmake.conf b/mkspecs/symbian-gcce/qmake.conf index 1d1053c..f550751a 100644 --- a/mkspecs/symbian-gcce/qmake.conf +++ b/mkspecs/symbian-gcce/qmake.conf @@ -54,7 +54,7 @@ DEFINES += __GCCE__ \ UNICODE QMAKE_LFLAGS_APP += --entry=_E32Startup -u _E32Startup -QMAKE_LFLAGS_SHLIB += -shared --default-symver --entry _E32Dll -u _E32Dll +QMAKE_LFLAGS_SHLIB += -shared --default-symver --entry=_E32Dll -u _E32Dll QMAKE_LFLAGS_PLUGIN += $$QMAKE_LFLAGS_SHLIB gcceExtraFlags = --include=$${EPOCROOT}epoc32/include/gcce/gcce.h -march=armv5t -mapcs -mthumb-interwork -nostdinc -c -msoft-float -T script diff --git a/src/3rdparty/phonon/qt7/videowidget.mm b/src/3rdparty/phonon/qt7/videowidget.mm index c281e16..0600268 100644 --- a/src/3rdparty/phonon/qt7/videowidget.mm +++ b/src/3rdparty/phonon/qt7/videowidget.mm @@ -446,10 +446,12 @@ public: void setDrawFrameRect(const QRect &rect) { - m_movieLayer.frame.origin.x = rect.x(); - m_movieLayer.frame.origin.y = rect.y(); - m_movieLayer.frame.size.width = rect.width(); - m_movieLayer.frame.size.height = rect.height(); + CGRect frame = m_movieLayer.frame; + frame.origin.x = rect.x(); + frame.origin.y = rect.y(); + frame.size.width = rect.width(); + frame.size.height = rect.height(); + m_movieLayer.frame = frame; } #else // QT_MAC_USE_COCOA == false diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 134ef2f..d7f8846 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1174,6 +1174,7 @@ bool qSharedBuild() \value MV_10_4 Mac OS X 10.4 \value MV_10_5 Mac OS X 10.5 \value MV_10_6 Mac OS X 10.6 + \value MV_10_7 Mac OS X 10.7 \value MV_Unknown An unknown and currently unsupported platform \value MV_CHEETAH Apple codename for MV_10_0 @@ -1183,6 +1184,7 @@ bool qSharedBuild() \value MV_TIGER Apple codename for MV_10_4 \value MV_LEOPARD Apple codename for MV_10_5 \value MV_SNOWLEOPARD Apple codename for MV_10_6 + \value MV_LION Apple codename for MV_10_7 \sa WinVersion, SymbianVersion */ diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 1879537..d5106cc 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -322,7 +322,10 @@ namespace QT_NAMESPACE {} # if !defined(MAC_OS_X_VERSION_10_6) # define MAC_OS_X_VERSION_10_6 MAC_OS_X_VERSION_10_5 + 1 # endif -# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6) +# if !defined(MAC_OS_X_VERSION_10_7) +# define MAC_OS_X_VERSION_10_7 MAC_OS_X_VERSION_10_6 + 1 +# endif +# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_7) # warning "This version of Mac OS X is unsupported" # endif #endif @@ -1571,6 +1574,7 @@ public: MV_10_4 = 0x0006, MV_10_5 = 0x0007, MV_10_6 = 0x0008, + MV_10_7 = 0x0009, /* codenames */ MV_CHEETAH = MV_10_0, @@ -1579,7 +1583,8 @@ public: MV_PANTHER = MV_10_3, MV_TIGER = MV_10_4, MV_LEOPARD = MV_10_5, - MV_SNOWLEOPARD = MV_10_6 + MV_SNOWLEOPARD = MV_10_6, + MV_LION = MV_10_7 }; static const MacVersion MacintoshVersion; #endif @@ -1828,32 +1833,32 @@ public: inline ~QGlobalStatic() { pointer = 0; } }; -#define Q_GLOBAL_STATIC(TYPE, NAME) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME; \ - static QGlobalStatic<TYPE > global_##NAME(&this_##NAME); \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC(TYPE, NAME) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable; \ + static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME ARGS; \ - static QGlobalStatic<TYPE > global_##NAME(&this_##NAME); \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable ARGS; \ + static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME; \ - static QGlobalStatic<TYPE > global_##NAME(0); \ - if (!global_##NAME.pointer) { \ - TYPE *x = global_##NAME.pointer = &this_##NAME; \ - INITIALIZER; \ - } \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable; \ + static QGlobalStatic<TYPE > thisGlobalStatic(0); \ + if (!thisGlobalStatic.pointer) { \ + TYPE *x = thisGlobalStatic.pointer = &thisVariable; \ + INITIALIZER; \ + } \ + return thisGlobalStatic.pointer; \ } #else @@ -1888,50 +1893,50 @@ public: } }; -#define Q_GLOBAL_STATIC_INIT(TYPE, NAME) \ - static QGlobalStatic<TYPE > this_##NAME = { Q_BASIC_ATOMIC_INITIALIZER(0), false } - -#define Q_GLOBAL_STATIC(TYPE, NAME) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - TYPE *x = new TYPE; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC(TYPE, NAME) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + TYPE *x = new TYPE; \ + if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \ + delete x; \ + else \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + } \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - TYPE *x = new TYPE ARGS; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + TYPE *x = new TYPE ARGS; \ + if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \ + delete x; \ + else \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + } \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - QScopedPointer<TYPE > x(new TYPE); \ - INITIALIZER; \ - if (this_##NAME.pointer.testAndSetOrdered(0, x.data())) { \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - x.take(); \ - } \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + QScopedPointer<TYPE > x(new TYPE); \ + INITIALIZER; \ + if (thisGlobalStatic.pointer.testAndSetOrdered(0, x.data())) { \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + x.take(); \ + } \ + } \ + return thisGlobalStatic.pointer; \ } #endif diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp index 235f671..71cf92d 100644 --- a/src/corelib/io/qnoncontiguousbytedevice.cpp +++ b/src/corelib/io/qnoncontiguousbytedevice.cpp @@ -153,6 +153,7 @@ void QNonContiguousByteDevice::disableReset() resetDisabled = true; } +// FIXME we should scrap this whole implementation and instead change the ByteArrayImpl to be able to cope with sub-arrays? QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice() { buffer = b; @@ -244,7 +245,7 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size() return byteArray->size(); } -QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb) +QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb) : QNonContiguousByteDevice(), currentPosition(0) { ringBuffer = rb; @@ -355,6 +356,11 @@ bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) // normal advancement currentReadBufferPosition += amount; + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + else + emit readProgress(totalAdvancements, size()); + // advancing over that what has actually been read before if (currentReadBufferPosition > currentReadBufferAmount) { qint64 i = currentReadBufferPosition - currentReadBufferAmount; @@ -370,10 +376,6 @@ bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) currentReadBufferAmount = 0; } - if (size() == -1) - emit readProgress(totalAdvancements, totalAdvancements); - else - emit readProgress(totalAdvancements, size()); return true; } @@ -505,7 +507,7 @@ QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *dev \internal */ -QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer) +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QSharedPointer<QRingBuffer> ringBuffer) { return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer); } diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h index ff946ed..5e0b1bb 100644 --- a/src/corelib/io/qnoncontiguousbytedevice_p.h +++ b/src/corelib/io/qnoncontiguousbytedevice_p.h @@ -57,6 +57,7 @@ #include <QtCore/qbytearray.h> #include <QtCore/qbuffer.h> #include <QtCore/qiodevice.h> +#include <QtCore/QSharedPointer> #include "private/qringbuffer_p.h" QT_BEGIN_NAMESPACE @@ -70,6 +71,7 @@ public: virtual bool atEnd() = 0; virtual bool reset() = 0; void disableReset(); + bool isResetDisabled() { return resetDisabled; } virtual qint64 size() = 0; virtual ~QNonContiguousByteDevice(); @@ -89,7 +91,7 @@ class Q_CORE_EXPORT QNonContiguousByteDeviceFactory public: static QNonContiguousByteDevice* create(QIODevice *device); static QNonContiguousByteDevice* create(QByteArray *byteArray); - static QNonContiguousByteDevice* create(QRingBuffer *ringBuffer); + static QNonContiguousByteDevice* create(QSharedPointer<QRingBuffer> ringBuffer); static QIODevice* wrap(QNonContiguousByteDevice* byteDevice); }; @@ -114,7 +116,7 @@ protected: class QNonContiguousByteDeviceRingBufferImpl : public QNonContiguousByteDevice { public: - QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb); + QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb); ~QNonContiguousByteDeviceRingBufferImpl(); const char* readPointer(qint64 maximumLength, qint64 &len); bool advanceReadPointer(qint64 amount); @@ -122,13 +124,14 @@ public: bool reset(); qint64 size(); protected: - QRingBuffer* ringBuffer; + QSharedPointer<QRingBuffer> ringBuffer; qint64 currentPosition; }; class QNonContiguousByteDeviceIoDeviceImpl : public QNonContiguousByteDevice { + Q_OBJECT public: QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d); ~QNonContiguousByteDeviceIoDeviceImpl(); @@ -150,6 +153,7 @@ protected: class QNonContiguousByteDeviceBufferImpl : public QNonContiguousByteDevice { + Q_OBJECT public: QNonContiguousByteDeviceBufferImpl(QBuffer *b); ~QNonContiguousByteDeviceBufferImpl(); diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index c003214..a903007 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -677,7 +677,7 @@ bool QChar::isSymbol() const \since 4.7 Returns true if the UCS-4-encoded character specified by \a ucs4 - is the high part of a utf16 surrogate + is the low part of a utf16 surrogate (ie. if its code point is between 0xdc00 and 0xdfff, inclusive). */ @@ -686,7 +686,7 @@ bool QChar::isSymbol() const \since 4.7 Returns true if the UCS-4-encoded character specified by \a ucs4 - can be splited to the high and low parts of a utf16 surrogate + can be split into the high and low parts of a utf16 surrogate (ie. if its code point is greater than or equals to 0x10000). */ diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp index dd27aff..cb8eb6a 100644 --- a/src/gui/dialogs/qfilesystemmodel.cpp +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -1458,7 +1458,6 @@ void QFileSystemModel::setIconProvider(QFileIconProvider *provider) { Q_D(QFileSystemModel); d->fileInfoGatherer.setIconProvider(provider); - QApplication::processEvents(); d->root.updateIcon(provider, QString()); } diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h index b41a4b5..5426159 100644 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ b/src/gui/kernel/qcocoapanel_mac_p.h @@ -60,6 +60,13 @@ QT_FORWARD_DECLARE_CLASS(QStringList); QT_FORWARD_DECLARE_CLASS(QCocoaDropData); +@interface NSPanel (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end + @interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { QStringList *currentCustomDragTypes; QCocoaDropData *dropData; diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h index e6b50f5..d567cab 100644 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ b/src/gui/kernel/qcocoawindow_mac_p.h @@ -74,6 +74,13 @@ QT_FORWARD_DECLARE_CLASS(QCocoaDropData); - (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget); @end +@interface NSWindow (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end + @interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { QStringList *currentCustomDragTypes; QCocoaDropData *dropData; diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 98f62ca..1a93e8e 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -184,7 +184,7 @@ struct dndenum_mapper bool Qt2Mac; }; -#ifdef QT_MAC_USE_COCOA && __OBJC__ +#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__) static dndenum_mapper dnd_enums[] = { { NSDragOperationLink, Qt::LinkAction, true }, diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index b378b78..4bff5ac 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -1496,7 +1496,7 @@ void QWidgetPrivate::setWindowIcon_sys(bool forceReset) || !QX11Info::appDefaultColormap(xinfo.screen())) { // unknown DE or non-default visual/colormap, use 1bpp bitmap if (!forceReset || !topData->iconPixmap) - topData->iconPixmap = new QBitmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64)))); + topData->iconPixmap = new QPixmap(qt_toX11Pixmap(QBitmap(icon.pixmap(QSize(64,64))))); pixmap_handle = topData->iconPixmap->handle(); } else { // default depth, use a normal pixmap (even though this diff --git a/src/network/access/access.pri b/src/network/access/access.pri index 7497c1a..57a79b3 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -33,7 +33,8 @@ HEADERS += \ access/qabstractnetworkcache_p.h \ access/qabstractnetworkcache.h \ access/qnetworkdiskcache_p.h \ - access/qnetworkdiskcache.h + access/qnetworkdiskcache.h \ + access/qhttpthreaddelegate_p.h SOURCES += \ access/qftp.cpp \ @@ -60,6 +61,7 @@ SOURCES += \ access/qnetworkreplydataimpl.cpp \ access/qnetworkreplyfileimpl.cpp \ access/qabstractnetworkcache.cpp \ - access/qnetworkdiskcache.cpp + access/qnetworkdiskcache.cpp \ + access/qhttpthreaddelegate.cpp include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index e94b099..3502113 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -286,7 +286,13 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, int i = indexOf(socket); // remove the corrupt data if any reply->d_func()->eraseData(); + + // Clean the channel channels[i].close(); + channels[i].reply = 0; + channels[i].request = QHttpNetworkRequest(); + channels[i].requeueCurrentlyPipelinedRequests(); + // send the next request QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); } diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 079f608..41bc64a 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -253,7 +253,7 @@ bool QHttpNetworkConnectionChannel::sendRequest() #endif { // get pointer to upload data - qint64 currentReadSize; + qint64 currentReadSize = 0; qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written); const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); @@ -476,7 +476,8 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } #ifndef QT_NO_COMPRESS else if (!expand(false)) { // expand a chunk if possible - return; // ### expand failed + // If expand() failed we can just return, it had already called connection->emitReplyError() + return; } #endif } @@ -645,28 +646,38 @@ void QHttpNetworkConnectionChannel::allDone() { #ifndef QT_NO_COMPRESS // expand the whole data. - if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) - expand(true); // ### if expand returns false, its an error + if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) { + bool expandResult = expand(true); + // If expand() failed we can just return, it had already called connection->emitReplyError() + if (!expandResult) + return; + } #endif + + if (!reply) { + qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.nokia.com/"; + return; + } + // while handling 401 & 407, we might reset the status code, so save this. bool emitFinished = reply->d_func()->shouldEmitSignals(); - handleStatus(); - // ### at this point there should be no more data on the socket - // close if server requested bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled(); - if (connectionCloseEnabled) - close(); + detectPipeliningSupport(); + + handleStatus(); + // handleStatus() might have removed the reply because it already called connection->emitReplyError() + // queue the finished signal, this is required since we might send new requests from // slot connected to it. The socket will not fire readyRead signal, if we are already // in the slot connected to readyRead - if (emitFinished) + if (reply && emitFinished) QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection); + + // reset the reconnection attempts after we receive a complete reply. // in case of failures, each channel will attempt two reconnects before emitting error. reconnectAttempts = 2; - detectPipeliningSupport(); - // now the channel can be seen as free/idle again, all signal emissions for the reply have been done this->state = QHttpNetworkConnectionChannel::IdleState; @@ -717,6 +728,8 @@ void QHttpNetworkConnectionChannel::allDone() // leading whitespace. QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } else if (alreadyPipelinedRequests.isEmpty()) { + if (connectionCloseEnabled) + close(); if (qobject_cast<QHttpNetworkConnection*>(connection)) QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 7f6b71f..704cf3a 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -177,6 +177,12 @@ qint64 QHttpNetworkReply::bytesAvailableNextBlock() const return -1; } +bool QHttpNetworkReply::readAnyAvailable() const +{ + Q_D(const QHttpNetworkReply); + return (d->responseData.bufferCount() > 0); +} + QByteArray QHttpNetworkReply::readAny() { Q_D(QHttpNetworkReply); diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 1856d7a..cc0f671 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -125,6 +125,7 @@ public: qint64 bytesAvailable() const; qint64 bytesAvailableNextBlock() const; + bool readAnyAvailable() const; QByteArray readAny(); QByteArray readAll(); void setDownstreamLimited(bool t); diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp new file mode 100644 index 0000000..b5cf00a --- /dev/null +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpthreaddelegate_p.h" + +#include <QThread> +#include <QTimer> +#include <QAuthenticator> +#include <QEventLoop> + +#include "private/qhttpnetworkreply_p.h" +#include "private/qnetworkaccesscache_p.h" +#include "private/qnoncontiguousbytedevice_p.h" + + +QT_BEGIN_NAMESPACE + +static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url) +{ + QNetworkReply::NetworkError code; + // we've got an error + switch (httpStatusCode) { + case 401: // Authorization required + code = QNetworkReply::AuthenticationRequiredError; + break; + + case 403: // Access denied + code = QNetworkReply::ContentOperationNotPermittedError; + break; + + case 404: // Not Found + code = QNetworkReply::ContentNotFoundError; + break; + + case 405: // Method Not Allowed + code = QNetworkReply::ContentOperationNotPermittedError; + break; + + case 407: + code = QNetworkReply::ProxyAuthenticationRequiredError; + break; + + default: + if (httpStatusCode > 500) { + // some kind of server error + code = QNetworkReply::ProtocolUnknownError; + } else if (httpStatusCode >= 400) { + // content error we did not handle above + code = QNetworkReply::UnknownContentError; + } else { + qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"", + httpStatusCode, qPrintable(url.toString())); + code = QNetworkReply::ProtocolFailure; + } + } + + return code; +} + + +static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy) +{ + QByteArray result; + QUrl copy = url; + bool isEncrypted = copy.scheme().toLower() == QLatin1String("https"); + copy.setPort(copy.port(isEncrypted ? 443 : 80)); + result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath | + QUrl::RemoveQuery | QUrl::RemoveFragment); + +#ifndef QT_NO_NETWORKPROXY + if (proxy && proxy->type() != QNetworkProxy::NoProxy) { + QUrl key; + + switch (proxy->type()) { + case QNetworkProxy::Socks5Proxy: + key.setScheme(QLatin1String("proxy-socks5")); + break; + + case QNetworkProxy::HttpProxy: + case QNetworkProxy::HttpCachingProxy: + key.setScheme(QLatin1String("proxy-http")); + break; + + default: + break; + } + + if (!key.scheme().isEmpty()) { + key.setUserName(proxy->user()); + key.setHost(proxy->hostName()); + key.setPort(proxy->port()); + key.setEncodedQuery(result); + result = key.toEncoded(); + } + } +#endif + + return "http-connection:" + result; +} + +class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection, + public QNetworkAccessCache::CacheableObject +{ + // Q_OBJECT +public: + QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt) + : QHttpNetworkConnection(hostName, port, encrypt) + { + setExpires(true); + setShareable(true); + } + + virtual void dispose() + { +#if 0 // sample code; do this right with the API + Q_ASSERT(!isWorking()); +#endif + delete this; + } +}; + + +QThreadStorage<QNetworkAccessCache *> QHttpThreadDelegate::connections; + + +QHttpThreadDelegate::~QHttpThreadDelegate() +{ + // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply + if (httpReply) { + delete httpReply; + } + + // Get the object cache that stores our QHttpNetworkConnection objects + // and release the entry for this QHttpNetworkConnection + if (connections.hasLocalData() && !cacheKey.isEmpty()) { + connections.localData()->releaseEntry(cacheKey); + } +} + + +QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) : + QObject(parent) + , ssl(false) + , downloadBufferMaximumSize(0) + , pendingDownloadData(0) + , pendingDownloadProgress(0) + , synchronous(false) + , incomingStatusCode(0) + , isPipeliningUsed(false) + , incomingContentLength(-1) + , incomingErrorCode(QNetworkReply::NoError) + , downloadBuffer(0) + , httpConnection(0) + , httpReply(0) +{ +} + +// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread +void QHttpThreadDelegate::startRequestSynchronously() +{ + synchronous = true; + + QEventLoop synchronousRequestLoop; + this->synchronousRequestLoop = &synchronousRequestLoop; + + // Worst case timeout + QTimer::singleShot(30*1000, this, SLOT(abortRequest())); + + QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection); + synchronousRequestLoop.exec(); + + connections.localData()->releaseEntry(cacheKey); + connections.setLocalData(0); + +} + + +// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread +void QHttpThreadDelegate::startRequest() +{ + // Check QThreadStorage for the QNetworkAccessCache + // If not there, create this connection cache + if (!connections.hasLocalData()) { + connections.setLocalData(new QNetworkAccessCache()); + } + + // check if we have an open connection to this host + QUrl urlCopy = httpRequest.url(); + urlCopy.setPort(urlCopy.port(ssl ? 443 : 80)); + + if (transparentProxy.type() != QNetworkProxy::NoProxy) + cacheKey = makeCacheKey(urlCopy, &transparentProxy); + else if (cacheProxy.type() != QNetworkProxy::NoProxy) + cacheKey = makeCacheKey(urlCopy, &cacheProxy); + else + cacheKey = makeCacheKey(urlCopy, 0); + + + // the http object is actually a QHttpNetworkConnection + httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey)); + if (httpConnection == 0) { + // no entry in cache; create an object + // the http object is actually a QHttpNetworkConnection + httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl); +#ifndef QT_NO_OPENSSL + // Set the QSslConfiguration from this QNetworkRequest. + if (ssl) { + httpConnection->setSslConfiguration(incomingSslConfiguration); + } +#endif + +#ifndef QT_NO_NETWORKPROXY + httpConnection->setTransparentProxy(transparentProxy); + httpConnection->setCacheProxy(cacheProxy); +#endif + + // cache the QHttpNetworkConnection corresponding to this cache key + connections.localData()->addEntry(cacheKey, httpConnection); + } + + + // Send the request to the connection + httpReply = httpConnection->sendRequest(httpRequest); + httpReply->setParent(this); + + // Connect the reply signals that we need to handle and then forward + if (synchronous) { + connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot())); + connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot())); + connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)), + this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString))); + + connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), + this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*))); + connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*))); + + // Don't care about ignored SSL errors for now in the synchronous HTTP case. + } else if (!synchronous) { + connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot())); + connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot())); + connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)), + this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString))); + // some signals are only interesting when normal asynchronous style is used + connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(httpReply,SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgressSlot(int,int))); + connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)), + this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*))); +#ifndef QT_NO_OPENSSL + connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>))); +#endif + + // In the asynchronous HTTP case we can just forward those signals + // Connect the reply signals that we can directly forward + connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), + this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*))); + connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + } +} + +// This gets called from the user thread or by the synchronous HTTP timeout timer +void QHttpThreadDelegate::abortRequest() +{ + if (httpReply) { + delete httpReply; + httpReply = 0; + this->deleteLater(); + } + + // Got aborted by the timeout timer + if (synchronous) + incomingErrorCode = QNetworkReply::TimeoutError; +} + +void QHttpThreadDelegate::readyReadSlot() +{ + // Don't do in zerocopy case + if (!downloadBuffer.isNull()) + return; + + while (httpReply->readAnyAvailable()) { + pendingDownloadData->fetchAndAddRelease(1); + emit downloadData(httpReply->readAny()); + } +} + +void QHttpThreadDelegate::finishedSlot() +{ + if (!httpReply) { + qWarning() << "QHttpThreadDelegate::finishedSlot: HTTP reply had already been deleted, internal problem. Please report."; + return; + } + + // If there is still some data left emit that now + while (httpReply->readAnyAvailable()) { + pendingDownloadData->fetchAndAddRelease(1); + emit downloadData(httpReply->readAny()); + } + +#ifndef QT_NO_OPENSSL + if (ssl) + emit sslConfigurationChanged(httpReply->sslConfiguration()); +#endif + + if (httpReply->statusCode() >= 400) { + // it's an error reply + QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply", + "Error downloading %1 - server replied: %2")); + msg = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase()); + emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg); + } + + emit downloadFinished(); + + QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + httpReply = 0; +} + +void QHttpThreadDelegate::synchronousFinishedSlot() +{ + if (httpReply->statusCode() >= 400) { + // it's an error reply + QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply", + "Error downloading %1 - server replied: %2")); + incomingErrorDetail = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase()); + incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()); + } + + synchronousDownloadData = httpReply->readAll(); + + QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection); + httpReply = 0; +} + +void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail) +{ + if (!httpReply) { + qWarning() << "QHttpThreadDelegate::finishedWithErrorSlot: HTTP reply had already been deleted, internal problem. Please report."; + return; + } + +#ifndef QT_NO_OPENSSL + if (ssl) + emit sslConfigurationChanged(httpReply->sslConfiguration()); +#endif + emit error(errorCode,detail); + emit downloadFinished(); + + + QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + httpReply = 0; +} + + +void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail) +{ + incomingErrorCode = errorCode; + incomingErrorDetail = detail; + + QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection); + httpReply = 0; +} + +static void downloadBufferDeleter(char *ptr) +{ + delete[] ptr; +} + +void QHttpThreadDelegate::headerChangedSlot() +{ +#ifndef QT_NO_OPENSSL + if (ssl) + emit sslConfigurationChanged(httpReply->sslConfiguration()); +#endif + + // Is using a zerocopy buffer allowed by user and possible with this reply? + if (httpReply->supportsUserProvidedDownloadBuffer() + && downloadBufferMaximumSize > 0) { + char *buf = new char[httpReply->contentLength()]; // throws if allocation fails + if (buf) { + downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter); + httpReply->setUserProvidedDownloadBuffer(buf); + } + } + + // We fetch this into our own + incomingHeaders = httpReply->header(); + incomingStatusCode = httpReply->statusCode(); + incomingReasonPhrase = httpReply->reasonPhrase(); + isPipeliningUsed = httpReply->isPipeliningUsed(); + incomingContentLength = httpReply->contentLength(); + + emit downloadMetaData(incomingHeaders, + incomingStatusCode, + incomingReasonPhrase, + isPipeliningUsed, + downloadBuffer, + incomingContentLength); +} + +void QHttpThreadDelegate::synchronousHeaderChangedSlot() +{ + // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it + incomingHeaders = httpReply->header(); + incomingStatusCode = httpReply->statusCode(); + incomingReasonPhrase = httpReply->reasonPhrase(); + isPipeliningUsed = httpReply->isPipeliningUsed(); + incomingContentLength = httpReply->contentLength(); +} + + +void QHttpThreadDelegate::dataReadProgressSlot(int done, int total) +{ + // If we don't have a download buffer don't attempt to go this codepath + // It is not used by QNetworkAccessHttpBackend + if (downloadBuffer.isNull()) + return; + + pendingDownloadProgress->fetchAndAddRelease(1); + emit downloadProgress(done, total); +} + +void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator) +{ + authenticationManager->cacheCredentials(request.url(), authenticator); +} + + +#ifndef QT_NO_OPENSSL +void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors) +{ + emit sslConfigurationChanged(httpReply->sslConfiguration()); + + bool ignoreAll = false; + QList<QSslError> specificErrors; + emit sslErrors(errors, &ignoreAll, &specificErrors); + if (ignoreAll) + httpReply->ignoreSslErrors(); + if (!specificErrors.isEmpty()) + httpReply->ignoreSslErrors(specificErrors); +} +#endif + +void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a) +{ + Q_UNUSED(request); + + // Ask the credential cache + QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a); + if (!credential.isNull()) { + a->setUser(credential.user); + a->setPassword(credential.password); + } + + // Disconnect this connection now since we only want to ask the authentication cache once. + QObject::disconnect(this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*))); +} + +#ifndef QT_NO_NETWORKPROXY +void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a) +{ + // Ask the credential cache + QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a); + if (!credential.isNull()) { + a->setUser(credential.user); + a->setPassword(credential.password); + } + + // Disconnect this connection now since we only want to ask the authentication cache once. + QObject::disconnect(this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*))); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h new file mode 100644 index 0000000..086a35d --- /dev/null +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTPTHREADDELEGATE_H +#define QHTTPTHREADDELEGATE_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QThreadStorage> +#include <QNetworkProxy> +#include <QSslConfiguration> +#include <QSslError> +#include <QList> +#include <QNetworkReply> +#include "qhttpnetworkrequest_p.h" +#include "qhttpnetworkconnection_p.h" +#include <QSharedPointer> +#include "qsslconfiguration.h" +#include "private/qnoncontiguousbytedevice_p.h" +#include "qnetworkaccessauthenticationmanager_p.h" + +QT_BEGIN_NAMESPACE + +class QAuthenticator; +class QHttpNetworkReply; +class QEventLoop; +class QNetworkAccessCache; +class QNetworkAccessCachedHttpConnection; + +class QHttpThreadDelegate : public QObject +{ + Q_OBJECT +public: + explicit QHttpThreadDelegate(QObject *parent = 0); + + ~QHttpThreadDelegate(); + + // incoming + bool ssl; +#ifndef QT_NO_OPENSSL + QSslConfiguration incomingSslConfiguration; +#endif + QHttpNetworkRequest httpRequest; + qint64 downloadBufferMaximumSize; + // From backend, modified by us for signal compression + QSharedPointer<QAtomicInt> pendingDownloadData; + QSharedPointer<QAtomicInt> pendingDownloadProgress; +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy cacheProxy; + QNetworkProxy transparentProxy; +#endif + QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager; + bool synchronous; + + // outgoing, Retrieved in the synchronous HTTP case + QByteArray synchronousDownloadData; + QList<QPair<QByteArray,QByteArray> > incomingHeaders; + int incomingStatusCode; + QString incomingReasonPhrase; + bool isPipeliningUsed; + qint64 incomingContentLength; + QNetworkReply::NetworkError incomingErrorCode; + QString incomingErrorDetail; + +protected: + // The zerocopy download buffer, if used: + QSharedPointer<char> downloadBuffer; + // The QHttpNetworkConnection that is used + QNetworkAccessCachedHttpConnection *httpConnection; + QByteArray cacheKey; + QHttpNetworkReply *httpReply; + + // Used for implementing the synchronous HTTP, see startRequestSynchronously() + QEventLoop *synchronousRequestLoop; + +signals: + void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *); +#ifndef QT_NO_NETWORKPROXY + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *); +#endif +#ifndef QT_NO_OPENSSL + void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *); + void sslConfigurationChanged(const QSslConfiguration); +#endif + void downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64); + void downloadProgress(qint64, qint64); + void downloadData(QByteArray); + void error(QNetworkReply::NetworkError, const QString); + void downloadFinished(); +public slots: + // This are called via QueuedConnection from user thread + void startRequest(); + void abortRequest(); + // This is called with a BlockingQueuedConnection from user thread + void startRequestSynchronously(); +protected slots: + // From QHttp* + void readyReadSlot(); + void finishedSlot(); + void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); + void synchronousFinishedSlot(); + void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); + void headerChangedSlot(); + void synchronousHeaderChangedSlot(); + void dataReadProgressSlot(int done, int total); + void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator); +#ifndef QT_NO_OPENSSL + void sslErrorsSlot(const QList<QSslError> &errors); +#endif + + void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *); +#ifndef QT_NO_NETWORKPROXY + void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *); +#endif + +protected: + // Cache for all the QHttpNetworkConnection objects. + // This is per thread. + static QThreadStorage<QNetworkAccessCache *> connections; + +}; + +// This QNonContiguousByteDevice is connected to the QNetworkAccessHttpBackend +// and represents the PUT/POST data. +class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevice +{ + Q_OBJECT +protected: + bool wantDataPending; + qint64 m_amount; + char *m_data; + QByteArray m_dataArray; + bool m_atEnd; + qint64 m_size; +public: + QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s) + : QNonContiguousByteDevice(), + wantDataPending(false), + m_amount(0), + m_data(0), + m_atEnd(aE), + m_size(s) + { + } + + ~QNonContiguousByteDeviceThreadForwardImpl() + { + } + + const char* readPointer(qint64 maximumLength, qint64 &len) + { + if (m_amount == 0 && wantDataPending == false) { + len = 0; + wantDataPending = true; + emit wantData(maximumLength); + } else if (m_amount == 0 && wantDataPending == true) { + // Do nothing, we already sent a wantData signal and wait for results + len = 0; + } else if (m_amount > 0) { + len = m_amount; + return m_data; + } + // cannot happen + return 0; + } + + bool advanceReadPointer(qint64 a) + { + if (m_data == 0) + return false; + + m_amount -= a; + m_data += a; + + // To main thread to inform about our state + emit processedData(a); + + // FIXME possible optimization, already ask user thread for some data + + return true; + } + + bool atEnd() + { + if (m_amount > 0) + return false; + else + return m_atEnd; + } + + bool reset() + { + m_amount = 0; + m_data = 0; + + // Communicate as BlockingQueuedConnection + bool b = false; + emit resetData(&b); + return b; + } + + qint64 size() + { + return m_size; + } + +public slots: + // From user thread: + void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize) + { + wantDataPending = false; + + m_dataArray = dataArray; + m_data = const_cast<char*>(m_dataArray.constData()); + m_amount = dataArray.size(); + + m_atEnd = dataAtEnd; + m_size = dataSize; + + // This will tell the HTTP code (QHttpNetworkConnectionChannel) that we have data available now + emit readyRead(); + } + +signals: + // void readyRead(); in parent class + // void readProgress(qint64 current, qint64 total); happens in the main thread with the real bytedevice + + // to main thread: + void wantData(qint64); + void processedData(qint64); + void resetData(bool *b); +}; + +QT_END_NAMESPACE + +#endif // QHTTPTHREADDELEGATE_H diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 53ae29e..5aedac9 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -106,12 +106,10 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() { - QNonContiguousByteDevice* device = 0; - if (reply->outgoingDataBuffer) - device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer); + uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer)); else if (reply->outgoingData) { - device = QNonContiguousByteDeviceFactory::create(reply->outgoingData); + uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingData)); } else { return 0; } @@ -120,14 +118,13 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(false)) == QVariant(true); if (bufferDisallowed) - device->disableReset(); - - // make sure we delete this later - device->setParent(this); + uploadByteDevice->disableReset(); - connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64))); + // We want signal emissions only for normal asynchronous uploads + if (!isSynchronous()) + connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64))); - return device; + return uploadByteDevice.data(); } // need to have this function since the reply is a private member variable @@ -327,11 +324,6 @@ void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator manager->authenticationRequired(this, authenticator); } -void QNetworkAccessBackend::cacheCredentials(QAuthenticator *authenticator) -{ - manager->authenticationManager->cacheCredentials(this->reply->url, authenticator); -} - void QNetworkAccessBackend::metaDataChanged() { reply->metaDataChanged(); diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 26ee61a..644ae2d 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -70,7 +70,6 @@ class QNetworkAccessManagerPrivate; class QNetworkReplyImplPrivate; class QAbstractNetworkCache; class QNetworkCacheMetaData; -class QNetworkAccessBackendUploadIODevice; class QNonContiguousByteDevice; // Should support direct file upload from disk or download to disk. @@ -175,7 +174,6 @@ protected: // Create the device used for reading the upload data QNonContiguousByteDevice* createUploadByteDevice(); - // these functions control the downstream mechanism // that is, data that has come via the connection and is going out the backend qint64 nextDownstreamBlockSize() const; @@ -185,6 +183,8 @@ protected: void writeDownstreamDataDownloadBuffer(qint64, qint64); char* getDownloadBuffer(qint64); + QSharedPointer<QNonContiguousByteDevice> uploadByteDevice; + public slots: // for task 251801, needs to be a slot to be called asynchronously void writeDownstreamData(QIODevice *data); @@ -196,19 +196,22 @@ protected slots: void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); #endif void authenticationRequired(QAuthenticator *auth); - void cacheCredentials(QAuthenticator *auth); void metaDataChanged(); void redirectionRequested(const QUrl &destination); void sslErrors(const QList<QSslError> &errors); void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); +protected: + // FIXME In the long run we should get rid of our QNAM architecture + // and scrap this ReplyImpl/Backend distinction. + QNetworkAccessManagerPrivate *manager; + QNetworkReplyImplPrivate *reply; + private: friend class QNetworkAccessManager; friend class QNetworkAccessManagerPrivate; - friend class QNetworkAccessBackendUploadIODevice; friend class QNetworkReplyImplPrivate; - QNetworkAccessManagerPrivate *manager; - QNetworkReplyImplPrivate *reply; + bool synchronous; }; diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp index 6da0722..7ebf626 100644 --- a/src/network/access/qnetworkaccessfilebackend.cpp +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -75,6 +75,8 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, // // this construct here must match the one below in open() QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery)); + if ((url.scheme().length()==1) && fi.exists()) + qWarning("QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files"); if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists())) return new QNetworkAccessFileBackend; } diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 79c43fa..4908e0a 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -52,60 +52,19 @@ #include "QtCore/qdatetime.h" #include "QtCore/qelapsedtimer.h" #include "QtNetwork/qsslconfiguration.h" +#include "qhttpthreaddelegate_p.h" +#include "qthread.h" #ifndef QT_NO_HTTP #include <string.h> // for strchr -QT_BEGIN_NAMESPACE +Q_DECLARE_METATYPE(QSharedPointer<char>) -enum { - DefaultHttpPort = 80, - DefaultHttpsPort = 443 -}; +QT_BEGIN_NAMESPACE class QNetworkProxy; -static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy *proxy) -{ - QByteArray result; - QUrl copy = backend->url(); - bool isEncrypted = copy.scheme().toLower() == QLatin1String("https"); - copy.setPort(copy.port(isEncrypted ? DefaultHttpsPort : DefaultHttpPort)); - result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath | - QUrl::RemoveQuery | QUrl::RemoveFragment); - -#ifndef QT_NO_NETWORKPROXY - if (proxy->type() != QNetworkProxy::NoProxy) { - QUrl key; - - switch (proxy->type()) { - case QNetworkProxy::Socks5Proxy: - key.setScheme(QLatin1String("proxy-socks5")); - break; - - case QNetworkProxy::HttpProxy: - case QNetworkProxy::HttpCachingProxy: - key.setScheme(QLatin1String("proxy-http")); - break; - - default: - break; - } - - if (!key.scheme().isEmpty()) { - key.setUserName(proxy->user()); - key.setHost(proxy->hostName()); - key.setPort(proxy->port()); - key.setEncodedQuery(result); - result = key.toEncoded(); - } - } -#endif - - return "http-connection:" + result; -} - static inline bool isSeparator(register char c) { static const char separators[] = "()<>@,;:\\\"/[]?={}"; @@ -230,71 +189,13 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op, return 0; } -static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url) -{ - QNetworkReply::NetworkError code; - // we've got an error - switch (httpStatusCode) { - case 401: // Authorization required - code = QNetworkReply::AuthenticationRequiredError; - break; - - case 403: // Access denied - code = QNetworkReply::ContentOperationNotPermittedError; - break; - - case 404: // Not Found - code = QNetworkReply::ContentNotFoundError; - break; - - case 405: // Method Not Allowed - code = QNetworkReply::ContentOperationNotPermittedError; - break; - - case 407: - code = QNetworkReply::ProxyAuthenticationRequiredError; - break; - - default: - if (httpStatusCode > 500) { - // some kind of server error - code = QNetworkReply::ProtocolUnknownError; - } else if (httpStatusCode >= 400) { - // content error we did not handle above - code = QNetworkReply::UnknownContentError; - } else { - qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"", - httpStatusCode, qPrintable(url.toString())); - code = QNetworkReply::ProtocolFailure; - } - } - - return code; -} - -class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection, - public QNetworkAccessCache::CacheableObject -{ - // Q_OBJECT -public: - QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt) - : QHttpNetworkConnection(hostName, port, encrypt) - { - setExpires(true); - setShareable(true); - } - - virtual void dispose() - { -#if 0 // sample code; do this right with the API - Q_ASSERT(!isWorking()); -#endif - delete this; - } -}; - QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() - : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0) + : QNetworkAccessBackend() + , statusCode(0) + , pendingDownloadDataEmissions(new QAtomicInt()) + , pendingDownloadProgressEmissions(new QAtomicInt()) + , loadingFromCache(false) + , usingZerocopyDownloadBuffer(false) #ifndef QT_NO_OPENSSL , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false) #endif @@ -304,44 +205,14 @@ QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend() { - if (http) - disconnectFromHttp(); + // This will do nothing if the request was already finished or aborted + emit abortHttpRequest(); + #ifndef QT_NO_OPENSSL delete pendingSslConfiguration; #endif } -void QNetworkAccessHttpBackend::disconnectFromHttp() -{ - if (http) { - // This is abut disconnecting signals, not about disconnecting TCP connections - disconnect(http, 0, this, 0); - - // Get the object cache that stores our QHttpNetworkConnection objects - QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this); - - // synchronous calls are not put into the cache, so for them the key is empty - if (!cacheKey.isEmpty()) - cache->releaseEntry(cacheKey); - } - - // This is abut disconnecting signals, not about disconnecting TCP connections - if (httpReply) - disconnect(httpReply, 0, this, 0); - - http = 0; - httpReply = 0; - cacheKey.clear(); -} - -void QNetworkAccessHttpBackend::finished() -{ - if (http) - disconnectFromHttp(); - // call parent - QNetworkAccessBackend::finished(); -} - /* For a given httpRequest 1) If AlwaysNetwork, return @@ -487,9 +358,82 @@ static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& pr void QNetworkAccessHttpBackend::postRequest() { + QThread *thread = 0; + if (isSynchronous()) { + // A synchronous HTTP request uses its own thread + thread = new QThread(); + QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + } else if (!manager->httpThread) { + // We use the manager-global thread. + // At some point we could switch to having multiple threads if it makes sense. + manager->httpThread = new QThread(); + QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater())); + manager->httpThread->start(); +#ifndef QT_NO_NETWORKPROXY + qRegisterMetaType<QNetworkProxy>("QNetworkProxy"); +#endif +#ifndef QT_NO_OPENSSL + qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); + qRegisterMetaType<QSslConfiguration>("QSslConfiguration"); +#endif + qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >"); + qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest"); + qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); + qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>"); + + thread = manager->httpThread; + } else { + // Asynchronous request, thread already exists + thread = manager->httpThread; + } + + QUrl url = request().url(); + httpRequest.setUrl(url); + + bool ssl = url.scheme().toLower() == QLatin1String("https"); + setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl); + httpRequest.setSsl(ssl); + + +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy transparentProxy, cacheProxy; + + foreach (const QNetworkProxy &p, proxyList()) { + // use the first proxy that works + // for non-encrypted connections, any transparent or HTTP proxy + // for encrypted, only transparent proxies + if (!ssl + && (p.capabilities() & QNetworkProxy::CachingCapability) + && (p.type() == QNetworkProxy::HttpProxy || + p.type() == QNetworkProxy::HttpCachingProxy)) { + cacheProxy = p; + transparentProxy = QNetworkProxy::NoProxy; + break; + } + if (p.isTransparentProxy()) { + transparentProxy = p; + cacheProxy = QNetworkProxy::NoProxy; + break; + } + } + + // check if at least one of the proxies + if (transparentProxy.type() == QNetworkProxy::DefaultProxy && + cacheProxy.type() == QNetworkProxy::DefaultProxy) { + // unsuitable proxies + QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, + Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), + Q_ARG(QString, tr("No suitable proxy found"))); + QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection); + return; + } +#endif + + bool loadedFromCache = false; - QHttpNetworkRequest httpRequest; httpRequest.setPriority(convert(request().priority())); + switch (operation()) { case QNetworkAccessManager::GetOperation: httpRequest.setOperation(QHttpNetworkRequest::Get); @@ -504,13 +448,13 @@ void QNetworkAccessHttpBackend::postRequest() case QNetworkAccessManager::PostOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Post); - httpRequest.setUploadByteDevice(createUploadByteDevice()); + createUploadByteDevice(); break; case QNetworkAccessManager::PutOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Put); - httpRequest.setUploadByteDevice(createUploadByteDevice()); + createUploadByteDevice(); break; case QNetworkAccessManager::DeleteOperation: @@ -521,7 +465,7 @@ void QNetworkAccessHttpBackend::postRequest() case QNetworkAccessManager::CustomOperation: invalidateCache(); // for safety reasons, we don't know what the operation does httpRequest.setOperation(QHttpNetworkRequest::Custom); - httpRequest.setUploadByteDevice(createUploadByteDevice()); + createUploadByteDevice(); httpRequest.setCustomVerb(request().attribute( QNetworkRequest::CustomVerbAttribute).toByteArray()); break; @@ -530,11 +474,6 @@ void QNetworkAccessHttpBackend::postRequest() break; // can't happen } - bool encrypt = (url().scheme().toLower() == QLatin1String("https")); - httpRequest.setSsl(encrypt); - - httpRequest.setUrl(url()); - QList<QByteArray> headers = request().rawHeaderList(); if (resumeOffset != 0) { if (headers.contains("Range")) { @@ -558,6 +497,7 @@ void QNetworkAccessHttpBackend::postRequest() httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-'); } } + foreach (const QByteArray &header, headers) httpRequest.setHeaderField(header, request().rawHeader(header)); @@ -576,30 +516,155 @@ void QNetworkAccessHttpBackend::postRequest() QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual) httpRequest.setWithCredentials(false); - httpReply = http->sendRequest(httpRequest); - httpReply->setParent(this); + + // Create the HTTP thread delegate + QHttpThreadDelegate *delegate = new QHttpThreadDelegate; + + // For the synchronous HTTP, this is the normal way the delegate gets deleted + // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished + connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater())); + + // Set the properties it needs + delegate->httpRequest = httpRequest; +#ifndef QT_NO_NETWORKPROXY + delegate->cacheProxy = cacheProxy; + delegate->transparentProxy = transparentProxy; +#endif + delegate->ssl = ssl; #ifndef QT_NO_OPENSSL - if (pendingSslConfiguration) - httpReply->setSslConfiguration(*pendingSslConfiguration); - if (pendingIgnoreAllSslErrors) - httpReply->ignoreSslErrors(); - httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList); - connect(httpReply, SIGNAL(sslErrors(QList<QSslError>)), - SLOT(sslErrors(QList<QSslError>))); + if (ssl) + delegate->incomingSslConfiguration = request().sslConfiguration(); #endif - connect(httpReply, SIGNAL(finished()), SLOT(replyFinished())); - connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)), - SLOT(httpError(QNetworkReply::NetworkError,QString))); - connect(httpReply, SIGNAL(headerChanged()), SLOT(replyHeaderChanged())); - connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)), - SLOT(httpCacheCredentials(QHttpNetworkRequest,QAuthenticator*))); + // Do we use synchronous HTTP? + delegate->synchronous = isSynchronous(); + + // The authentication manager is used to avoid the BlockingQueuedConnection communication + // from HTTP thread to user thread in some cases. + delegate->authenticationManager = manager->authenticationManager; + + if (!isSynchronous()) { + // Tell our zerocopy policy to the delegate + delegate->downloadBufferMaximumSize = + request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong(); + + // These atomic integers are used for signal compression + delegate->pendingDownloadData = pendingDownloadDataEmissions; + delegate->pendingDownloadProgress = pendingDownloadProgressEmissions; + + // Connect the signals of the delegate to us + connect(delegate, SIGNAL(downloadData(QByteArray)), + this, SLOT(replyDownloadData(QByteArray)), + Qt::QueuedConnection); + connect(delegate, SIGNAL(downloadFinished()), + this, SLOT(replyFinished()), + Qt::QueuedConnection); + connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), + this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), + Qt::QueuedConnection); + connect(delegate, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(replyDownloadProgressSlot(qint64,qint64)), + Qt::QueuedConnection); + connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)), + this, SLOT(httpError(QNetworkReply::NetworkError, const QString)), + Qt::QueuedConnection); +#ifndef QT_NO_OPENSSL + connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)), + this, SLOT(replySslConfigurationChanged(QSslConfiguration)), + Qt::QueuedConnection); +#endif + // Those need to report back, therefire BlockingQueuedConnection + connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), + this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)), + Qt::BlockingQueuedConnection); #ifndef QT_NO_NETWORKPROXY - connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::BlockingQueuedConnection); #endif - connect(httpReply, SIGNAL(authenticationRequired(const QHttpNetworkRequest,QAuthenticator*)), - SLOT(httpAuthenticationRequired(const QHttpNetworkRequest,QAuthenticator*))); +#ifndef QT_NO_OPENSSL + connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)), + this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)), + Qt::BlockingQueuedConnection); +#endif + // This signal we will use to start the request. + connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest())); + connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest())); + + if (uploadByteDevice) { + QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice = + new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size()); + if (uploadByteDevice->isResetDisabled()) + forwardUploadDevice->disableReset(); + forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread() + delegate->httpRequest.setUploadByteDevice(forwardUploadDevice); + + // From main thread to user thread: + QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)), + forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection); + QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), + forwardUploadDevice, SIGNAL(readyRead()), + Qt::QueuedConnection); + + // From http thread to user thread: + QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), + this, SLOT(wantUploadDataSlot(qint64))); + QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), + this, SLOT(sentUploadDataSlot(qint64))); + connect(forwardUploadDevice, SIGNAL(resetData(bool*)), + this, SLOT(resetUploadDataSlot(bool*)), + Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! + } + } else if (isSynchronous()) { + connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection); + + if (uploadByteDevice) { + // For the synchronous HTTP use case the use thread (this one here) is blocked + // so we cannot use the asynchronous upload architecture. + // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly + // use the uploadByteDevice provided to us by the QNetworkReplyImpl. + // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread + // since it only wraps a QRingBuffer + delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data()); + } + } + + + // Move the delegate to the http thread + delegate->moveToThread(thread); + // This call automatically moves the uploadDevice too for the asynchronous case. + + // Send an signal to the delegate so it starts working in the other thread + if (isSynchronous()) { + emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done + + if (delegate->incomingErrorCode != QNetworkReply::NoError) { + replyDownloadMetaData + (delegate->incomingHeaders, + delegate->incomingStatusCode, + delegate->incomingReasonPhrase, + delegate->isPipeliningUsed, + QSharedPointer<char>(), + delegate->incomingContentLength); + httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail); + } else { + replyDownloadMetaData + (delegate->incomingHeaders, + delegate->incomingStatusCode, + delegate->incomingReasonPhrase, + delegate->isPipeliningUsed, + QSharedPointer<char>(), + delegate->incomingContentLength); + replyDownloadData(delegate->synchronousDownloadData); + } + + // End the thread. It will delete itself from the finished() signal + thread->quit(); + + finished(); + } else { + emit startHttpRequest(); // Signal to the HTTP thread and go back to user. + } } void QNetworkAccessHttpBackend::invalidateCache() @@ -611,159 +676,52 @@ void QNetworkAccessHttpBackend::invalidateCache() void QNetworkAccessHttpBackend::open() { - QUrl url = request().url(); - bool encrypt = url.scheme().toLower() == QLatin1String("https"); - setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, encrypt); - - // set the port number in the reply if it wasn't set - url.setPort(url.port(encrypt ? DefaultHttpsPort : DefaultHttpPort)); - - QNetworkProxy *theProxy = 0; -#ifndef QT_NO_NETWORKPROXY - QNetworkProxy transparentProxy, cacheProxy; - - foreach (const QNetworkProxy &p, proxyList()) { - // use the first proxy that works - // for non-encrypted connections, any transparent or HTTP proxy - // for encrypted, only transparent proxies - if (!encrypt - && (p.capabilities() & QNetworkProxy::CachingCapability) - && (p.type() == QNetworkProxy::HttpProxy || - p.type() == QNetworkProxy::HttpCachingProxy)) { - cacheProxy = p; - transparentProxy = QNetworkProxy::NoProxy; - theProxy = &cacheProxy; - break; - } - if (p.isTransparentProxy()) { - transparentProxy = p; - cacheProxy = QNetworkProxy::NoProxy; - theProxy = &transparentProxy; - break; - } - } - - // check if at least one of the proxies - if (transparentProxy.type() == QNetworkProxy::DefaultProxy && - cacheProxy.type() == QNetworkProxy::DefaultProxy) { - // unsuitable proxies - if (isSynchronous()) { - error(QNetworkReply::ProxyNotFoundError, tr("No suitable proxy found")); - finished(); - } else { - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), - Q_ARG(QString, tr("No suitable proxy found"))); - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); - } - return; - } -#endif - - if (isSynchronous()) { - // for synchronous requests, we just create a new connection - http = new QHttpNetworkConnection(1, url.host(), url.port(), encrypt, this); -#ifndef QT_NO_NETWORKPROXY - http->setTransparentProxy(transparentProxy); - http->setCacheProxy(cacheProxy); -#endif - postRequest(); - processRequestSynchronously(); - } else { - // check if we have an open connection to this host - cacheKey = makeCacheKey(this, theProxy); - QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this); - // the http object is actually a QHttpNetworkConnection - http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey)); - if (http == 0) { - // no entry in cache; create an object - // the http object is actually a QHttpNetworkConnection - http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt); - -#ifndef QT_NO_NETWORKPROXY - http->setTransparentProxy(transparentProxy); - http->setCacheProxy(cacheProxy); -#endif - - // cache the QHttpNetworkConnection corresponding to this cache key - cache->addEntry(cacheKey, static_cast<QNetworkAccessCachedHttpConnection *>(http.data())); - } - postRequest(); - } + postRequest(); } void QNetworkAccessHttpBackend::closeDownstreamChannel() { - // this indicates that the user closed the stream while the reply isn't finished yet + // FIXME Maybe we can get rid of this whole architecture part } void QNetworkAccessHttpBackend::downstreamReadyWrite() { - readFromHttp(); - if (httpReply && httpReply->bytesAvailable() == 0 && httpReply->isFinished()) - replyFinished(); + // FIXME Maybe we can get rid of this whole architecture part } void QNetworkAccessHttpBackend::setDownstreamLimited(bool b) { - if (httpReply) - httpReply->setDownstreamLimited(b); -} - -void QNetworkAccessHttpBackend::replyReadyRead() -{ - readFromHttp(); + Q_UNUSED(b); + // We know that readBuffer maximum size limiting is broken since quite a while. + // The task to fix this is QTBUG-15065 } -void QNetworkAccessHttpBackend::readFromHttp() +void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d) { - if (!httpReply) + int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1; + + if (pendingSignals > 0) { + // Some more signal emissions to this slot are pending. + // Instead of writing the downstream data, we wait + // and do it in the next call we get + // (signal comppression) + pendingDownloadData.append(d); return; - - // We read possibly more than nextDownstreamBlockSize(), but - // this is not a critical thing since it is already in the - // memory anyway - - QByteDataBuffer list; - - while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) { - list.append(httpReply->readAny()); } - if (!list.isEmpty()) - writeDownstreamData(list); + pendingDownloadData.append(d); + d.clear(); + writeDownstreamData(pendingDownloadData); + pendingDownloadData.clear(); } void QNetworkAccessHttpBackend::replyFinished() { - if (httpReply->bytesAvailable()) - // we haven't read everything yet. Wait some more. + // We are already loading from cache, we still however + // got this signal because it was posted already + if (loadingFromCache) return; - int statusCode = httpReply->statusCode(); - if (statusCode >= 400) { - // it's an error reply - QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply", - "Error downloading %1 - server replied: %2")); - msg = msg.arg(url().toString(), httpReply->reasonPhrase()); - error(statusCodeFromHttp(httpReply->statusCode(), httpReply->url()), msg); - } - -#ifndef QT_NO_OPENSSL - // store the SSL configuration now - // once we call finished(), we won't have access to httpReply anymore - QSslConfiguration sslConfig = httpReply->sslConfiguration(); - if (pendingSslConfiguration) { - *pendingSslConfiguration = sslConfig; - } else if (!sslConfig.isNull()) { - QT_TRY { - pendingSslConfiguration = new QSslConfiguration(sslConfig); - } QT_CATCH(...) { - qWarning("QNetworkAccess: could not allocate a QSslConfiguration object for a SSL connection."); - } - } -#endif - finished(); } @@ -785,12 +743,27 @@ void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode) } } -void QNetworkAccessHttpBackend::replyHeaderChanged() +void QNetworkAccessHttpBackend::replyDownloadMetaData + (QList<QPair<QByteArray,QByteArray> > hm, + int sc,QString rp,bool pu, + QSharedPointer<char> db, + qint64 contentLength) { - setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, httpReply->isPipeliningUsed()); + statusCode = sc; + reasonPhrase = rp; + + // Download buffer + if (!db.isNull()) { + reply->setDownloadBuffer(db, contentLength); + usingZerocopyDownloadBuffer = true; + } else { + usingZerocopyDownloadBuffer = false; + } + + setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu); // reconstruct the HTTP header - QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header(); + QList<QPair<QByteArray, QByteArray> > headerMap = hm; QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(), end = headerMap.constEnd(); QByteArray header; @@ -807,11 +780,10 @@ void QNetworkAccessHttpBackend::replyHeaderChanged() setRawHeader(it->first, value); } - setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpReply->statusCode()); - setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase()); + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode); + setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase); // is it a redirection? - const int statusCode = httpReply->statusCode(); checkForRedirect(statusCode); if (statusCode >= 500 && statusCode < 600) { @@ -854,29 +826,21 @@ void QNetworkAccessHttpBackend::replyHeaderChanged() setCachingEnabled(true); } - // Check if a download buffer is supported from the HTTP reply - char *buf = 0; - if (httpReply->supportsUserProvidedDownloadBuffer()) { - // Check if a download buffer is supported by the user - buf = getDownloadBuffer(httpReply->contentLength()); - if (buf) { - httpReply->setUserProvidedDownloadBuffer(buf); - // If there is a download buffer we react on the progress signal - connect(httpReply, SIGNAL(dataReadProgress(int,int)), SLOT(replyDownloadProgressSlot(int,int))); - } - } - - // If there is no buffer, we react on the readyRead signal - if (!buf) { - connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead())); - } - metaDataChanged(); } -void QNetworkAccessHttpBackend::replyDownloadProgressSlot(int received, int total) +void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received, qint64 total) { // we can be sure here that there is a download buffer + + int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1; + if (pendingSignals > 0) { + // Let's ignore this signal and look at the next one coming in + // (signal comppression) + return; + } + + // Now do the actual notification of new bytes writeDownstreamDataDownloadBuffer(received, total); } @@ -886,20 +850,62 @@ void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkReq authenticationRequired(auth); } -void QNetworkAccessHttpBackend::httpCacheCredentials(const QHttpNetworkRequest &, - QAuthenticator *auth) -{ - cacheCredentials(auth); -} - void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode, const QString &errorString) { #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) qDebug() << "http error!" << errorCode << errorString; #endif + error(errorCode, errorString); - finished(); +} + +#ifndef QT_NO_OPENSSL +void QNetworkAccessHttpBackend::replySslErrors( + const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored) +{ + // Go to generic backend + sslErrors(list); + // Check if the callback set any ignore and return this here to http thread + if (pendingIgnoreAllSslErrors) + *ignoreAll = true; + if (!pendingIgnoreSslErrorsList.isEmpty()) + *toBeIgnored = pendingIgnoreSslErrorsList; +} + +void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfiguration &c) +{ + // Receiving the used SSL configuration from the HTTP thread + if (pendingSslConfiguration) + *pendingSslConfiguration = c; + else if (!c.isNull()) + pendingSslConfiguration = new QSslConfiguration(c); +} +#endif + +// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r) +{ + *r = uploadByteDevice->reset(); +} + +// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount) +{ + uploadByteDevice->advanceReadPointer(amount); +} + +// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize) +{ + // call readPointer + qint64 currentUploadDataLength = 0; + char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength)); + // Let's make a copy of this data + QByteArray dataArray(data, currentUploadDataLength); + + // Communicate back to HTTP thread + emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); } /* @@ -950,8 +956,10 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes"; #endif - if (httpReply) - disconnect(httpReply, SIGNAL(finished()), this, SLOT(replyFinished())); + + // Set the following flag so we can ignore some signals from HTTP thread + // that would still come + loadingFromCache = true; return true; } @@ -964,39 +972,29 @@ void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev) #ifndef QT_NO_OPENSSL void QNetworkAccessHttpBackend::ignoreSslErrors() { - if (httpReply) - httpReply->ignoreSslErrors(); - else - pendingIgnoreAllSslErrors = true; + pendingIgnoreAllSslErrors = true; } void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors) { - if (httpReply) { - httpReply->ignoreSslErrors(errors); - } else { - // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) - // is called before QNetworkAccessManager::get() (or post(), etc.) - pendingIgnoreSslErrorsList = errors; - } + // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) + // is called before QNetworkAccessManager::get() (or post(), etc.) + pendingIgnoreSslErrorsList = errors; } void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const { - if (httpReply) - config = httpReply->sslConfiguration(); - else if (pendingSslConfiguration) + if (pendingSslConfiguration) config = *pendingSslConfiguration; + else + config = request().sslConfiguration(); } void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig) { - if (httpReply) - httpReply->setSslConfiguration(newconfig); - else if (pendingSslConfiguration) - *pendingSslConfiguration = newconfig; - else - pendingSslConfiguration = new QSslConfiguration(newconfig); + // Setting a SSL configuration on a reply is not supported. The user needs to set + // her/his QSslConfiguration on the QNetworkRequest. + Q_UNUSED(newconfig); } #endif @@ -1101,7 +1099,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo bool canDiskCache; // only cache GET replies by default, all other replies (POST, PUT, DELETE) // are not cacheable by default (according to RFC 2616 section 9) - if (httpReply->request().operation() == QHttpNetworkRequest::Get) { + if (httpRequest.operation() == QHttpNetworkRequest::Get) { canDiskCache = true; // 14.32 @@ -1119,7 +1117,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo canDiskCache = false; // responses to POST might be cacheable - } else if (httpReply->request().operation() == QHttpNetworkRequest::Post) { + } else if (httpRequest.operation() == QHttpNetworkRequest::Post) { canDiskCache = false; // some pages contain "expires:" and "cache-control: no-cache" field, @@ -1133,12 +1131,11 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo } metaData.setSaveToDisk(canDiskCache); - int statusCode = httpReply->statusCode(); QNetworkCacheMetaData::AttributesMap attributes; if (statusCode != 304) { // update the status code attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode); - attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase()); + attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase); } else { // this is a redirection, keep the attributes intact attributes = oldMetaData.attributes(); @@ -1154,7 +1151,8 @@ bool QNetworkAccessHttpBackend::canResume() const return false; // Can only resume if server/resource supports Range header. - if (httpReply->headerField("Accept-Ranges", "none") == "none") + QByteArray acceptRangesheaderName("Accept-Ranges"); + if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none") return false; // We only support resuming for byte ranges. @@ -1166,7 +1164,7 @@ bool QNetworkAccessHttpBackend::canResume() const // If we're using a download buffer then we don't support resuming/migration // right now. Too much trouble. - if (httpReply->userProvidedDownloadBuffer()) + if (usingZerocopyDownloadBuffer) return false; return true; @@ -1177,87 +1175,6 @@ void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset) resumeOffset = offset; } -bool QNetworkAccessHttpBackend::processRequestSynchronously() -{ - QHttpNetworkConnectionChannel *channel = &http->channels()[0]; - - // Disconnect all socket signals. They will only confuse us when using waitFor* - QObject::disconnect(channel->socket, 0, 0, 0); - - qint64 timeout = 20*1000; // 20 sec - QElapsedTimer timeoutTimer; - - bool waitResult = channel->socket->waitForConnected(timeout); - timeoutTimer.start(); - - if (!waitResult || channel->socket->state() != QAbstractSocket::ConnectedState) { - error(QNetworkReply::UnknownNetworkError, QLatin1String("could not connect")); - return false; - } - channel->_q_connected(); // this will send the request (via sendRequest()) - -#ifndef QT_NO_OPENSSL - if (http->isSsl()) { - qint64 remainingTimeEncrypted = timeout - timeoutTimer.elapsed(); - if (!static_cast<QSslSocket *>(channel->socket)->waitForEncrypted(remainingTimeEncrypted)) { - error(QNetworkReply::SslHandshakeFailedError, - QLatin1String("could not encrypt or timeout while encrypting")); - return false; - } - channel->_q_encrypted(); - } -#endif - - // if we get a 401 or 407, we might need to send the request twice, see below - bool authenticating = false; - - do { - channel->sendRequest(); - - qint64 remainingTimeBytesWritten; - while(channel->socket->bytesToWrite() > 0 || - channel->state == QHttpNetworkConnectionChannel::WritingState) { - remainingTimeBytesWritten = timeout - timeoutTimer.elapsed(); - channel->sendRequest(); // triggers channel->socket->write() - if (!channel->socket->waitForBytesWritten(remainingTimeBytesWritten)) { - error(QNetworkReply::TimeoutError, - QLatin1String("could not write bytes to socket or timeout while writing")); - return false; - } - } - - qint64 remainingTimeBytesRead = timeout - timeoutTimer.elapsed(); - // Loop for at most remainingTime until either the socket disconnects - // or the reply is finished - do { - waitResult = channel->socket->waitForReadyRead(remainingTimeBytesRead); - remainingTimeBytesRead = timeout - timeoutTimer.elapsed(); - if (!waitResult || remainingTimeBytesRead <= 0 - || channel->socket->state() != QAbstractSocket::ConnectedState) { - error(QNetworkReply::TimeoutError, - QLatin1String("could not read from socket or timeout while reading")); - return false; - } - - if (channel->socket->bytesAvailable()) - channel->_q_readyRead(); - - if (!httpReply) - return false; // we got a 401 or 407 and cannot handle it (it might happen that - // disconnectFromHttp() was called, in that case the reply is zero) - // ### I am quite sure this does not work for NTLM - // ### how about uploading to an auth / proxyAuth site? - - authenticating = (httpReply->statusCode() == 401 || httpReply->statusCode() == 407); - - if (httpReply->isFinished()) - break; - } while (remainingTimeBytesRead > 0); - } while (authenticating); - - return true; -} - QT_END_NAMESPACE #endif // QT_NO_HTTP diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 7064d4a..712dd2f 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -61,6 +61,8 @@ #include "QtCore/qpointer.h" #include "QtCore/qdatetime.h" +#include "QtCore/qsharedpointer.h" +#include "qatomic.h" #ifndef QT_NO_HTTP @@ -99,24 +101,44 @@ public: bool canResume() const; void setResumeOffset(quint64 offset); - virtual bool processRequestSynchronously(); +signals: + // To HTTP thread: + void startHttpRequest(); + void abortHttpRequest(); + void startHttpRequestSynchronously(); + + void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); private slots: - void replyReadyRead(); + // From HTTP thread: + void replyDownloadData(QByteArray); void replyFinished(); - void replyHeaderChanged(); - void replyDownloadProgressSlot(int,int); + void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64); + void replyDownloadProgressSlot(qint64,qint64); void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); - void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth); void httpError(QNetworkReply::NetworkError error, const QString &errorString); +#ifndef QT_NO_OPENSSL + void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *); + void replySslConfigurationChanged(const QSslConfiguration&); +#endif + + // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: + void resetUploadDataSlot(bool *r); + void wantUploadDataSlot(qint64); + void sentUploadDataSlot(qint64); + bool sendCacheContents(const QNetworkCacheMetaData &metaData); - void finished(); // override private: - QHttpNetworkReply *httpReply; - QPointer<QHttpNetworkConnection> http; - QByteArray cacheKey; - QNetworkAccessBackendUploadIODevice *uploadDevice; + QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread + int statusCode; + QString reasonPhrase; + // Will be increased by HTTP thread: + QSharedPointer<QAtomicInt> pendingDownloadDataEmissions; + QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions; + bool loadingFromCache; + QByteDataBuffer pendingDownloadData; + bool usingZerocopyDownloadBuffer; #ifndef QT_NO_OPENSSL QSslConfiguration *pendingSslConfiguration; @@ -126,7 +148,6 @@ private: quint64 resumeOffset; - void disconnectFromHttp(); void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache); void invalidateCache(); void postRequest(); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 22f62cb..7e75045 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -65,6 +65,8 @@ #include "QtNetwork/qsslconfiguration.h" #include "QtNetwork/qnetworkconfigmanager.h" +#include "qthread.h" + QT_BEGIN_NAMESPACE #ifndef QT_NO_HTTP @@ -1099,10 +1101,21 @@ void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager) { manager->d_func()->objectCache.clear(); manager->d_func()->authenticationManager->clearCache(); + + if (manager->d_func()->httpThread) { + // The thread will deleteLater() itself from its finished() signal + manager->d_func()->httpThread->quit(); + manager->d_func()->httpThread = 0; + } } QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate() { + if (httpThread) { + // The thread will deleteLater() itself from its finished() signal + httpThread->quit(); + httpThread = 0; + } } #ifndef QT_NO_BEARERMANAGEMENT diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 7ef009f..d67b8ac 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -156,6 +156,8 @@ protected: private: friend class QNetworkReplyImplPrivate; + friend class QNetworkAccessHttpBackend; + Q_DECLARE_PRIVATE(QNetworkAccessManager) Q_PRIVATE_SLOT(d_func(), void _q_replyFinished()) Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>)) diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index dba1fd2..ee6ad70 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -73,6 +73,7 @@ class QNetworkAccessManagerPrivate: public QObjectPrivate public: QNetworkAccessManagerPrivate() : networkCache(0), cookieJar(0), + httpThread(0), #ifndef QT_NO_NETWORKPROXY proxyFactory(0), #endif @@ -123,6 +124,8 @@ public: QNetworkCookieJar *cookieJar; + QThread *httpThread; + #ifndef QT_NO_NETWORKPROXY QNetworkProxy proxy; @@ -140,7 +143,7 @@ public: bool cookieJarCreated; // The cache with authorization data: - QNetworkAccessAuthenticationManager* authenticationManager; + QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager; // this cache can be used by individual backends to cache e.g. their TCP connections to a server // and use the connections for multiple requests. diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp index 686eb5f..babee32 100644 --- a/src/network/access/qnetworkreplyfileimpl.cpp +++ b/src/network/access/qnetworkreplyfileimpl.cpp @@ -49,15 +49,10 @@ QT_BEGIN_NAMESPACE QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate() - : QNetworkReplyPrivate(), fileEngine(0), fileSize(0), filePos(0) + : QNetworkReplyPrivate(), realFileSize(0) { } -QNetworkReplyFileImplPrivate::~QNetworkReplyFileImplPrivate() -{ - delete fileEngine; -} - QNetworkReplyFileImpl::~QNetworkReplyFileImpl() { } @@ -112,15 +107,15 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ return; } - d->fileEngine = QAbstractFileEngine::create(fileName); - bool opened = d->fileEngine->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + d->realFile.setFileName(fileName); + bool opened = d->realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered); // could we open the file? if (!opened) { QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2") - .arg(fileName, d->fileEngine->errorString()); + .arg(d->realFile.fileName(), d->realFile.errorString()); - if (fi.exists()) { + if (d->realFile.exists()) { setError(QNetworkReply::ContentAccessDenied, msg); QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied)); @@ -133,13 +128,13 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ return; } - d->fileSize = fi.size(); setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified()); - setHeader(QNetworkRequest::ContentLengthHeader, d->fileSize); + d->realFileSize = fi.size(); + setHeader(QNetworkRequest::ContentLengthHeader, d->realFileSize); QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection, - Q_ARG(qint64, d->fileSize), Q_ARG(qint64, d->fileSize)); + Q_ARG(qint64, d->realFileSize), Q_ARG(qint64, d->realFileSize)); QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); } @@ -147,25 +142,20 @@ void QNetworkReplyFileImpl::close() { Q_D(QNetworkReplyFileImpl); QNetworkReply::close(); - if (d->fileEngine) - d->fileEngine->close(); + d->realFile.close(); } void QNetworkReplyFileImpl::abort() { Q_D(QNetworkReplyFileImpl); QNetworkReply::close(); - if (d->fileEngine) - d->fileEngine->close(); + d->realFile.close(); } qint64 QNetworkReplyFileImpl::bytesAvailable() const { Q_D(const QNetworkReplyFileImpl); - if (!d->fileEngine) - return 0; - - return QNetworkReply::bytesAvailable() + d->fileSize - d->filePos; + return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable(); } bool QNetworkReplyFileImpl::isSequential () const @@ -176,7 +166,7 @@ bool QNetworkReplyFileImpl::isSequential () const qint64 QNetworkReplyFileImpl::size() const { Q_D(const QNetworkReplyFileImpl); - return d->fileSize; + return d->realFileSize; } /*! @@ -185,17 +175,11 @@ qint64 QNetworkReplyFileImpl::size() const qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen) { Q_D(QNetworkReplyFileImpl); - if (!d->fileEngine) + qint64 ret = d->realFile.read(data, maxlen); + if (ret == 0 && bytesAvailable() == 0) return -1; - - qint64 ret = d->fileEngine->read(data, maxlen); - if (ret == 0 && bytesAvailable() == 0) { - return -1; // everything had been read - } else if (ret > 0) { - d->filePos += ret; - } - - return ret; + else + return ret; } diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h index 627363f..393e3cd 100644 --- a/src/network/access/qnetworkreplyfileimpl_p.h +++ b/src/network/access/qnetworkreplyfileimpl_p.h @@ -86,11 +86,9 @@ class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate { public: QNetworkReplyFileImplPrivate(); - ~QNetworkReplyFileImplPrivate(); - QAbstractFileEngine *fileEngine; - qint64 fileSize; - qint64 filePos; + QFile realFile; + qint64 realFileSize; Q_DECLARE_PUBLIC(QNetworkReplyFileImpl) }; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 41a6c62..485f449 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -57,7 +57,7 @@ Q_DECLARE_METATYPE(QSharedPointer<char>) QT_BEGIN_NAMESPACE inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() - : backend(0), outgoingData(0), outgoingDataBuffer(0), + : backend(0), outgoingData(0), copyDevice(0), cacheEnabled(false), cacheSaveDevice(0), notificationHandlingPaused(false), @@ -212,7 +212,7 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData() if (!outgoingDataBuffer) { // first call, create our buffer - outgoingDataBuffer = new QRingBuffer(); + outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer()); QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData())); QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished())); @@ -306,20 +306,22 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const // Internal code that does a HTTP reply for the synchronous Ajax // in QtWebKit. QVariant synchronousHttpAttribute = req.attribute( - static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1)); - if (backend && synchronousHttpAttribute.toBool()) { - backend->setSynchronous(true); - if (outgoingData && outgoingData->isSequential()) { - outgoingDataBuffer = new QRingBuffer(); - QByteArray data; - do { - data = outgoingData->readAll(); - if (data.isEmpty()) - break; - outgoingDataBuffer->append(data); - } while (1); - } + static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute)); + // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer. + // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway. + if (synchronousHttpAttribute.toBool() && outgoingData) { + outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer()); + qint64 previousDataSize = 0; + do { + previousDataSize = outgoingDataBuffer->size(); + outgoingDataBuffer->append(outgoingData->readAll()); + } while (outgoingDataBuffer->size() != previousDataSize); } + + if (backend) + backend->setSynchronous(synchronousHttpAttribute.toBool()); + + if (outgoingData && backend && !backend->isSynchronous()) { // there is data to be uploaded, e.g. HTTP POST. @@ -349,10 +351,6 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const } } } else { - // No outgoing data (e.g. HTTP GET request) - // or no backend - // if no backend, _q_startOperation will handle the error of this - // for HTTP, we want to send out the request as fast as possible to the network, without // invoking methods in a QueuedConnection #ifndef QT_NO_HTTP @@ -634,8 +632,9 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size) { Q_Q(QNetworkReplyImpl); - // Check attribute() if allocating a buffer of that size can be allowed if (!downloadBuffer) { + // We are requested to create it + // Check attribute() if allocating a buffer of that size can be allowed QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute); if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) { downloadBufferCurrentSize = 0; @@ -650,6 +649,18 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size) return downloadBuffer; } +void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size) +{ + Q_Q(QNetworkReplyImpl); + + downloadBufferPointer = sp; + downloadBuffer = downloadBufferPointer.data(); + downloadBufferCurrentSize = 0; + downloadBufferMaximumSize = size; + q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer)); +} + + void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal) { Q_Q(QNetworkReplyImpl); @@ -746,6 +757,11 @@ void QNetworkReplyImplPrivate::finished() void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage) { Q_Q(QNetworkReplyImpl); + // Can't set and emit multiple errors. + if (errorCode != QNetworkReply::NoError) { + qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once."; + return; + } errorCode = code; q->setErrorString(errorMessage); @@ -803,9 +819,6 @@ QNetworkReplyImpl::~QNetworkReplyImpl() // save had been properly finished. So if it is still enabled it means we got deleted/aborted. if (d->isCachingEnabled()) d->networkCache()->remove(url()); - - if (d->outgoingDataBuffer) - delete d->outgoingDataBuffer; } void QNetworkReplyImpl::abort() diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index 238bee5..1a9ab7e 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -164,6 +164,7 @@ public: void appendDownstreamData(QIODevice *data); void appendDownstreamData(const QByteArray &data); + void setDownloadBuffer(QSharedPointer<char> sp, qint64 size); char* getDownloadBuffer(qint64 size); void appendDownstreamDataDownloadBuffer(qint64, qint64); @@ -175,7 +176,7 @@ public: QNetworkAccessBackend *backend; QIODevice *outgoingData; - QRingBuffer *outgoingDataBuffer; + QSharedPointer<QRingBuffer> outgoingDataBuffer; QIODevice *copyDevice; QAbstractNetworkCache *networkCache() const; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 8d46003..a48a26f 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -226,6 +226,8 @@ QT_BEGIN_NAMESPACE \omitvalue DownloadBufferAttribute + \omitvalue SynchronousRequestAttribute + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 586e6ff..b5ef109 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -84,9 +84,7 @@ public: CookieSaveControlAttribute, MaximumDownloadBufferSizeAttribute, // internal DownloadBufferAttribute, // internal - - // (DownloadBufferAttribute + 1) is reserved internal for QSynchronousHttpNetworkReply - // add the enum in 4.8 + SynchronousRequestAttribute, // internal User = 1000, UserMax = 32767 diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp index 2a46229..9e1eaea 100644 --- a/src/network/bearer/qnetworkconfigmanager.cpp +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -52,7 +52,8 @@ QT_BEGIN_NAMESPACE #define Q_GLOBAL_STATIC_QAPP_DESTRUCTION(TYPE, NAME) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ + static QGlobalStatic<TYPE > this_##NAME \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ static void NAME##_cleanup() \ { \ delete this_##NAME.pointer; \ diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index ead9897..2a942cc 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -2136,7 +2136,7 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1 [char buffer]", data, isprint(int(uchar(*data))) ? *data : '?', *data); #endif - if (d->readBuffer.isEmpty() && d->socketEngine) + if (d->readBuffer.isEmpty() && d->socketEngine && d->socketEngine->isValid()) d->socketEngine->setReadNotificationEnabled(true); return 1; } @@ -2148,7 +2148,8 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) && d->readBuffer.size() < maxSize && d->readBufferMaxSize > 0 && maxSize < d->readBufferMaxSize - && d->socketEngine) { + && d->socketEngine + && d->socketEngine->isValid()) { // Our buffer is empty and a read() was requested for a byte amount that is smaller // than the readBufferMaxSize. This means that we should fill our buffer since we want // such small reads come from the buffer and not always go to the costly socket engine read() @@ -2198,6 +2199,10 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) if (!d->isBuffered) { if (!d->socketEngine) return -1; // no socket engine is probably EOF + if (!d->socketEngine->isValid()) + return -1; // This is for unbuffered TCP when we already had been disconnected + if (d->state != QAbstractSocket::ConnectedState) + return -1; // This is for unbuffered TCP if we're not connected yet qint64 readBytes = d->socketEngine->read(data, maxSize); if (readBytes == -2) { // -2 from the engine means no bytes available (EAGAIN) so read more later @@ -2624,7 +2629,7 @@ void QAbstractSocket::setReadBufferSize(qint64 size) // ensure that the read notification is enabled if we've now got // room in the read buffer // but only if we're not inside canReadNotification -- that will take care on its own - if (size == 0 || d->readBuffer.size() < size) + if ((size == 0 || d->readBuffer.size() < size) && d->state == QAbstractSocket::ConnectedState) // Do not change the notifier unless we are connected. d->socketEngine->setReadNotificationEnabled(true); } } diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index 4e628f3..6a025f2 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -743,7 +743,10 @@ void QHttpSocketEngine::emitReadNotification() { Q_D(QHttpSocketEngine); d->readNotificationActivated = true; - if (d->readNotificationEnabled && !d->readNotificationPending) { + // if there is a connection notification pending we have to emit the readNotification + // incase there is connection error. This is only needed for Windows, but it does not + // hurt in other cases. + if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) { d->readNotificationPending = true; QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection); } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 76c9158..940569a 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -337,15 +337,17 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc } #if !defined(Q_OS_WINCE) - // enable new behavior using - // SIO_UDP_CONNRESET - DWORD dwBytesReturned = 0; - int bNewBehavior = 1; - if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), - NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) { - // not to worry isBogusUdpReadNotification() should handle this otherwise - int err = WSAGetLastError(); - WS_ERROR_DEBUG(err); + if (socketType == QAbstractSocket::UdpSocket) { + // enable new behavior using + // SIO_UDP_CONNRESET + DWORD dwBytesReturned = 0; + int bNewBehavior = 1; + if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), + NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) { + // not to worry isBogusUdpReadNotification() should handle this otherwise + int err = WSAGetLastError(); + WS_ERROR_DEBUG(err); + } } #endif @@ -639,6 +641,11 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin socketState = QAbstractSocket::UnconnectedState; break; } + if (value == WSAEADDRNOTAVAIL) { + setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString); + socketState = QAbstractSocket::UnconnectedState; + break; + } } // fall through } diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 4252123..61f27fe 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -143,6 +143,15 @@ setDefaultCaCertificates(). \endlist + \note If available, root certificates on Unix (excluding Mac OS X) will be + loaded on demand from the standard certificate directories. If + you do not want to load root certificates on demand, you need to call either + the static function setDefaultCaCertificates() before the first SSL handshake + is made in your application, (e.g. via + "QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());"), + or call setCaCertificates() on your QSslSocket instance prior to the SSL + handshake. + For more information about ciphers and certificates, refer to QSslCipher and QSslCertificate. @@ -1249,6 +1258,7 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) { Q_D(QSslSocket); d->configuration.caCertificates = certificates; + d->allowRootCertOnDemandLoading = false; } /*! @@ -1258,6 +1268,9 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates) handshake with addCaCertificate(), addCaCertificates(), and setCaCertificates(). + \note On Unix, this method may return an empty list if the root + certificates are loaded on demand. + \sa addCaCertificate(), addCaCertificates(), setCaCertificates() */ QList<QSslCertificate> QSslSocket::caCertificates() const @@ -1311,10 +1324,9 @@ void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certific /*! Sets the default CA certificate database to \a certificates. The default CA certificate database is originally set to your system's - default CA certificate database. If no system default database is - found, Qt will provide its own default database. You can override - the default CA certificate database with your own CA certificate - database using this function. + default CA certificate database. You can override the default CA + certificate database with your own CA certificate database using + this function. Each SSL socket's CA certificate database is initialized to the default CA certificate database. @@ -1336,6 +1348,9 @@ void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certific Each SSL socket's CA certificate database is initialized to the default CA certificate database. + \note On Unix, this method may return an empty list if the root + certificates are loaded on demand. + \sa caCertificates() */ QList<QSslCertificate> QSslSocket::defaultCaCertificates() @@ -1803,6 +1818,7 @@ QSslSocketPrivate::QSslSocketPrivate() , connectionEncrypted(false) , ignoreAllSslErrors(false) , readyReadEmittedPointer(0) + , allowRootCertOnDemandLoading(true) , plainSocket(0) { QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); @@ -1879,6 +1895,7 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph */ QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates() { + // ### Qt5: rename everything containing "caCertificates" to "rootCertificates" or similar QSslSocketPrivate::ensureInitialized(); QMutexLocker locker(&globalData()->mutex); return globalData()->config->caCertificates; @@ -1893,6 +1910,9 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates = certs; + // when the certificates are set explicitly, we do not want to + // load the system certificates on demand + s_loadRootCertsOnDemand = false; } /*! @@ -2192,6 +2212,20 @@ void QSslSocketPrivate::_q_flushReadBuffer() transmit(); } +/*! + \internal +*/ +QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories() +{ + return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ... + << "/usr/lib/ssl/certs/" // Gentoo, Mandrake + << "/usr/share/ssl/" // Centos, Redhat, SuSE + << "/usr/local/ssl/" // Normal OpenSSL Tarball + << "/var/ssl/certs/" // AIX + << "/usr/local/ssl/certs/" // Solaris + << "/opt/openssl/certs/"; // HP-UX +} + QT_END_NAMESPACE // For private slots diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 84e14ff..83714ed 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -80,6 +80,7 @@ QT_BEGIN_NAMESPACE bool QSslSocketPrivate::s_libraryLoaded = false; bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; +bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; /* \internal @@ -317,6 +318,13 @@ init_context: q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); } + if (s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) { + // tell OpenSSL the directories where to look up the root certs on demand + QList<QByteArray> unixDirs = unixRootCertDirectories(); + for (int a = 0; a < unixDirs.count(); ++a) + q_SSL_CTX_load_verify_locations(ctx, 0, unixDirs.at(a).constData()); + } + // Register a custom callback to get all verification errors. X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback); @@ -517,8 +525,22 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded() } else { qWarning("could not load crypt32 library"); // should never happen } +#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_MAC) + // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) + QList<QByteArray> dirs = unixRootCertDirectories(); + QStringList symLinkFilter; + symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"); + for (int a = 0; a < dirs.count(); ++a) { + QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); + if (iterator.hasNext()) { + s_loadRootCertsOnDemand = true; + break; + } + } #endif - setDefaultCaCertificates(systemCaCertificates()); + // if on-demand loading was not enabled, load the certs now + if (!s_loadRootCertsOnDemand) + setDefaultCaCertificates(systemCaCertificates()); } /*! @@ -814,15 +836,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() } #elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) QSet<QString> certFiles; - QList<QByteArray> directories; - directories << "/etc/ssl/certs/"; // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ... - directories << "/usr/lib/ssl/certs/"; // Gentoo, Mandrake - directories << "/usr/share/ssl/"; // Centos, Redhat, SuSE - directories << "/usr/local/ssl/"; // Normal OpenSSL Tarball - directories << "/var/ssl/certs/"; // AIX - directories << "/usr/local/ssl/certs/"; // Solaris - directories << "/opt/openssl/certs/"; // HP-UX - + QList<QByteArray> directories = unixRootCertDirectories(); QDir currentDir; QStringList nameFilters; nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt"); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 38598b6..b9a05f3 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -262,6 +262,7 @@ DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, #endif DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return) #ifdef Q_OS_SYMBIAN #define RESOLVEFUNC(func, ordinal, lib) \ @@ -630,6 +631,7 @@ bool q_resolveOpenSslSymbols() #endif RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf, 1153, libs.second ) RESOLVEFUNC(OPENSSL_add_all_algorithms_conf, 1152, libs.second ) + RESOLVEFUNC(SSL_CTX_load_verify_locations, 34, libs.second ) #else // Q_OS_SYMBIAN #ifdef SSLEAY_MACROS RESOLVEFUNC(ASN1_dup) @@ -754,6 +756,7 @@ bool q_resolveOpenSslSymbols() #endif RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf) RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) + RESOLVEFUNC(SSL_CTX_load_verify_locations) #endif // Q_OS_SYMBIAN symbolsResolved = true; delete libs.first; diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 954ffba..c05dfe11 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -412,6 +412,7 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #endif void q_OPENSSL_add_all_algorithms_noconf(); void q_OPENSSL_add_all_algorithms_conf(); +int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); // Helper function class QDateTime; diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 3a14488..7b92f95 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -112,6 +112,8 @@ public: // that was used for connecting to. QString verificationPeerName; + bool allowRootCertOnDemandLoading; + static bool supportsSsl(); static void ensureInitialized(); static void deinitialize(); @@ -168,6 +170,9 @@ private: static bool s_libraryLoaded; static bool s_loadedCiphersAndCerts; +protected: + static bool s_loadRootCertsOnDemand; + static QList<QByteArray> unixRootCertDirectories(); }; QT_END_NAMESPACE diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 2cf2412..74b1ace 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -685,10 +685,13 @@ void Moc::parse() if (parseEnum(&enumDef)) def.enumList += enumDef; } break; + case SEMIC: + case COLON: + break; default: FunctionDef funcDef; funcDef.access = access; - int rewind = index; + int rewind = index--; if (parseMaybeFunction(&def, &funcDef)) { if (funcDef.isConstructor) { if ((access == FunctionDef::Public) && funcDef.isInvokable) { diff --git a/tests/auto/moc/tst_moc.cpp b/tests/auto/moc/tst_moc.cpp index 56a3107..203f0ae 100644 --- a/tests/auto/moc/tst_moc.cpp +++ b/tests/auto/moc/tst_moc.cpp @@ -493,6 +493,7 @@ private slots: void QTBUG5590_dummyProperty(); void QTBUG12260_defaultTemplate(); void notifyError(); + void QTBUG17635_invokableAndProperty(); void revisions(); void warnings_data(); void warnings(); @@ -1390,6 +1391,31 @@ void tst_Moc::notifyError() #endif } +class QTBUG_17635_InvokableAndProperty : public QObject +{ + Q_OBJECT +public: + Q_PROPERTY(int numberOfEggs READ numberOfEggs) + Q_PROPERTY(int numberOfChickens READ numberOfChickens) + Q_INVOKABLE QString getEgg(int index) { return QString::fromLatin1("Egg"); } + Q_INVOKABLE QString getChicken(int index) { return QString::fromLatin1("Chicken"); } + int numberOfEggs() { return 2; } + int numberOfChickens() { return 4; } +}; + +void tst_Moc::QTBUG17635_invokableAndProperty() +{ + //Moc used to fail parsing Q_INVOKABLE if they were dirrectly following a Q_PROPERTY; + QTBUG_17635_InvokableAndProperty mc; + QString val; + QMetaObject::invokeMethod(&mc, "getEgg", Q_RETURN_ARG(QString, val), Q_ARG(int, 10)); + QCOMPARE(val, QString::fromLatin1("Egg")); + QMetaObject::invokeMethod(&mc, "getChicken", Q_RETURN_ARG(QString, val), Q_ARG(int, 10)); + QCOMPARE(val, QString::fromLatin1("Chicken")); + QVERIFY(mc.metaObject()->indexOfProperty("numberOfEggs") != -1); + QVERIFY(mc.metaObject()->indexOfProperty("numberOfChickens") != -1); +} + // If changed, update VersionTestNotify below class VersionTest : public QObject { diff --git a/tests/auto/network.pro b/tests/auto/network.pro index 7d83054..b427f1c 100644 --- a/tests/auto/network.pro +++ b/tests/auto/network.pro @@ -35,6 +35,8 @@ SUBDIRS=\ qsslerror \ qsslkey \ qsslsocket \ + qsslsocket_onDemandCertificates_member \ + qsslsocket_onDemandCertificates_static \ # qnetworkproxyfactory \ # Uses a hardcoded proxy configuration !contains(QT_CONFIG, private_tests): SUBDIRS -= \ diff --git a/tests/auto/networkselftest/tst_networkselftest.cpp b/tests/auto/networkselftest/tst_networkselftest.cpp index cfcafc0..64de64a 100644 --- a/tests/auto/networkselftest/tst_networkselftest.cpp +++ b/tests/auto/networkselftest/tst_networkselftest.cpp @@ -967,7 +967,7 @@ void tst_NetworkSelfTest::smbServer() QVERIFY2(f, qt_error_string().toLocal8Bit()); char buf[128]; - size_t ret = fread(buf, sizeof buf, 1, f); + size_t ret = fread(buf, 1, sizeof buf, f); fclose(f); QCOMPARE(ret, strlen(contents)); diff --git a/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp b/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp index 6331db7..db0d0a7 100644 --- a/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp +++ b/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp @@ -331,7 +331,7 @@ void tst_QAbstractNetworkCache::checkSynchronous() QNetworkRequest request(realUrl); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1), + QNetworkRequest::SynchronousRequestAttribute, true); // prime the cache diff --git a/tests/auto/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp b/tests/auto/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp index 10fa7c6..41da16e 100644 --- a/tests/auto/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp +++ b/tests/auto/qnetworkproxyfactory/tst_qnetworkproxyfactory.cpp @@ -60,8 +60,8 @@ QString tst_QNetworkProxyFactory::formatProxyName(const QNetworkProxy & proxy) c QString proxyName; if (!proxy.user().isNull()) proxyName.append("%1:%2@").arg(proxy.user(), proxy.password()); - proxyName.append("%1:%2").arg(proxy.hostName(), proxy.port()); - proxyName.append(" (type=%1, capabilities=%2)").arg(proxy.type(), proxy.capabilities()); + proxyName.append(QString("%1:%2").arg(proxy.hostName()).arg(proxy.port())); + proxyName.append(QString(" (type=%1, capabilities=%2)").arg(proxy.type()).arg(proxy.capabilities())); return proxyName; } diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index 3715162..93e3051 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -84,8 +84,6 @@ Q_DECLARE_METATYPE(QList<QNetworkProxy>) Q_DECLARE_METATYPE(QNetworkReply::NetworkError) Q_DECLARE_METATYPE(QBuffer*) -const int SynchronousRequestAttribute = QNetworkRequest::DownloadBufferAttribute + 1; - class QNetworkReplyPtr: public QSharedPointer<QNetworkReply> { public: @@ -327,6 +325,7 @@ private Q_SLOTS: void ioGetFromHttpBrokenChunkedEncoding(); void qtbug12908compressedHttpReply(); + void compressedHttpReplyBrokenGzip(); void getFromUnreachableIp(); @@ -340,7 +339,9 @@ private Q_SLOTS: void synchronousRequest_data(); void synchronousRequest(); +#ifndef QT_NO_OPENSSL void synchronousRequestSslFailure(); +#endif void httpAbort(); @@ -457,6 +458,7 @@ private: { //qDebug() << "connectSocketSignals" << client; connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot())); connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotError(QAbstractSocket::SocketError))); } @@ -484,15 +486,19 @@ public slots: if (multiple) receivedData.remove(0, doubleEndlPos+4); - client->write(dataToTransmit); - while (client->bytesToWrite() > 0) - client->waitForBytesWritten(); + // we need to emulate the bytesWrittenSlot call if the data is empty. + if (dataToTransmit.size() == 0) + QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); + else + client->write(dataToTransmit); + } + } - if (doClose) { - client->disconnectFromHost(); - disconnect(client, 0, this, 0); - client = 0; - } + void bytesWrittenSlot() { + if (doClose && client->bytesToWrite() == 0) { + client->disconnectFromHost(); + disconnect(client, 0, this, 0); + client = 0; } } }; @@ -961,7 +967,9 @@ tst_QNetworkReply::tst_QNetworkReply() qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy qRegisterMetaType<QAuthenticator *>(); qRegisterMetaType<QNetworkProxy>(); +#ifndef QT_NO_OPENSSL qRegisterMetaType<QList<QSslError> >(); +#endif Q_SET_DEFAULT_IAP @@ -1057,7 +1065,7 @@ QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op, returnCode = Timeout; int code = Success; - if (request.attribute(static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute)).toBool()) { + if (request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) { if (reply->isFinished()) code = reply->error() != QNetworkReply::NoError ? Failure : Success; else @@ -1494,6 +1502,12 @@ void tst_QNetworkReply::getErrors() { QFETCH(QString, url); QNetworkRequest request(url); + +#if defined(Q_OS_WIN) || defined (Q_OS_SYMBIAN) + if (qstrcmp(QTest::currentDataTag(), "empty-scheme-host") == 0) + QTest::ignoreMessage(QtWarningMsg, "QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files"); +#endif + QNetworkReplyPtr reply = manager.get(request); reply->setParent(this); // we have expect-fails @@ -1508,6 +1522,9 @@ void tst_QNetworkReply::getErrors() //qDebug() << reply->errorString(); QFETCH(int, error); +#if defined(Q_OS_WIN) || defined (Q_OS_SYMBIAN) + QEXPECT_FAIL("empty-scheme-host", "this is expected to fail on Windows and Symbian, QTBUG-17731", Abort); +#endif QEXPECT_FAIL("ftp-is-dir", "QFtp cannot provide enough detail", Abort); // the line below is not necessary QEXPECT_FAIL("ftp-dir-not-readable", "QFtp cannot provide enough detail", Abort); @@ -1680,7 +1697,7 @@ void tst_QNetworkReply::putToHttpSynchronous() QFETCH(QByteArray, data); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); @@ -1740,7 +1757,7 @@ void tst_QNetworkReply::postToHttpSynchronous() QNetworkRequest request(url); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply; @@ -2255,7 +2272,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() // now check with synchronous calls: { request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); @@ -2279,7 +2296,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous() QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt")); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); @@ -2364,7 +2381,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() reference.seek(0); { request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); @@ -2390,7 +2407,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous() QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); manager.setProxy(proxy); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); @@ -3503,7 +3520,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous() QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); QNetworkRequest request(url); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]); @@ -3695,12 +3712,12 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() incomingSocket->setReadBufferSize(1*1024); QTestEventLoop::instance().enterLoop(2); // some progress should have been made + QVERIFY(!spy.isEmpty()); QList<QVariant> args = spy.last(); qDebug() << "tst_QNetworkReply::ioPostToHttpsUploadProgress" << args.at(0).toLongLong() << sourceFile.size() << spy.size(); - QVERIFY(!args.isEmpty()); QVERIFY(args.at(0).toLongLong() > 0); // FIXME this is where it messes up @@ -3711,16 +3728,16 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() incomingSocket->read(16*1024); QTestEventLoop::instance().enterLoop(2); // some more progress than before + QVERIFY(!spy.isEmpty()); QList<QVariant> args2 = spy.last(); - QVERIFY(!args2.isEmpty()); QVERIFY(args2.at(0).toLongLong() > args.at(0).toLongLong()); // set the read buffer to unlimited incomingSocket->setReadBufferSize(0); QTestEventLoop::instance().enterLoop(10); // progress should be finished + QVERIFY(!spy.isEmpty()); QList<QVariant> args3 = spy.last(); - QVERIFY(!args3.isEmpty()); QVERIFY(args3.at(0).toLongLong() > args2.at(0).toLongLong()); QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong()); QCOMPARE(args3.at(0).toLongLong(), sourceFile.size()); @@ -3810,6 +3827,8 @@ void tst_QNetworkReply::ioGetFromBuiltinHttp() if (reader.data.size() < testData.size()) { // oops? QCOMPARE(reader.data, testData.mid(0, reader.data.size())); qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing"; + QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Abort); + QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Abort); } QCOMPARE(reader.data.size(), testData.size()); QCOMPARE(reader.data, testData); @@ -3822,8 +3841,8 @@ void tst_QNetworkReply::ioGetFromBuiltinHttp() const int maxRate = rate * 1024 * (100+allowedDeviation) / 100; qDebug() << minRate << "<="<< server.transferRate << "<=" << maxRate << "?"; QVERIFY(server.transferRate >= minRate); - QEXPECT_FAIL("http+limited", "Limiting is broken right now", Continue); - QEXPECT_FAIL("https+limited", "Limiting is broken right now", Continue); + QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Continue); + QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Continue); QVERIFY(server.transferRate <= maxRate); } } @@ -4274,7 +4293,7 @@ void tst_QNetworkReply::receiveCookiesFromHttpSynchronous() QNetworkRequest request(url); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply; @@ -4366,7 +4385,7 @@ void tst_QNetworkReply::sendCookiesSynchronous() QNetworkRequest request(url); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply; @@ -4508,7 +4527,7 @@ void tst_QNetworkReply::httpProxyCommandsSynchronous() // send synchronous request request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply = manager.get(request); @@ -5234,6 +5253,7 @@ void tst_QNetworkReply::qtbug12908compressedHttpReply() // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA"); QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii()); + QCOMPARE(decodedFile.size(), 63); MiniHttpServer server(header.toAscii() + decodedFile); server.doClose = true; @@ -5246,6 +5266,31 @@ void tst_QNetworkReply::qtbug12908compressedHttpReply() QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->size(), qint64(16384)); + QCOMPARE(reply->readAll(), QByteArray(16384, '\0')); +} + +void tst_QNetworkReply::compressedHttpReplyBrokenGzip() +{ + QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n"); + + // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz + // Then change "BMQ" to "BMX" + QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMXEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA"); + QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii()); + QCOMPARE(decodedFile.size(), 63); + + MiniHttpServer server(header.toAscii() + decodedFile); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure); } // TODO add similar test for FTP @@ -5454,7 +5499,7 @@ void tst_QNetworkReply::synchronousRequest() #endif request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply; @@ -5485,6 +5530,7 @@ void tst_QNetworkReply::synchronousRequest() reply->deleteLater(); } +#ifndef QT_NO_OPENSSL void tst_QNetworkReply::synchronousRequestSslFailure() { // test that SSL won't be accepted with self-signed certificate, @@ -5494,7 +5540,7 @@ void tst_QNetworkReply::synchronousRequestSslFailure() QUrl url("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); QNetworkRequest request(url); request.setAttribute( - static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + QNetworkRequest::SynchronousRequestAttribute, true); QNetworkReplyPtr reply; QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &))); @@ -5503,6 +5549,7 @@ void tst_QNetworkReply::synchronousRequestSslFailure() QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); QCOMPARE(sslErrorsSpy.count(), 0); } +#endif void tst_QNetworkReply::httpAbort() { diff --git a/tests/auto/qscriptjstestsuite/expect_fail.txt b/tests/auto/qscriptjstestsuite/expect_fail.txt new file mode 100644 index 0000000..7f93378 --- /dev/null +++ b/tests/auto/qscriptjstestsuite/expect_fail.txt @@ -0,0 +1,198 @@ +ecma/Array/15.4.3.1-2.js | var props = ''; for ( p in Array ) { props += p } props + +ecma/Boolean/15.6.3.1-1.js | var str='';for ( p in Boolean ) { str += p } str; + +ecma/Expressions/11.4.1.js | var abc; delete(abc) + +ecma/FunctionObjects/15.3.3.1-2.js | var str='';for (prop in Function ) str += prop; str; + +ecma/ObjectObjects/15.2.3.1-1.js | var str = '';for ( p in Object ) { str += p; }; str + +ecma/Statements/12.6.3-11.js | result = ""; for ( p in Number ) { result += String(p) }; +ecma/Statements/12.6.3-2.js | Boolean.prototype.foo = 34; for ( j in Boolean ) Boolean[j] + +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4256) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4257) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4258) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4259) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4260) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4261) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4262) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4263) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4264) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4265) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4266) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4267) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4268) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4269) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4270) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4271) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4272) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4273) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4274) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4275) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4276) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4277) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4278) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4279) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4280) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4281) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4282) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4283) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4284) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4285) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4286) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4287) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4288) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4289) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4290) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4291) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4292) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-2.js | var s = new String( String.fromCharCode(4293) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-5.js | var s = new String( String.fromCharCode(1024) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.11-5.js | var s = new String( String.fromCharCode(1037) ); s.toLowerCase().charCodeAt(0) +ecma/String/15.5.4.12-1.js | var s = new String( String.fromCharCode(181) ); s.toUpperCase().charCodeAt(0) +ecma/String/15.5.4.12-1.js | var s = new String( String.fromCharCode(329) ); s.toUpperCase().charCodeAt(0) +ecma/String/15.5.4.12-4.js | var s = new String( String.fromCharCode(1104) ); s.toUpperCase().charCodeAt(0) +ecma/String/15.5.4.12-4.js | var s = new String( String.fromCharCode(1117) ); s.toUpperCase().charCodeAt(0) +ecma/String/15.5.4.12-5.js | var s = new String( String.fromCharCode(1415) ); s.toUpperCase().charCodeAt(0) + +ecma/TypeConversion/9.3.1-3.js | -"\u20001234\u2001" +ecma/TypeConversion/9.3.1-3.js | - "-0x123456789abcde8" + +ecma/extensions/15.1.2.1-1.js | var PROPS = ''; for ( p in eval ) { PROPS += p }; PROPS + +ecma/GlobalObject/15.1.2.2-1.js | var PROPS=''; for ( var p in parseInt ) { PROPS += p; }; PROPS +ecma/GlobalObject/15.1.2.3-1.js | var MYPROPS=''; for ( var p in parseFloat ) { MYPROPS += p }; MYPROPS +ecma/GlobalObject/15.1.2.4.js | var MYPROPS=''; for ( var p in escape ) { MYPROPS+= p}; MYPROPS +ecma/GlobalObject/15.1.2.5-1.js | var MYPROPS=''; for ( var p in unescape ) { MYPROPS+= p }; MYPROPS +ecma/GlobalObject/15.1.2.6.js | var MYPROPS=''; for ( var p in isNaN ) { MYPROPS+= p }; MYPROPS +ecma/GlobalObject/15.1.2.7.js | var MYPROPS=''; for ( p in isFinite ) { MYPROPS+= p }; MYPROPS + +ecma_3/Array/15.4.5.1-01.js | 15.4.5.1 - array.length coverage + +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 0 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 1 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 2 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 3 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 4 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 5 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 6 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 7 +ecma_3/extensions/regress-274152.js | Do not ignore unicode format-control characters: 8 +ecma_3/extensions/regress-368516.js | Treat unicode BOM characters as whitespace: 0 +ecma_3/extensions/regress-368516.js | Treat unicode BOM characters as whitespace: 1 + +ecma_3/Date/15.9.4.3.js | 15.9.4.3 - Date.UTC edge-case arguments.: date Infinity +ecma_3/Date/15.9.4.3.js | 15.9.4.3 - Date.UTC edge-case arguments.: hours Infinity +ecma_3/Date/15.9.4.3.js | 15.9.4.3 - Date.UTC edge-case arguments.: minutes Infinity +ecma_3/Date/15.9.4.3.js | 15.9.4.3 - Date.UTC edge-case arguments.: seconds Infinity +ecma_3/Function/regress-131964.js | Section 1 of test - +ecma_3/Function/regress-313570.js | length of objects whose prototype chain includes a function: immutable +ecma_3/FunExpr/fe-001.js | Both functions were defined. + +ecma_3/LexicalConventions/7.9.1.js | Automatic Semicolon insertion in postfix expressions: expr\n++ +ecma_3/LexicalConventions/7.9.1.js | Automatic Semicolon insertion in postfix expressions: expr\n-- +ecma_3/LexicalConventions/7.9.1.js | Automatic Semicolon insertion in postfix expressions: (x\n)-- y +ecma_3/LexicalConventions/7.9.1.js | Automatic Semicolon insertion in postfix expressions: (x)-- y + +ecma_3/Object/8.6.1-01.js | In strict mode, setting a read-only property should generate a warning: Throw if STRICT and WERROR is enabled + +ecma_3/Operators/order-01.js | operator evaluation order: 11.8.2 > +ecma_3/Operators/order-01.js | operator evaluation order: 11.8.4 >= + +ecma_3/RegExp/15.10.2-1.js | Section 7 of test - \nregexp = /(z)((a+)?(b+)?(c))*/\nstring = 'zaacbbbcac'\nERROR !!! regexp failed to give expected match array:\nExpect: ["zaacbbbcac", "z", "ac", "a", , "c"]\nActual: ["zaacbbbcac", "z", "ac", "a", "bbb", "c"]\n +ecma_3/RegExp/15.10.2-1.js | Section 8 of test - \nregexp = /(a*)*/\nstring = 'b'\nERROR !!! regexp failed to give expected match array:\nExpect: ["", , ]\nActual: ["", ""]\n +ecma_3/RegExp/15.10.2-1.js | Section 12 of test - \nregexp = /(.*?)a(?!(a+)b\2c)\2(.*)/\nstring = 'baaabaac'\nERROR !!! regexp failed to give expected match array:\nExpect: ["baaabaac", "ba", , "abaac"]\nActual: ["baaabaac", "ba", "aa", "abaac"]\n +ecma_3/RegExp/perlstress-001.js | Section 218 of test - \nregexp = /((foo)|(bar))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: ["foobar", "bar", , "bar"]\nActual: ["foobar", "bar", "foo", "bar"]\n +ecma_3/RegExp/perlstress-001.js | Section 234 of test - \nregexp = /(?:(f)(o)(o)|(b)(a)(r))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: ["foobar", , , , "b", "a", "r"]\nActual: ["foobar", "f", "o", "o", "b", "a", "r"]\n +ecma_3/RegExp/perlstress-001.js | Section 241 of test - \nregexp = /^(?:b|a(?=(.)))*\1/\nstring = 'abc'\nERROR !!! regexp failed to give expected match array:\nExpect: ["ab", , ]\nActual: ["ab", "b"]\n +ecma_3/RegExp/perlstress-001.js | Section 412 of test - \nregexp = /^(a(b)?)+$/\nstring = 'aba'\nERROR !!! regexp failed to give expected match array:\nExpect: ["aba", "a", , ]\nActual: ["aba", "a", "b"]\n +ecma_3/RegExp/perlstress-001.js | Section 413 of test - \nregexp = /^(aa(bb)?)+$/\nstring = 'aabbaa'\nERROR !!! regexp failed to give expected match array:\nExpect: ["aabbaa", "aa", , ]\nActual: ["aabbaa", "aa", "bb"]\n + +ecma_3/RegExp/regress-209919.js | Section 1 of test - \nregexp = /(a|b*)*/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: ["a", "a"]\nActual: ["a", ""]\n +ecma_3/RegExp/regress-209919.js | Section 3 of test - \nregexp = /(b*)*/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: ["", , ]\nActual: ["", ""]\n +ecma_3/RegExp/regress-209919.js | Section 5 of test - \nregexp = /^\-?(\d{1,}|\.{0,})*(\,\d{1,})?$/\nstring = '100.00'\nERROR !!! regexp failed to give expected match array:\nExpect: ["100.00", "00", , ]\nActual: ["100.00", "", , ]\n +ecma_3/RegExp/regress-209919.js | Section 6 of test - \nregexp = /^\-?(\d{1,}|\.{0,})*(\,\d{1,})?$/\nstring = '100,00'\nERROR !!! regexp failed to give expected match array:\nExpect: ["100,00", "100", ",00"]\nActual: ["100,00", "", ",00"]\n +ecma_3/RegExp/regress-209919.js | Section 7 of test - \nregexp = /^\-?(\d{1,}|\.{0,})*(\,\d{1,})?$/\nstring = '1.000,00'\nERROR !!! regexp failed to give expected match array:\nExpect: ["1.000,00", "000", ",00"]\nActual: ["1.000,00", "", ",00"]\n + +ecma_3/String/15.5.4.11.js | Section 7 +ecma_3/String/15.5.4.11.js | Section 24 +ecma_3/String/15.5.4.11.js | Section 26 +ecma_3/String/15.5.4.11.js | Section 28 +ecma_3/String/15.5.4.11.js | Section 30 +ecma_3/String/15.5.4.14.js | 15.5.4.14 - String.prototype.split(/()/) + +ecma_3/Unicode/regress-352044-01.js | issues with Unicode escape sequences in JavaScript source code +ecma_3/Unicode/uc-001.js | Unicode format-control character test (Category Cf.) + +ecma_2/RegExp/exec-001.js | NO TESTS EXIST +ecma_2/String/replace-001.js | NO TESTS EXIST + +[Q_CC_MSVC] +ecma_3/Expressions/11.7.3-01.js | 11.7.3 - >>> should evaluate operands in order: order | QTBUG-8056 +ecma_3/Operators/order-01.js | operator evaluation order: 11.7.3 >>> | QTBUG-8056 +ecma_3/Operators/order-01.js | operator evaluation order: 11.13.2 >>>= | QTBUG-8056 + +[Q_CC_MINGW] +ecma/Math/15.8.2.13.js | Math.pow(NaN,0) +ecma/Math/15.8.2.13.js | Math.pow(NaN,-0) +ecma/Math/15.8.2.5.js | Math.atan2(Infinity, Infinity) +ecma/Math/15.8.2.5.js | Math.atan2(Infinity, -Infinity) +ecma/Math/15.8.2.5.js | Math.atan2(-Infinity, Infinity) +ecma/Math/15.8.2.5.js | Math.atan2(-Infinity, -Infinity) + +[Q_OS_SOLARIS] +ecma/Expressions/11.13.2-2.js | VAR1 = -0; VAR2= Infinity; VAR2 /= VAR1 +ecma/Expressions/11.13.2-2.js | VAR1 = -0; VAR2= -Infinity; VAR2 /= VAR1 +ecma/Expressions/11.13.2-2.js | VAR1 = 1; VAR2= -0; VAR1 /= VAR2 +ecma/Expressions/11.13.2-2.js | VAR1 = -1; VAR2= -0; VAR1 /= VAR2 +ecma/Expressions/11.5.2.js | Number.POSITIVE_INFINITY / -0 +ecma/Expressions/11.5.2.js | Number.NEGATIVE_INFINITY / -0 +ecma/Expressions/11.5.2.js | 1 / -0 +ecma/Expressions/11.5.2.js | -1 / -0 +ecma/Math/15.8.2.10.js | Math.log(-0.0000001) +ecma/Math/15.8.2.10.js | Math.log(-1) +ecma/Math/15.8.2.11.js | Infinity/Math.max(-0,-0) +ecma/Math/15.8.2.12.js | Infinity/Math.min(0,-0) +ecma/Math/15.8.2.12.js | Infinity/Math.min(-0,-0) +ecma/Math/15.8.2.13.js | Math.pow(NaN,0) +ecma/Math/15.8.2.13.js | Math.pow(NaN,-0) +ecma/Math/15.8.2.13.js | Infinity/Math.pow(-Infinity, -1) +ecma/Math/15.8.2.13.js | Math.pow(0, -1) +ecma/Math/15.8.2.13.js | Math.pow(0, -0.5) +ecma/Math/15.8.2.13.js | Math.pow(0, -1000) +ecma/Math/15.8.2.13.js | Infinity/Math.pow(-0, 1) +ecma/Math/15.8.2.13.js | Infinity/Math.pow(-0, 3) +ecma/Math/15.8.2.13.js | Math.pow(-0, -2) +ecma/Math/15.8.2.15.js | Infinity/Math.round(-0) +ecma/Math/15.8.2.15.js | Infinity/Math.round(-0.49) +ecma/Math/15.8.2.15.js | Infinity/Math.round(-0.5) +ecma/Math/15.8.2.17.js | Infinity/Math.sqrt(-0) +ecma/Math/15.8.2.18.js | Infinity/Math.tan(-0) +ecma/Math/15.8.2.2.js | Math.acos(1.00000001) +ecma/Math/15.8.2.2.js | Math.acos(11.00000001) +ecma/Math/15.8.2.3.js | Math.asin(1.000001) +ecma/Math/15.8.2.3.js | Math.asin(-1.000001) +ecma/Math/15.8.2.3.js | Infinity/Math.asin(-0) +ecma/Math/15.8.2.4.js | Infinity/Math.atan(-0) +ecma/Math/15.8.2.5.js | Math.atan2(0, -0) +ecma/Math/15.8.2.5.js | Infinity/Math.atan2(-0, 1) +ecma/Math/15.8.2.5.js | Math.atan2(-0,\t-0) +ecma/Math/15.8.2.5.js | Math.atan2(-0,\t-1) +ecma/Math/15.8.2.6.js | Infinity/Math.ceil('-0') +ecma/Math/15.8.2.6.js | Infinity/Math.ceil(-0) +ecma/Math/15.8.2.6.js | Infinity/Math.ceil(-Number.MIN_VALUE) +ecma/Math/15.8.2.6.js | Infinity/Math.ceil(-0.9) +ecma/Math/15.8.2.9.js | Infinity/Math.floor(-0) +ecma/TypeConversion/9.3.1-3.js | var z = 0; print(1/-z) +ecma/TypeConversion/9.3.1-3.js | 1/-1e-2000 + +[Q_OS_SYMBIAN] +ecma/Math/15.8.2.13.js | Math.pow(-1, 0.5) +ecma/Math/15.8.2.13.js | Math.pow(-1, -0.5) +ecma_3/Operators/order-01.js | operator evaluation order: 11.5.1 * +ecma_3/Operators/order-01.js | operator evaluation order: 11.5.2 / +ecma_3/Operators/order-01.js | operator evaluation order: 11.6.2 - +ecma_3/Operators/order-01.js | operator evaluation order: 11.13.2 *= +ecma_3/Operators/order-01.js | operator evaluation order: 11.13.2 /= diff --git a/tests/auto/qscriptjstestsuite/qscriptjstestsuite.pro b/tests/auto/qscriptjstestsuite/qscriptjstestsuite.pro index b1ddd64..471aa02 100644 --- a/tests/auto/qscriptjstestsuite/qscriptjstestsuite.pro +++ b/tests/auto/qscriptjstestsuite/qscriptjstestsuite.pro @@ -1,6 +1,8 @@ load(qttest_p4) QT = core script SOURCES += tst_qscriptjstestsuite.cpp +RESOURCES += qscriptjstestsuite.qrc +include(../qscriptv8testsuite/abstracttestsuite.pri) !symbian: DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/auto/qscriptjstestsuite/qscriptjstestsuite.qrc b/tests/auto/qscriptjstestsuite/qscriptjstestsuite.qrc new file mode 100644 index 0000000..4a4eb60 --- /dev/null +++ b/tests/auto/qscriptjstestsuite/qscriptjstestsuite.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>expect_fail.txt</file> + <file>skip.txt</file> +</qresource> +</RCC> diff --git a/tests/auto/qscriptjstestsuite/skip.txt b/tests/auto/qscriptjstestsuite/skip.txt new file mode 100644 index 0000000..2dc0ccb --- /dev/null +++ b/tests/auto/qscriptjstestsuite/skip.txt @@ -0,0 +1,9 @@ +.+/15\.9\.2\..+ | unstable on slow machines +.+/15\.9\.5\..+ | too slooow +regress-130451.js | asserts +regress-322135-01.js | asserts +regress-322135-02.js | asserts +regress-322135-03.js | takes forever +regress-322135-04.js | takes forever +ecma_3/RegExp/regress-375715-04.js | bug +ecma_3/RegExp/regress-289669.js | Can fail due to relying on wall-clock time diff --git a/tests/auto/qscriptjstestsuite/tst_qscriptjstestsuite.cpp b/tests/auto/qscriptjstestsuite/tst_qscriptjstestsuite.cpp index 43a6dba..e042dfe 100644 --- a/tests/auto/qscriptjstestsuite/tst_qscriptjstestsuite.cpp +++ b/tests/auto/qscriptjstestsuite/tst_qscriptjstestsuite.cpp @@ -40,44 +40,18 @@ ****************************************************************************/ +#include "abstracttestsuite.h" #include <QtTest/QtTest> #include <QtScript> #if defined(Q_OS_SYMBIAN) -# define SRCDIR "" +# define SRCDIR "." #endif //TESTED_CLASS= //TESTED_FILES= -// Uncomment the following define to have the autotest generate -// addExpectedFailure() code for all the tests that fail. -// This is useful when a whole new test (sub)suite is added. -// The code is stored in addexpectedfailures.cpp. -// Paste the contents into this file after the existing -// addExpectedFailure() calls. - -//#define GENERATE_ADDEXPECTEDFAILURE_CODE - -static QString readFile(const QString &filename) -{ - QFile file(filename); - if (!file.open(QFile::ReadOnly)) - return QString(); - QTextStream stream(&file); - return stream.readAll(); -} - -static void appendCString(QVector<char> *v, const char *s) -{ - char c; - do { - c = *(s++); - *v << c; - } while (c != '\0'); -} - struct TestRecord { TestRecord() : lineNumber(-1) { } @@ -120,17 +94,18 @@ struct FailureItem QString message; }; -class tst_Suite : public QObject +class tst_QScriptJSTestSuite : public AbstractTestSuite { public: - tst_Suite(); - virtual ~tst_Suite(); + tst_QScriptJSTestSuite(); + virtual ~tst_QScriptJSTestSuite(); - static QMetaObject staticMetaObject; - virtual const QMetaObject *metaObject() const; - virtual void *qt_metacast(const char *); - virtual int qt_metacall(QMetaObject::Call, int, void **argv); +protected: + virtual void configData(TestConfig::Mode mode, const QStringList &parts); + virtual void writeSkipConfigFile(QTextStream &); + virtual void writeExpectFailConfigFile(QTextStream &); + virtual void runTestFunction(int testIndex); private: void addExpectedFailure(const QString &fileName, const QString &description, const QString &message); @@ -143,33 +118,11 @@ private: void addFileExclusion(const QRegExp &rx, const QString &message); bool isExcludedFile(const QString &fileName, QString *message) const; - QDir testsDir; QList<QString> subSuitePaths; QList<FailureItem> expectedFailures; QList<QPair<QRegExp, QString> > fileExclusions; -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - QString generatedAddExpectedFailureCode; -#endif }; -QMetaObject tst_Suite::staticMetaObject; - -Q_GLOBAL_STATIC(QVector<uint>, qt_meta_data_tst_Suite) -Q_GLOBAL_STATIC(QVector<char>, qt_meta_stringdata_tst_Suite) - -const QMetaObject *tst_Suite::metaObject() const -{ - return &staticMetaObject; -} - -void *tst_Suite::qt_metacast(const char *_clname) -{ - if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_tst_Suite()->constData())) - return static_cast<void*>(const_cast<tst_Suite*>(this)); - return QObject::qt_metacast(_clname); -} - static QScriptValue qscript_void(QScriptContext *, QScriptEngine *eng) { return eng->undefinedValue(); @@ -222,625 +175,245 @@ static QScriptValue qscript_TestCase(QScriptContext *ctx, QScriptEngine *eng) return ret; } -int tst_Suite::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +void tst_QScriptJSTestSuite::runTestFunction(int testIndex) { - _id = QObject::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - if (!(_id & 1)) { - // data - QTest::addColumn<TestRecord>("record"); - bool hasData = false; - - QString testsShellPath = testsDir.absoluteFilePath("shell.js"); - QString testsShellContents = readFile(testsShellPath); - - QDir subSuiteDir(subSuitePaths.at(_id / 2)); - QString subSuiteShellPath = subSuiteDir.absoluteFilePath("shell.js"); - QString subSuiteShellContents = readFile(subSuiteShellPath); - - QDir testSuiteDir(subSuiteDir); - testSuiteDir.cdUp(); - QString suiteJsrefPath = testSuiteDir.absoluteFilePath("jsref.js"); - QString suiteJsrefContents = readFile(suiteJsrefPath); - QString suiteShellPath = testSuiteDir.absoluteFilePath("shell.js"); - QString suiteShellContents = readFile(suiteShellPath); - - QFileInfoList testFileInfos = subSuiteDir.entryInfoList(QStringList() << "*.js", QDir::Files); - foreach (QFileInfo tfi, testFileInfos) { - if ((tfi.fileName() == "shell.js") || (tfi.fileName() == "browser.js")) - continue; - - QString abspath = tfi.absoluteFilePath(); - QString relpath = testsDir.relativeFilePath(abspath); - QString excludeMessage; - if (isExcludedFile(relpath, &excludeMessage)) { - QTest::newRow(relpath.toLatin1()) << TestRecord(excludeMessage, relpath); - continue; - } + if (!(testIndex & 1)) { + // data + QTest::addColumn<TestRecord>("record"); + bool hasData = false; + + QString testsShellPath = testsDir.absoluteFilePath("shell.js"); + QString testsShellContents = readFile(testsShellPath); + + QDir subSuiteDir(subSuitePaths.at(testIndex / 2)); + QString subSuiteShellPath = subSuiteDir.absoluteFilePath("shell.js"); + QString subSuiteShellContents = readFile(subSuiteShellPath); + + QDir testSuiteDir(subSuiteDir); + testSuiteDir.cdUp(); + QString suiteJsrefPath = testSuiteDir.absoluteFilePath("jsref.js"); + QString suiteJsrefContents = readFile(suiteJsrefPath); + QString suiteShellPath = testSuiteDir.absoluteFilePath("shell.js"); + QString suiteShellContents = readFile(suiteShellPath); + + QFileInfoList testFileInfos = subSuiteDir.entryInfoList(QStringList() << "*.js", QDir::Files); + foreach (QFileInfo tfi, testFileInfos) { + if ((tfi.fileName() == "shell.js") || (tfi.fileName() == "browser.js")) + continue; + + QString abspath = tfi.absoluteFilePath(); + QString relpath = testsDir.relativeFilePath(abspath); + QString excludeMessage; + if (isExcludedFile(relpath, &excludeMessage)) { + QTest::newRow(relpath.toLatin1()) << TestRecord(excludeMessage, relpath); + continue; + } - QScriptEngine eng; - QScriptValue global = eng.globalObject(); - global.setProperty("print", eng.newFunction(qscript_void)); - global.setProperty("quit", eng.newFunction(qscript_quit)); - global.setProperty("options", eng.newFunction(qscript_options)); - - eng.evaluate(testsShellContents, testsShellPath); - if (eng.hasUncaughtException()) { - QStringList bt = eng.uncaughtExceptionBacktrace(); - QString err = eng.uncaughtException().toString(); - qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); - break; - } + QScriptEngine eng; + QScriptValue global = eng.globalObject(); + global.setProperty("print", eng.newFunction(qscript_void)); + global.setProperty("quit", eng.newFunction(qscript_quit)); + global.setProperty("options", eng.newFunction(qscript_options)); + + eng.evaluate(testsShellContents, testsShellPath); + if (eng.hasUncaughtException()) { + QStringList bt = eng.uncaughtExceptionBacktrace(); + QString err = eng.uncaughtException().toString(); + qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); + break; + } - eng.evaluate(suiteJsrefContents, suiteJsrefPath); - if (eng.hasUncaughtException()) { - QStringList bt = eng.uncaughtExceptionBacktrace(); - QString err = eng.uncaughtException().toString(); - qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); - break; - } + eng.evaluate(suiteJsrefContents, suiteJsrefPath); + if (eng.hasUncaughtException()) { + QStringList bt = eng.uncaughtExceptionBacktrace(); + QString err = eng.uncaughtException().toString(); + qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); + break; + } - eng.evaluate(suiteShellContents, suiteShellPath); - if (eng.hasUncaughtException()) { - QStringList bt = eng.uncaughtExceptionBacktrace(); - QString err = eng.uncaughtException().toString(); - qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); - break; - } + eng.evaluate(suiteShellContents, suiteShellPath); + if (eng.hasUncaughtException()) { + QStringList bt = eng.uncaughtExceptionBacktrace(); + QString err = eng.uncaughtException().toString(); + qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); + break; + } - eng.evaluate(subSuiteShellContents, subSuiteShellPath); - if (eng.hasUncaughtException()) { - QStringList bt = eng.uncaughtExceptionBacktrace(); - QString err = eng.uncaughtException().toString(); - qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); - break; - } + eng.evaluate(subSuiteShellContents, subSuiteShellPath); + if (eng.hasUncaughtException()) { + QStringList bt = eng.uncaughtExceptionBacktrace(); + QString err = eng.uncaughtException().toString(); + qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); + break; + } - QScriptValue origTestCaseCtor = global.property("TestCase"); - QScriptValue myTestCaseCtor = eng.newFunction(qscript_TestCase); - myTestCaseCtor.setData(origTestCaseCtor); - global.setProperty("TestCase", myTestCaseCtor); + QScriptValue origTestCaseCtor = global.property("TestCase"); + QScriptValue myTestCaseCtor = eng.newFunction(qscript_TestCase); + myTestCaseCtor.setData(origTestCaseCtor); + global.setProperty("TestCase", myTestCaseCtor); - global.setProperty("gTestfile", tfi.fileName()); - global.setProperty("gTestsuite", testSuiteDir.dirName()); - global.setProperty("gTestsubsuite", subSuiteDir.dirName()); - QString testFileContents = readFile(abspath); + global.setProperty("gTestfile", tfi.fileName()); + global.setProperty("gTestsuite", testSuiteDir.dirName()); + global.setProperty("gTestsubsuite", subSuiteDir.dirName()); + QString testFileContents = readFile(abspath); // qDebug() << relpath; - eng.evaluate(testFileContents, abspath); - if (eng.hasUncaughtException() && !relpath.endsWith("-n.js")) { - QStringList bt = eng.uncaughtExceptionBacktrace(); - QString err = eng.uncaughtException().toString(); - qWarning("%s\n%s\n", qPrintable(err), qPrintable(bt.join("\n"))); - continue; - } + eng.evaluate(testFileContents, abspath); + if (eng.hasUncaughtException() && !relpath.endsWith("-n.js")) { + QStringList bt = eng.uncaughtExceptionBacktrace(); + QString err = eng.uncaughtException().toString(); + qWarning("%s\n%s\n", qPrintable(err), qPrintable(bt.join("\n"))); + continue; + } - QScriptValue testcases = global.property("testcases"); - if (!testcases.isArray()) - testcases = global.property("gTestcases"); - int count = testcases.property("length").toInt32(); - if (count == 0) - continue; - - hasData = true; - QString title = global.property("TITLE").toString(); - for (int i = 0; i < count; ++i) { - QScriptValue kase = testcases.property(i); - QString description = kase.property("description").toString(); - QScriptValue expect = kase.property("expect"); - QScriptValue actual = kase.property("actual"); - bool passed = kase.property("passed").toBoolean(); - int lineNumber = kase.property("__lineNumber__").toInt32(); - - TestRecord rec(description, passed, - actual.toString(), expect.toString(), - relpath, lineNumber); - - QTest::newRow(description.toLatin1()) << rec; - } + QScriptValue testcases = global.property("testcases"); + if (!testcases.isArray()) + testcases = global.property("gTestcases"); + int count = testcases.property("length").toInt32(); + if (count == 0) + continue; + + hasData = true; + QString title = global.property("TITLE").toString(); + for (int i = 0; i < count; ++i) { + QScriptValue kase = testcases.property(i); + QString description = kase.property("description").toString(); + QScriptValue expect = kase.property("expect"); + QScriptValue actual = kase.property("actual"); + bool passed = kase.property("passed").toBoolean(); + int lineNumber = kase.property("__lineNumber__").toInt32(); + + TestRecord rec(description, passed, + actual.toString(), expect.toString(), + relpath, lineNumber); + + QTest::newRow(description.toLatin1()) << rec; } - if (!hasData) - QTest::newRow("") << TestRecord(); // dummy + } + if (!hasData) + QTest::newRow("") << TestRecord(); // dummy + } else { + QFETCH(TestRecord, record); + if ((record.lineNumber == -1) && (record.actual == "QSKIP")) { + QTest::qSkip(record.description.toLatin1(), QTest::SkipAll, record.fileName.toLatin1(), -1); } else { - QFETCH(TestRecord, record); - if ((record.lineNumber == -1) && (record.actual == "QSKIP")) { - QTest::qSkip(record.description.toLatin1(), QTest::SkipAll, record.fileName.toLatin1(), -1); - } else { - QString msg; - FailureItem::Action failAct; - bool expectFail = isExpectedFailure(record.fileName, record.description, &msg, &failAct); - if (expectFail) { - switch (failAct) { - case FailureItem::ExpectFail: - QTest::qExpectFail("", msg.toLatin1(), - QTest::Continue, record.fileName.toLatin1(), - record.lineNumber); - break; - case FailureItem::Skip: - QTest::qSkip(msg.toLatin1(), QTest::SkipSingle, - record.fileName.toLatin1(), record.lineNumber); - break; - } + QString msg; + FailureItem::Action failAct; + bool expectFail = isExpectedFailure(record.fileName, record.description, &msg, &failAct); + if (expectFail) { + switch (failAct) { + case FailureItem::ExpectFail: + QTest::qExpectFail("", msg.toLatin1(), + QTest::Continue, record.fileName.toLatin1(), + record.lineNumber); + break; + case FailureItem::Skip: + QTest::qSkip(msg.toLatin1(), QTest::SkipSingle, + record.fileName.toLatin1(), record.lineNumber); + break; } - if (!expectFail || (failAct == FailureItem::ExpectFail)) { - if (!record.passed) { -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - if (!expectFail) { - QString escapedDescription = record.description; - escapedDescription.replace("\\", "\\\\"); - escapedDescription.replace("\n", "\\n"); - escapedDescription.replace("\"", "\\\""); - generatedAddExpectedFailureCode.append( - " addExpectedFailure(\"" + record.fileName - + "\", \"" + escapedDescription + - "\", willFixInNextReleaseMessage);\n"); - } -#endif - QTest::qCompare(record.actual, record.expected, "actual", "expect", - record.fileName.toLatin1(), record.lineNumber); - } else { - QTest::qCompare(record.actual, record.actual, "actual", "expect", - record.fileName.toLatin1(), record.lineNumber); + } + if (!expectFail || (failAct == FailureItem::ExpectFail)) { + if (!record.passed) { + if (!expectFail && shouldGenerateExpectedFailures) { + addExpectedFailure(record.fileName, + record.description, + QString()); } + QTest::qCompare(record.actual, record.expected, "actual", "expect", + record.fileName.toLatin1(), record.lineNumber); + } else { + QTest::qCompare(record.actual, record.actual, "actual", "expect", + record.fileName.toLatin1(), record.lineNumber); } } } - _id -= subSuitePaths.size()*2; } - return _id; } -tst_Suite::tst_Suite() +tst_QScriptJSTestSuite::tst_QScriptJSTestSuite() + : AbstractTestSuite("tst_QScriptJsTestSuite", + QString::fromLatin1("%0/tests").arg(SRCDIR), + ":/") { - testsDir = QDir(SRCDIR); - bool testsFound = testsDir.cd("tests"); - if (!testsFound) { - qWarning("*** no tests/ dir!"); - } - - QString willFixInNextReleaseMessage = QString::fromLatin1("Will fix in next release"); - QString fromCharCodeMessage = QString::fromLatin1("Test is wrong?"); - for (int i = 4256; i < 4294; ++i) { - addExpectedFailure("ecma/String/15.5.4.11-2.js", QString::fromLatin1("var s = new String( String.fromCharCode(%0) ); s.toLowerCase().charCodeAt(0)").arg(i), fromCharCodeMessage); - } - addExpectedFailure("ecma/String/15.5.4.11-5.js", "var s = new String( String.fromCharCode(1024) ); s.toLowerCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.11-5.js", "var s = new String( String.fromCharCode(1037) ); s.toLowerCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.12-1.js", "var s = new String( String.fromCharCode(181) ); s.toUpperCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.12-1.js", "var s = new String( String.fromCharCode(329) ); s.toUpperCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.12-4.js", "var s = new String( String.fromCharCode(1104) ); s.toUpperCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.12-4.js", "var s = new String( String.fromCharCode(1117) ); s.toUpperCase().charCodeAt(0)", fromCharCodeMessage); - addExpectedFailure("ecma/String/15.5.4.12-5.js", "var s = new String( String.fromCharCode(1415) ); s.toUpperCase().charCodeAt(0)", fromCharCodeMessage); - - addExpectedFailure("ecma/TypeConversion/9.3.1-3.js", "- \"-0x123456789abcde8\"", willFixInNextReleaseMessage); - - addExpectedFailure("ecma/extensions/15.1.2.1-1.js", "var PROPS = ''; for ( p in eval ) { PROPS += p }; PROPS", willFixInNextReleaseMessage); - addExpectedFailure("ecma/GlobalObject/15.1.2.2-1.js", "var PROPS=''; for ( var p in parseInt ) { PROPS += p; }; PROPS", willFixInNextReleaseMessage); - - addExpectedFailure("ecma/GlobalObject/15.1.2.3-1.js", "var MYPROPS=''; for ( var p in parseFloat ) { MYPROPS += p }; MYPROPS", willFixInNextReleaseMessage); - addExpectedFailure("ecma/GlobalObject/15.1.2.4.js", "var MYPROPS=''; for ( var p in escape ) { MYPROPS+= p}; MYPROPS", willFixInNextReleaseMessage); - addExpectedFailure("ecma/GlobalObject/15.1.2.5-1.js", "var MYPROPS=''; for ( var p in unescape ) { MYPROPS+= p }; MYPROPS", willFixInNextReleaseMessage); - addExpectedFailure("ecma/GlobalObject/15.1.2.6.js", "var MYPROPS=''; for ( var p in isNaN ) { MYPROPS+= p }; MYPROPS", willFixInNextReleaseMessage); - addExpectedFailure("ecma/GlobalObject/15.1.2.7.js", "var MYPROPS=''; for ( p in isFinite ) { MYPROPS+= p }; MYPROPS", willFixInNextReleaseMessage); - - addExpectedFailure(QRegExp(), "NO TESTS EXIST", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/Array/15.4.5.1-01.js", "15.4.5.1 - array.length coverage", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/extensions/regress-228087-002.js", - "Section 1 of test - \nregexp = /{1.*}/g\n" - "string = 'foo {1} foo {2} foo'\n" - "ERROR !!! match arrays have different lengths:\n" - "Expect: [\"{1} foo {2}\"]\n" - "Actual: []", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", - "Section 1 of test - \n" - "regexp = /a|ab/\n" - "string = 'abc'\n" - "ERROR !!! regexp failed to give expected match array:\n" - "Expect: [\"a\"]\n" - "Actual: [\"ab\"]\n", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", - "Section 2 of test - \n" - "regexp = /((a)|(ab))((c)|(bc))/\n" - "string = 'abc'\n" - "ERROR !!! regexp failed to give expected match array:\n" - "Expect: [\"abc\", \"a\", \"a\", , \"bc\", , \"bc\"]\n" - "Actual: [\"abc\", \"ab\", \"\", \"ab\", \"c\", \"c\", \"\"]\n", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", - "Section 4 of test - \n" - "regexp = /a[a-z]{2,4}?/\n" - "string = 'abcdefghi'\n" - "ERROR !!! regexp FAILED to match anything !!!\n" - "Expect: abc\n" - "Actual: null\n", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js(317)", - "Section 5 of test - \n" - "regexp = /(aa|aabaac|ba|b|c)*/\n" - "string = 'aabaac'\n" - "ERROR !!! regexp failed to give expected match array:\n" - "Expect: [\"aaba\", \"ba\"]\n" - "Actual: [\"aabaac\", \"aabaac\"]\n", willFixInNextReleaseMessage); - - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 1 of test - \nregexp = /{1.*}/g\nstring = 'foo {1} foo {2} foo'\nERROR !!! match arrays have different lengths:\nExpect: [\"{1} foo {2}\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 2 of test - \nregexp = /{1.*}/g\nstring = 'foo {1} foo {2} foo'\nERROR !!! match arrays have different lengths:\nExpect: [\"{1} foo {2}\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 3 of test - \nregexp = /{1[.!}]*}/g\nstring = 'foo {1} foo {2} foo'\nERROR !!! match arrays have different lengths:\nExpect: [\"{1}\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 4 of test - \nregexp = /{1[.!}]*}/g\nstring = 'foo {1} foo {2} foo'\nERROR !!! match arrays have different lengths:\nExpect: [\"{1}\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 6 of test - \nregexp = /c{3 }/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 7 of test - \nregexp = /c{3.}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 8 of test - \nregexp = /c{3\\s}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 9 of test - \nregexp = /c{3[ ]}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 10 of test - \nregexp = /c{ 3}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{ 3}\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 12 of test - \nregexp = /c{3, }/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3, }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 13 of test - \nregexp = /c{3 ,}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 ,}\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 15 of test - \nregexp = /c{3 ,4}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3 ,4}\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 16 of test - \nregexp = /c{3, 4}/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3, 4}\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-228087.js", "Section 17 of test - \nregexp = /c{3,4 }/\nstring = 'abccccc{3 }c{ 3}c{3, }c{3 ,}c{3 ,4}c{3, 4}c{3,4 }de'\nERROR !!! regexp FAILED to match anything !!!\nExpect: c{3,4 }\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 0", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 1", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 2", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 3", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 4", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 5", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 6", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 7", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-274152.js", "Do not ignore unicode format-control characters: 8", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-368516.js", "Treat unicode BOM characters as whitespace: 0", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/extensions/regress-368516.js", "Treat unicode BOM characters as whitespace: 1", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/FunExpr/fe-001-n.js", "Previous statement should have thrown a ReferenceError", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/LexicalConventions/7.9.1.js", "Automatic Semicolon insertion in postfix expressions: expr\n++", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/LexicalConventions/7.9.1.js", "Automatic Semicolon insertion in postfix expressions: expr\n--", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/LexicalConventions/7.9.1.js", "Automatic Semicolon insertion in postfix expressions: (x\n)-- y", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/LexicalConventions/7.9.1.js", "Automatic Semicolon insertion in postfix expressions: (x)-- y", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Object/8.6.1-01.js", "In strict mode, setting a read-only property should generate a warning: Throw if STRICT and WERROR is enabled", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.8.2 >", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.8.4 >=", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 5 of test - \nregexp = /(aa|aabaac|ba|b|c)*/\nstring = 'aabaac'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aaba\", \"ba\"]\nActual: [\"aabaac\", \"aabaac\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 6 of test - \nregexp = /^(a+)\\1*,\\1+$/\nstring = 'aaaaaaaaaa,aaaaaaaaaaaaaaa'\nERROR !!! regexp FAILED to match anything !!!\nExpect: aaaaaaaaaa,aaaaaaaaaaaaaaa,aaaaa\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 7 of test - \nregexp = /(z)((a+)?(b+)?(c))*/\nstring = 'zaacbbbcac'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"zaacbbbcac\", \"z\", \"ac\", \"a\", , \"c\"]\nActual: [\"zaacbbbcac\", \"z\", \"ac\", \"a\", \"\", \"c\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 8 of test - \nregexp = /(a*)*/\nstring = 'b'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\", , ]\nActual: [\"\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 10 of test - \nregexp = /(?=(a+))/\nstring = 'baaabac'\nERROR !!! match arrays have different lengths:\nExpect: [\"\", \"aaa\"]\nActual: [\"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 11 of test - \nregexp = /(?=(a+))a*b\\1/\nstring = 'baaabac'\nERROR !!! match arrays have different lengths:\nExpect: [\"aba\", \"a\"]\nActual: [\"aaab\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 12 of test - \nregexp = /(.*?)a(?!(a+)b\\2c)\\2(.*)/\nstring = 'baaabaac'\nERROR !!! regexp FAILED to match anything !!!\nExpect: baaabaac,ba,,abaac\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 13 of test - \nregexp = /(?=(a+))/\nstring = 'baaabac'\nERROR !!! match arrays have different lengths:\nExpect: [\"\", \"aaa\"]\nActual: [\"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 34 of test - \nregexp = /a]/\nstring = 'a]'\nERROR !!! regexp FAILED to match anything !!!\nExpect: a]\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 66 of test - \nregexp = /a.+?c/\nstring = 'abcabc'\nERROR !!! regexp FAILED to match anything !!!\nExpect: abc\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 94 of test - \nregexp = /^a(bc+|b[eh])g|.h$/\nstring = 'abh'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"bh\", , ]\nActual: [\"bh\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 95 of test - \nregexp = /(bc+d$|ef*g.|h?i(j|k))/\nstring = 'effgz'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"effgz\", \"effgz\", , ]\nActual: [\"effgz\", \"effgz\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 97 of test - \nregexp = /(bc+d$|ef*g.|h?i(j|k))/\nstring = 'reffgz'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"effgz\", \"effgz\", , ]\nActual: [\"effgz\", \"effgz\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 109 of test - \nregexp = /(([a-c])b*?\\2)*/\nstring = 'ababbbcbc'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ababb,bb,b\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 110 of test - \nregexp = /(([a-c])b*?\\2){3}/\nstring = 'ababbbcbc'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ababbbcbc,cbc,c\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 119 of test - \nregexp = /ab*?bc/i\nstring = 'ABBBBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBBBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 120 of test - \nregexp = /ab{0,}?bc/i\nstring = 'ABBBBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBBBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 121 of test - \nregexp = /ab+?bc/i\nstring = 'ABBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 123 of test - \nregexp = /ab{1,}?bc/i\nstring = 'ABBBBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBBBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 124 of test - \nregexp = /ab{1,3}?bc/i\nstring = 'ABBBBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBBBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 125 of test - \nregexp = /ab{3,4}?bc/i\nstring = 'ABBBBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBBBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 126 of test - \nregexp = /ab??bc/i\nstring = 'ABBC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABBC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 127 of test - \nregexp = /ab??bc/i\nstring = 'ABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 128 of test - \nregexp = /ab{0,1}?bc/i\nstring = 'ABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 129 of test - \nregexp = /ab??c/i\nstring = 'ABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 130 of test - \nregexp = /ab{0,1}?c/i\nstring = 'ABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 138 of test - \nregexp = /a.*?c/i\nstring = 'AXYZC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: AXYZC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 144 of test - \nregexp = /a]/i\nstring = 'A]'\nERROR !!! regexp FAILED to match anything !!!\nExpect: A]\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 160 of test - \nregexp = /a.+?c/i\nstring = 'ABCABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 161 of test - \nregexp = /a.*?c/i\nstring = 'ABCABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 162 of test - \nregexp = /a.{0,5}?c/i\nstring = 'ABCABC'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ABC\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 169 of test - \nregexp = /(a+|b){0,1}?/i\nstring = 'AB'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ,\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 191 of test - \nregexp = /^a(bc+|b[eh])g|.h$/i\nstring = 'ABH'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"BH\", , ]\nActual: [\"BH\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 192 of test - \nregexp = /(bc+d$|ef*g.|h?i(j|k))/i\nstring = 'EFFGZ'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"EFFGZ\", \"EFFGZ\", , ]\nActual: [\"EFFGZ\", \"EFFGZ\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 194 of test - \nregexp = /(bc+d$|ef*g.|h?i(j|k))/i\nstring = 'REFFGZ'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"EFFGZ\", \"EFFGZ\", , ]\nActual: [\"EFFGZ\", \"EFFGZ\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 212 of test - \nregexp = /a(?:b|c|d)+?(.)/\nstring = 'ace'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ace,e\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 213 of test - \nregexp = /a(?:b|c|d)+?(.)/\nstring = 'acdbcdbe'\nERROR !!! regexp FAILED to match anything !!!\nExpect: acd,d\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 217 of test - \nregexp = /a(?:b|c|d){4,5}?(.)/\nstring = 'acdbcdbe'\nERROR !!! regexp FAILED to match anything !!!\nExpect: acdbcd,d\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 218 of test - \nregexp = /((foo)|(bar))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"foobar\", \"bar\", , \"bar\"]\nActual: [\"foobar\", \"bar\", \"\", \"bar\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 220 of test - \nregexp = /a(?:b|c|d){6,7}?(.)/\nstring = 'acdbcdbe'\nERROR !!! regexp FAILED to match anything !!!\nExpect: acdbcdbe,e\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 222 of test - \nregexp = /a(?:b|c|d){5,6}?(.)/\nstring = 'acdbcdbe'\nERROR !!! regexp FAILED to match anything !!!\nExpect: acdbcdb,b\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 224 of test - \nregexp = /a(?:b|c|d){5,7}?(.)/\nstring = 'acdbcdbe'\nERROR !!! regexp FAILED to match anything !!!\nExpect: acdbcdb,b\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 225 of test - \nregexp = /a(?:b|(c|e){1,2}?|d)+?(.)/\nstring = 'ace'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ace,c,e\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 227 of test - \nregexp = /^([^a-z])|(\\^)$/\nstring = '.'\nERROR !!! regexp failed to give expected match array:\nExpect: [\".\", \".\", , ]\nActual: [\".\", \".\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 234 of test - \nregexp = /(?:(f)(o)(o)|(b)(a)(r))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"foobar\", , , , \"b\", \"a\", \"r\"]\nActual: [\"foobar\", \"\", \"\", \"\", \"b\", \"a\", \"r\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 240 of test - \nregexp = /(?:..)*?a/\nstring = 'aba'\nERROR !!! regexp FAILED to match anything !!!\nExpect: a\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 241 of test - \nregexp = /^(?:b|a(?=(.)))*\\1/\nstring = 'abc'\nERROR !!! match arrays have different lengths:\nExpect: [\"ab\", , ]\nActual: [\"ab\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 246 of test - \nregexp = /(a|x)*ab/\nstring = 'cab'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"ab\", , ]\nActual: [\"ab\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 247 of test - \nregexp = /(a)*ab/\nstring = 'cab'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"ab\", , ]\nActual: [\"ab\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 300 of test - \nregexp = /(?=(a+?))(\\1ab)/\nstring = 'aaab'\nERROR !!! regexp FAILED to match anything !!!\nExpect: aab,a,aab\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 303 of test - \nregexp = /(?=(a+?))(\\1ab)/\nstring = 'aaab'\nERROR !!! regexp FAILED to match anything !!!\nExpect: aab,a,aab\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 304 of test - \nregexp = /([\\w:]+::)?(\\w+)$/\nstring = 'abcd'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"abcd\", , \"abcd\"]\nActual: [\"abcd\", \"\", \"abcd\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 308 of test - \nregexp = /([\\w:]+::)?(\\w+)$/\nstring = 'abcd'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"abcd\", , \"abcd\"]\nActual: [\"abcd\", \"\", \"abcd\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 342 of test - \nregexp = /a$/m\nstring = 'a\\nb\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: a\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 344 of test - \nregexp = /a$/m\nstring = 'b\\na\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: a\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 353 of test - \nregexp = /aa$/m\nstring = 'aa\\nb\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: aa\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 355 of test - \nregexp = /aa$/m\nstring = 'b\\naa\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: aa\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 364 of test - \nregexp = /ab$/m\nstring = 'ab\\nb\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ab\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 366 of test - \nregexp = /ab$/m\nstring = 'b\\nab\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: ab\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 375 of test - \nregexp = /abb$/m\nstring = 'abb\\nb\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: abb\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 377 of test - \nregexp = /abb$/m\nstring = 'b\\nabb\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: abb\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 383 of test - \nregexp = /^d[x][x][x]/m\nstring = 'abcd\\ndxxx'\nERROR !!! regexp FAILED to match anything !!!\nExpect: dxxx\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 391 of test - \nregexp = /\\.c(pp|xx|c)?$/i\nstring = 'IO.c'\nERROR !!! regexp failed to give expected match array:\nExpect: [\".c\", , ]\nActual: [\".c\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 392 of test - \nregexp = /(\\.c(pp|xx|c)?$)/i\nstring = 'IO.c'\nERROR !!! regexp failed to give expected match array:\nExpect: [\".c\", \".c\", , ]\nActual: [\".c\", \".c\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 394 of test - \nregexp = /^([ab]*?)(b)?(c)$/\nstring = 'abac'\nERROR !!! regexp FAILED to match anything !!!\nExpect: abac,aba,,c\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 412 of test - \nregexp = /^(a(b)?)+$/\nstring = 'aba'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aba\", \"a\", , ]\nActual: [\"aba\", \"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 413 of test - \nregexp = /^(aa(bb)?)+$/\nstring = 'aabbaa'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aabbaa\", \"aa\", , ]\nActual: [\"aabbaa\", \"aa\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 414 of test - \nregexp = /^.{9}abc.*\\n/m\nstring = '123\\nabcabcabcabc\\n'\nERROR !!! regexp FAILED to match anything !!!\nExpect: abcabcabcabc\n\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 415 of test - \nregexp = /^(a)?a$/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", , ]\nActual: [\"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 416 of test - \nregexp = /^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$/\nstring = 'aaaaaa'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aaaaaa\", \"a\", \"aa\", \"a\", \"aa\"]\nActual: [\"aaaaaa\", \"aa\", \"a\", \"aa\", \"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 418 of test - \nregexp = /^(0+)?(?:x(1))?/\nstring = 'x1'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"x1\", , \"1\"]\nActual: [\"x1\", \"\", \"1\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 419 of test - \nregexp = /^([0-9a-fA-F]+)(?:x([0-9a-fA-F]+)?)(?:x([0-9a-fA-F]+))?/\nstring = '012cxx0190'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"012cxx0190\", \"012c\", , \"0190\"]\nActual: [\"012cxx0190\", \"012c\", \"\", \"0190\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 420 of test - \nregexp = /^(b+?|a){1,2}c/\nstring = 'bbbac'\nERROR !!! regexp FAILED to match anything !!!\nExpect: bbbac,a\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 421 of test - \nregexp = /^(b+?|a){1,2}c/\nstring = 'bbbbac'\nERROR !!! regexp FAILED to match anything !!!\nExpect: bbbbac,a\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-002.js", "Section 40 of test - \nregexp = /(a)|\\1/\nstring = 'x'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\", , ]\nActual: [\"\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-105972.js", "Section 1 of test - \nregexp = /^.*?$/\nstring = 'Hello World'\nERROR !!! regexp FAILED to match anything !!!\nExpect: Hello World\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-105972.js", "Section 2 of test - \nregexp = /^.*?/\nstring = 'Hello World'\nERROR !!! regexp FAILED to match anything !!!\nExpect: \nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-105972.js", "Section 3 of test - \nregexp = /^.*?(:|$)/\nstring = 'Hello: World'\nERROR !!! regexp FAILED to match anything !!!\nExpect: Hello:,:\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-123437.js", "Section 1 of test - \nregexp = /(a)?a/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", , ]\nActual: [\"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-123437.js", "Section 2 of test - \nregexp = /a|(b)/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", , ]\nActual: [\"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-123437.js", "Section 3 of test - \nregexp = /(a)?(a)/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", , \"a\"]\nActual: [\"a\", \"\", \"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-165353.js", "Section 1 of test - \nregexp = /^([a-z]+)*[a-z]$/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", , ]\nActual: [\"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-169497.js", "Section 1 of test - \nregexp = /<body.*>((.*\\n?)*?)<\\/body>/i\nstring = '<html>\\n<body onXXX=\"alert(event.type);\">\\n<p>Kibology for all</p>\\n<p>All for Kibology</p>\\n</body>\\n</html>'\nERROR !!! regexp FAILED to match anything !!!\nExpect: <body onXXX=\"alert(event.type);\">\n<p>Kibology for all</p>\n<p>All for Kibology</p>\n</body>,\n<p>Kibology for all</p>\n<p>All for Kibology</p>\n,<p>All for Kibology</p>\n\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-187133.js", "Section 5 of test - \nregexp = /(?!a|b)|c/\nstring = 'bc'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\"]\nActual: [\"c\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 1 of test - \nregexp = /(\\d|\\d\\s){2,}/\nstring = '12 3 45'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"12\", \"2\"]\nActual: [\"12 3 45\", \"5\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 3 of test - \nregexp = /(\\d|\\d\\s)+/\nstring = '12 3 45'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"12\", \"2\"]\nActual: [\"12 3 45\", \"5\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 8 of test - \nregexp = /(\\d|\\d\\s){2,}?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 12,2\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 9 of test - \nregexp = /(\\d|\\d\\s){4,}?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 12 3 4,4\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 10 of test - \nregexp = /(\\d|\\d\\s)+?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 1,1\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 11 of test - \nregexp = /(\\d\\s?){4,}?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 12 3 4,4\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 12 of test - \nregexp = /(\\d\\s|\\d){2,}?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 12 ,2 \nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 13 of test - \nregexp = /(\\d\\s|\\d){4,}?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 12 3 4,4\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-191479.js", "Section 14 of test - \nregexp = /(\\d\\s|\\d)+?/\nstring = '12 3 45'\nERROR !!! regexp FAILED to match anything !!!\nExpect: 1,1\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-202564.js", "Section 1 of test - \nregexp = /(?:(.+), )?(.+), (..) to (?:(.+), )?(.+), (..)/\nstring = 'Seattle, WA to Buckley, WA'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"Seattle, WA to Buckley, WA\", , \"Seattle\", \"WA\", , \"Buckley\", \"WA\"]\nActual: [\"Seattle, WA to Buckley, WA\", \"\", \"Seattle\", \"WA\", \"\", \"Buckley\", \"WA\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 2 of test - \nregexp = /(a|b*){5,}/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", \"\"]\nActual: [\"a\", \"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 3 of test - \nregexp = /(b*)*/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\", , ]\nActual: [\"\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 5 of test - \nregexp = /^\\-?(\\d{1,}|\\.{0,})*(\\,\\d{1,})?$/\nstring = '100.00'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"100.00\", \"00\", , ]\nActual: [\"100.00\", \"00\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-216591.js", "Section 1 of test - \nregexp = /\\{(([a-z0-9\\-_]+?\\.)+?)([a-z0-9\\-_]+?)\\}/i\nstring = 'a {result.data.DATA} b'\nERROR !!! regexp FAILED to match anything !!!\nExpect: {result.data.DATA},result.data.,data.,DATA\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-216591.js", "Section 2 of test - \nregexp = /\\{(([a-z0-9\\-_]+?\\.)+?)([a-z0-9\\-_]+?)\\}/gi\nstring = 'a {result.data.DATA} b'\nERROR !!! match arrays have different lengths:\nExpect: [\"{result.data.DATA}\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-220367-001.js", "Section 1 of test - \nregexp = /(a)|(b)/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", \"a\", , ]\nActual: [\"a\", \"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-220367-001.js", "Section 2 of test - \nregexp = /(a)|(b)/\nstring = 'b'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"b\", , \"b\"]\nActual: [\"b\", \"\", \"b\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-223535.js", "Section 2 of test - \nregexp = /|a/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\"]\nActual: [\"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-223535.js", "Section 6 of test - \nregexp = /(|a)/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\", \"\"]\nActual: [\"a\", \"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-223535.js", "Section 7 of test - \nregexp = /(|a|)/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"\", \"\"]\nActual: [\"a\", \"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-224676.js", "Section 17 of test - \nregexp = /[x]b|(a)/\nstring = 'ZZZxbZZZ'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"xb\", , ]\nActual: [\"xb\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-224676.js", "Section 18 of test - \nregexp = /[x]b|()a/\nstring = 'ZZZxbZZZ'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"xb\", , ]\nActual: [\"xb\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-225289.js", "Section 7 of test - \nregexp = /(a)|([^a])/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", \"a\", , ]\nActual: [\"a\", \"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-225289.js", "Section 9 of test - \nregexp = /(a)|([^a])/\nstring = '()'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"(\", , \"(\"]\nActual: [\"(\", \"\", \"(\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-225289.js", "Section 10 of test - \nregexp = /((?:a|[^a])*)/g\nstring = 'a'\nERROR !!! match arrays have different lengths:\nExpect: [\"a\", \"\"]\nActual: [\"a\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-225289.js", "Section 11 of test - \nregexp = /((?:a|[^a])*)/g\nstring = ''\nERROR !!! match arrays have different lengths:\nExpect: [\"\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-225289.js", "Section 12 of test - \nregexp = /((?:a|[^a])*)/g\nstring = '()'\nERROR !!! match arrays have different lengths:\nExpect: [\"()\", \"\"]\nActual: [\"()\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-31316.js", "Section 1 of test - \nregexp = /<([^\\/<>][^<>]*[^\\/])>|<([^\\/<>])>/\nstring = '<p>Some<br />test</p>'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"<p>\", , \"p\"]\nActual: [\"<p>\", \"\", \"p\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-330684.js", "Do not hang on RegExp", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-375711.js", "Do not assert with /[Q-b]/i.exec(\"\"): /[q-b]/.exec(\"\")", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-375711.js", "Do not assert with /[Q-b]/i.exec(\"\"): /[q-b]/i.exec(\"\")", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(?)'and flag 'i'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(?)'and flag 'g'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(?)'and flag 'm'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(?)'and flag 'undefined'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(a'and flag 'i'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(a'and flag 'g'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(a'and flag 'm'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '(a'and flag 'undefined'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '( ]'and flag 'i'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '( ]'and flag 'g'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '( ]'and flag 'm'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-57631.js", "Testing for error creating illegal RegExp object on pattern '( ]'and flag 'undefined'", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-78156.js", "Section 1 of test - \nregexp = /^\\d/gm\nstring = 'aaa\\n789\\r\\nccc\\r\\n345'\nERROR !!! match arrays have different lengths:\nExpect: [\"7\", \"3\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-78156.js", "Section 2 of test - \nregexp = /\\d$/gm\nstring = 'aaa\\n789\\r\\nccc\\r\\n345'\nERROR !!! match arrays have different lengths:\nExpect: [\"9\", \"5\"]\nActual: [\"5\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-78156.js", "Section 3 of test - \nregexp = /^\\d/gm\nstring = 'aaa\\n789\\r\\nccc\\r\\nddd'\nERROR !!! match arrays have different lengths:\nExpect: [\"7\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-78156.js", "Section 4 of test - \nregexp = /\\d$/gm\nstring = 'aaa\\n789\\r\\nccc\\r\\nddd'\nERROR !!! match arrays have different lengths:\nExpect: [\"9\"]\nActual: []\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-85721.js", "Section 2 of test - \nregexp = /<sql:connection id=\"([^\\r\\n]*?)\">\\s*<sql:url>\\s*([^\\r\\n]*?)\\s*<\\/sql:url>\\s*<sql:driver>\\s*([^\\r\\n]*?)\\s*<\\/sql:driver>\\s*(\\s*<sql:userId>\\s*([^\\r\\n]*?)\\s*<\\/sql:userId>\\s*)?\\s*(\\s*<sql:password>\\s*([^\\r\\n]*?)\\s*<\\/sql:password>\\s*)?\\s*<\\/sql:connection>/\nstring = '<sql:connection id=\"conn1\"> <sql:url>www.m.com</sql:url> <sql:driver>drive.class</sql:driver>\\n<sql:userId>foo</sql:userId> <sql:password>goo</sql:password> </sql:connection>'\nERROR !!! regexp FAILED to match anything !!!\nExpect: <sql:connection id=\"conn1\"> <sql:url>www.m.com</sql:url> <sql:driver>drive.class</sql:driver>\n<sql:userId>foo</sql:userId> <sql:password>goo</sql:password> </sql:connection>,conn1,www.m.com,drive.class,<sql:userId>foo</sql:userId> ,foo,<sql:password>goo</sql:password> ,goo\nActual: null\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-87231.js", "Section 3 of test - \nregexp = /^(A)?(A.*)$/\nstring = 'A'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"A\", , \"A\"]\nActual: [\"A\", \"\", \"A\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-87231.js", "Section 6 of test - \nregexp = /(A)?(A.*)/\nstring = 'zxcasd;fl\\ ^AaaAAaaaf;lrlrzs'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"AaaAAaaaf;lrlrzs\", , \"AaaAAaaaf;lrlrzs\"]\nActual: [\"AaaAAaaaf;lrlrzs\", \"\", \"AaaAAaaaf;lrlrzs\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.11.js", "Section 24", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.11.js", "Section 28", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.11.js", "Section 30", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.14.js", "15.5.4.14 - String.prototype.split(/()/)", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Unicode/regress-352044-01.js", "issues with Unicode escape sequences in JavaScript source code", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Unicode/uc-001.js", "Unicode format-control character test (Category Cf.)", willFixInNextReleaseMessage); - - addFileExclusion(".+/15\\.9\\.2\\..+", "unstable on slow machines"); - addFileExclusion(".+/15\\.9\\.5\\..+", "too slooow"); - addFileExclusion("regress-130451.js", "asserts"); - addFileExclusion("regress-322135-01.js", "asserts"); - addFileExclusion("regress-322135-02.js", "asserts"); - addFileExclusion("regress-322135-03.js", "takes forever"); - addFileExclusion("regress-322135-04.js", "takes forever"); - addFileExclusion("ecma_3/RegExp/regress-375715-04.js", "bug"); - - addFileExclusion("ecma_3/RegExp/regress-289669.js", "Can fail due to relying on wall-clock time"); - - // Failures due to switch to JSC as back-end - addExpectedFailure("ecma/Array/15.4.3.1-2.js", "var props = ''; for ( p in Array ) { props += p } props", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Boolean/15.6.3.1-1.js", "var str='';for ( p in Boolean ) { str += p } str;", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.4.1.js", "var abc; delete(abc)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/FunctionObjects/15.3.3.1-2.js", "var str='';for (prop in Function ) str += prop; str;", willFixInNextReleaseMessage); - addExpectedFailure("ecma/ObjectObjects/15.2.3.1-1.js", "var str = '';for ( p in Object ) { str += p; }; str", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Statements/12.6.3-11.js", "result = \"\"; for ( p in Number ) { result += String(p) };", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Statements/12.6.3-2.js", "Boolean.prototype.foo = 34; for ( j in Boolean ) Boolean[j]", willFixInNextReleaseMessage); - addExpectedFailure("ecma/TypeConversion/9.3.1-3.js", "-\"\\u20001234\\u2001\"", willFixInNextReleaseMessage); - addExpectedFailure("ecma_2/RegExp/properties-001.js", "//.toString()", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Date/15.9.4.3.js", "15.9.4.3 - Date.UTC edge-case arguments.: date Infinity", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Date/15.9.4.3.js", "15.9.4.3 - Date.UTC edge-case arguments.: hours Infinity", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Date/15.9.4.3.js", "15.9.4.3 - Date.UTC edge-case arguments.: minutes Infinity", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Date/15.9.4.3.js", "15.9.4.3 - Date.UTC edge-case arguments.: seconds Infinity", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Function/regress-131964.js", "Section 1 of test - ", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Function/regress-313570.js", "length of objects whose prototype chain includes a function: immutable", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/FunExpr/fe-001.js", "Both functions were defined.", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 7 of test - \nregexp = /(z)((a+)?(b+)?(c))*/\nstring = 'zaacbbbcac'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"zaacbbbcac\", \"z\", \"ac\", \"a\", , \"c\"]\nActual: [\"zaacbbbcac\", \"z\", \"ac\", \"a\", \"bbb\", \"c\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/15.10.2-1.js", "Section 12 of test - \nregexp = /(.*?)a(?!(a+)b\\2c)\\2(.*)/\nstring = 'baaabaac'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"baaabaac\", \"ba\", , \"abaac\"]\nActual: [\"baaabaac\", \"ba\", \"aa\", \"abaac\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 218 of test - \nregexp = /((foo)|(bar))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"foobar\", \"bar\", , \"bar\"]\nActual: [\"foobar\", \"bar\", \"foo\", \"bar\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 234 of test - \nregexp = /(?:(f)(o)(o)|(b)(a)(r))*/\nstring = 'foobar'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"foobar\", , , , \"b\", \"a\", \"r\"]\nActual: [\"foobar\", \"f\", \"o\", \"o\", \"b\", \"a\", \"r\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 241 of test - \nregexp = /^(?:b|a(?=(.)))*\\1/\nstring = 'abc'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"ab\", , ]\nActual: [\"ab\", \"b\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 412 of test - \nregexp = /^(a(b)?)+$/\nstring = 'aba'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aba\", \"a\", , ]\nActual: [\"aba\", \"a\", \"b\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/perlstress-001.js", "Section 413 of test - \nregexp = /^(aa(bb)?)+$/\nstring = 'aabbaa'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"aabbaa\", \"aa\", , ]\nActual: [\"aabbaa\", \"aa\", \"bb\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 1 of test - \nregexp = /(a|b*)*/\nstring = 'a'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"a\", \"a\"]\nActual: [\"a\", \"\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 5 of test - \nregexp = /^\\-?(\\d{1,}|\\.{0,})*(\\,\\d{1,})?$/\nstring = '100.00'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"100.00\", \"00\", , ]\nActual: [\"100.00\", \"\", , ]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 6 of test - \nregexp = /^\\-?(\\d{1,}|\\.{0,})*(\\,\\d{1,})?$/\nstring = '100,00'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"100,00\", \"100\", \",00\"]\nActual: [\"100,00\", \"\", \",00\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-209919.js", "Section 7 of test - \nregexp = /^\\-?(\\d{1,}|\\.{0,})*(\\,\\d{1,})?$/\nstring = '1.000,00'\nERROR !!! regexp failed to give expected match array:\nExpect: [\"1.000,00\", \"000\", \",00\"]\nActual: [\"1.000,00\", \"\", \",00\"]\n", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/RegExp/regress-311414.js", "RegExp captured tail match should be O(N) BigO 2 < 2", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.11.js", "Section 7", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/String/15.5.4.11.js", "Section 26", willFixInNextReleaseMessage); - -#ifdef Q_CC_MSVC - addExpectedFailure("ecma_3/Expressions/11.7.3-01.js", "11.7.3 - >>> should evaluate operands in order: order", "QTBUG-8056"); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.7.3 >>>", "QTBUG-8056"); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.13.2 >>>=", "QTBUG-8056"); -#endif - -#ifdef Q_CC_MINGW - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(NaN,0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(NaN,-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(Infinity, Infinity)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(Infinity, -Infinity)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(-Infinity, Infinity)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(-Infinity, -Infinity)", willFixInNextReleaseMessage); -#endif - -#ifdef Q_OS_SOLARIS - addExpectedFailure("ecma/Expressions/11.13.2-2.js", "VAR1 = -0; VAR2= Infinity; VAR2 /= VAR1", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.13.2-2.js", "VAR1 = -0; VAR2= -Infinity; VAR2 /= VAR1", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.13.2-2.js", "VAR1 = 1; VAR2= -0; VAR1 /= VAR2", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.13.2-2.js", "VAR1 = -1; VAR2= -0; VAR1 /= VAR2", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.5.2.js", "Number.POSITIVE_INFINITY / -0", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.5.2.js", "Number.NEGATIVE_INFINITY / -0", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.5.2.js", "1 / -0", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Expressions/11.5.2.js", "-1 / -0", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.10.js", "Math.log(-0.0000001)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.10.js", "Math.log(-1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.11.js", "Infinity/Math.max(-0,-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.12.js", "Infinity/Math.min(0,-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.12.js", "Infinity/Math.min(-0,-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(NaN,0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(NaN,-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Infinity/Math.pow(-Infinity, -1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(0, -1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(0, -0.5)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(0, -1000)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Infinity/Math.pow(-0, 1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Infinity/Math.pow(-0, 3)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(-0, -2)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.15.js", "Infinity/Math.round(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.15.js", "Infinity/Math.round(-0.49)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.15.js", "Infinity/Math.round(-0.5)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.17.js", "Infinity/Math.sqrt(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.18.js", "Infinity/Math.tan(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.2.js", "Math.acos(1.00000001)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.2.js", "Math.acos(11.00000001)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.3.js", "Math.asin(1.000001)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.3.js", "Math.asin(-1.000001)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.3.js", "Infinity/Math.asin(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.4.js", "Infinity/Math.atan(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(0, -0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Infinity/Math.atan2(-0, 1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(-0,\t-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.5.js", "Math.atan2(-0,\t-1)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.6.js", "Infinity/Math.ceil('-0')", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.6.js", "Infinity/Math.ceil(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.6.js", "Infinity/Math.ceil(-Number.MIN_VALUE)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.6.js", "Infinity/Math.ceil(-0.9)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.9.js", "Infinity/Math.floor(-0)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/TypeConversion/9.3.1-3.js", "var z = 0; print(1/-z)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/TypeConversion/9.3.1-3.js", "1/-1e-2000", willFixInNextReleaseMessage); -#endif - -#ifdef Q_OS_SYMBIAN - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(-1, 0.5)", willFixInNextReleaseMessage); - addExpectedFailure("ecma/Math/15.8.2.13.js", "Math.pow(-1, -0.5)", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.5.1 *", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.5.2 /", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.6.2 -", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.13.2 *=", willFixInNextReleaseMessage); - addExpectedFailure("ecma_3/Operators/order-01.js", "operator evaluation order: 11.13.2 /=", willFixInNextReleaseMessage); -#endif - - static const char klass[] = "tst_QScriptJsTestSuite"; - - QVector<uint> *data = qt_meta_data_tst_Suite(); - // content: - *data << 1 // revision - << 0 // classname - << 0 << 0 // classinfo - << 0 << 10 // methods (backpatched later) - << 0 << 0 // properties - << 0 << 0 // enums/sets - ; - - QVector<char> *stringdata = qt_meta_stringdata_tst_Suite(); - appendCString(stringdata, klass); - appendCString(stringdata, ""); - // don't execute any tests on slow machines #if !defined(Q_OS_IRIX) // do all the test suites - QFileInfoList testSuiteDirInfos; - if (testsFound) - testSuiteDirInfos = testsDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot); + QFileInfoList testSuiteDirInfos = testsDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot); foreach (QFileInfo tsdi, testSuiteDirInfos) { QDir testSuiteDir(tsdi.absoluteFilePath()); // do all the dirs in the test suite QFileInfoList subSuiteDirInfos = testSuiteDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot); foreach (QFileInfo ssdi, subSuiteDirInfos) { subSuitePaths.append(ssdi.absoluteFilePath()); - // slot: signature, parameters, type, tag, flags - QString data_slot = QString::fromLatin1("%0/%1_data()") - .arg(testSuiteDir.dirName()).arg(ssdi.fileName()); - static const int nullbyte = sizeof(klass); - *data << stringdata->size() << nullbyte << nullbyte << nullbyte << 0x08; - appendCString(stringdata, data_slot.toLatin1()); - QString slot = QString::fromLatin1("%0/%1()") - .arg(testSuiteDir.dirName()).arg(ssdi.fileName()); - *data << stringdata->size() << nullbyte << nullbyte << nullbyte << 0x08; - appendCString(stringdata, slot.toLatin1()); + QString function = QString::fromLatin1("%0/%1") + .arg(testSuiteDir.dirName()).arg(ssdi.fileName()); + addTestFunction(function, CreateDataFunction); } } #endif - (*data)[4] = subSuitePaths.size() * 2; + finalizeMetaObject(); +} + +tst_QScriptJSTestSuite::~tst_QScriptJSTestSuite() +{ +} - *data << 0; // eod +void tst_QScriptJSTestSuite::configData(TestConfig::Mode mode, const QStringList &parts) +{ + switch (mode) { + case TestConfig::Skip: + addFileExclusion(parts.at(0), parts.value(1)); + break; + + case TestConfig::ExpectFail: + addExpectedFailure(parts.at(0), parts.value(1), parts.value(2)); + break; + } +} - // initialize staticMetaObject - staticMetaObject.d.superdata = &QObject::staticMetaObject; - staticMetaObject.d.stringdata = stringdata->constData(); - staticMetaObject.d.data = data->constData(); - staticMetaObject.d.extradata = 0; +void tst_QScriptJSTestSuite::writeSkipConfigFile(QTextStream &stream) +{ + stream << QString::fromLatin1("# testcase | message") << endl; } -tst_Suite::~tst_Suite() +void tst_QScriptJSTestSuite::writeExpectFailConfigFile(QTextStream &stream) { -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - if (!generatedAddExpectedFailureCode.isEmpty()) { - QFile file("addexpectedfailures.cpp"); - file.open(QFile::WriteOnly); - QTextStream ts(&file); - ts << generatedAddExpectedFailureCode; + stream << QString::fromLatin1("# testcase | description | message") << endl; + for (int i = 0; i < expectedFailures.size(); ++i) { + const FailureItem &fail = expectedFailures.at(i); + if (fail.pathRegExp.pattern().isEmpty()) + continue; + stream << QString::fromLatin1("%0 | %1") + .arg(fail.pathRegExp.pattern()) + .arg(escape(fail.description)); + if (!fail.message.isEmpty()) + stream << QString::fromLatin1(" | %0").arg(escape(fail.message)); + stream << endl; } -#endif } -void tst_Suite::addExpectedFailure(const QRegExp &path, const QString &description, const QString &message) +void tst_QScriptJSTestSuite::addExpectedFailure(const QRegExp &path, const QString &description, const QString &message) { expectedFailures.append(FailureItem(FailureItem::ExpectFail, path, description, message)); } -void tst_Suite::addExpectedFailure(const QString &fileName, const QString &description, const QString &message) +void tst_QScriptJSTestSuite::addExpectedFailure(const QString &fileName, const QString &description, const QString &message) { expectedFailures.append(FailureItem(FailureItem::ExpectFail, QRegExp(fileName), description, message)); } -void tst_Suite::addSkip(const QRegExp &path, const QString &description, const QString &message) +void tst_QScriptJSTestSuite::addSkip(const QRegExp &path, const QString &description, const QString &message) { expectedFailures.append(FailureItem(FailureItem::Skip, path, description, message)); } -void tst_Suite::addSkip(const QString &fileName, const QString &description, const QString &message) +void tst_QScriptJSTestSuite::addSkip(const QString &fileName, const QString &description, const QString &message) { expectedFailures.append(FailureItem(FailureItem::Skip, QRegExp(fileName), description, message)); } -bool tst_Suite::isExpectedFailure(const QString &fileName, const QString &description, +bool tst_QScriptJSTestSuite::isExpectedFailure(const QString &fileName, const QString &description, QString *message, FailureItem::Action *action) const { for (int i = 0; i < expectedFailures.size(); ++i) { @@ -857,17 +430,17 @@ bool tst_Suite::isExpectedFailure(const QString &fileName, const QString &descri return false; } -void tst_Suite::addFileExclusion(const QString &fileName, const QString &message) +void tst_QScriptJSTestSuite::addFileExclusion(const QString &fileName, const QString &message) { fileExclusions.append(qMakePair(QRegExp(fileName), message)); } -void tst_Suite::addFileExclusion(const QRegExp &rx, const QString &message) +void tst_QScriptJSTestSuite::addFileExclusion(const QRegExp &rx, const QString &message) { fileExclusions.append(qMakePair(rx, message)); } -bool tst_Suite::isExcludedFile(const QString &fileName, QString *message) const +bool tst_QScriptJSTestSuite::isExcludedFile(const QString &fileName, QString *message) const { for (int i = 0; i < fileExclusions.size(); ++i) { if (fileExclusions.at(i).first.indexIn(fileName) != -1) { @@ -879,4 +452,4 @@ bool tst_Suite::isExcludedFile(const QString &fileName, QString *message) const return false; } -QTEST_MAIN(tst_Suite) +QTEST_MAIN(tst_QScriptJSTestSuite) diff --git a/tests/auto/qscriptv8testsuite/abstracttestsuite.cpp b/tests/auto/qscriptv8testsuite/abstracttestsuite.cpp new file mode 100644 index 0000000..d47eb24 --- /dev/null +++ b/tests/auto/qscriptv8testsuite/abstracttestsuite.cpp @@ -0,0 +1,481 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstracttestsuite.h" +#include <QtTest/QtTest> +#include <QtCore/qset.h> +#include <QtCore/qtextstream.h> + +/*! + AbstractTestSuite provides a way of building QtTest test objects + dynamically. The use case is integration of JavaScript test suites + into QtTest autotests. + + Subclasses add their tests functions with addTestFunction() in the + constructor, and must reimplement runTestFunction(). Additionally, + subclasses can reimplement initTestCase() and cleanupTestCase() + (but make sure to call the base implementation). + + AbstractTestSuite uses configuration files for getting information + about skipped tests (skip.txt) and expected test failures + (expect_fail.txt). Subclasses must reimplement + createSkipConfigFile() and createExpectFailConfigFile() for + creating these files, and configData() for processing an entry of + such a file. + + The config file format is as follows: + - Lines starting with '#' are skipped. + - Lines of the form [SYMBOL] means that the upcoming data + should only be processed if the given SYMBOL is defined on + this platform. + - Any other line is split on ' | ' and handed off to the client. + + Subclasses must provide a default tests directory (where the + subclass expects to find the script files to run as tests), and a + default config file directory. Some environment variables can be + used to affect where AbstractTestSuite will look for files: + + - QTSCRIPT_TEST_CONFIG_DIR: Overrides the default test config path. + + - QTSCRIPT_TEST_CONFIG_SUFFIX: Is appended to "skip" and + "expect_fail" to create the test config name. This makes it easy to + maintain skip- and expect_fail-files corresponding to different + revisions of a test suite, and switch between them. + + - QTSCRIPT_TEST_DIR: Overrides the default test dir. + + AbstractTestSuite does _not_ define how the test dir itself is + processed or how tests are run; this is left up to the subclass. + + If no config files are found, AbstractTestSuite will ask the + subclass to create a default skip file. Also, the + shouldGenerateExpectedFailures variable will be set to true. The + subclass should check for this when a test fails, and add an entry + to its set of expected failures. When all tests have been run, + AbstractTestSuite will ask the subclass to create the expect_fail + file based on the tests that failed. The next time the autotest is + run, the created config files will be used. + + The reason for skipping a test is usually that it takes a very long + time to complete (or even hangs completely), or it crashes. It's + not possible for the test runner to know in advance which tests are + problematic, which is why the entries to the skip file are + typically added manually. When running tests for the first time, it + can be useful to run the autotest with the -v1 command line option, + so you can see the name of each test before it's run, and can add a + skip entry if appropriate. +*/ + +// Helper class for constructing the test class's QMetaObject contents +// at runtime. +class TestMetaObjectBuilder +{ +public: + TestMetaObjectBuilder(const QByteArray &className, + const QMetaObject *superClass); + + void appendPrivateVoidSlot(const char *signature); + void appendPrivateVoidSlot(const QString &signature) + { appendPrivateVoidSlot(signature.toLatin1().constData()); } + + void assignContents(QMetaObject &); + +private: + void appendString(const char *); + void finalize(); + + const QByteArray m_className; + const QMetaObject *m_superClass; + QVector<uint> m_data; + QVector<char> m_stringdata; + int m_emptyStringOffset; + bool m_finalized; +}; + +TestMetaObjectBuilder::TestMetaObjectBuilder( + const QByteArray &className, + const QMetaObject *superClass) + : m_className(className), m_superClass(superClass), + m_finalized(false) +{ + // header + m_data << 1 // revision + << 0 // classname + << 0 << 0 // classinfo + << 0 << 10 // methods (backpatched later) + << 0 << 0 // properties + << 0 << 0 // enums/sets + ; + + appendString(className.constData()); + m_emptyStringOffset = m_stringdata.size(); + appendString(""); +} + +void TestMetaObjectBuilder::appendString(const char *s) +{ + char c; + do { + c = *(s++); + m_stringdata << c; + } while (c != '\0'); +} + +void TestMetaObjectBuilder::appendPrivateVoidSlot(const char *signature) +{ + static const int methodCountOffset = 4; + // signature, parameters, type, tag, flags + m_data << m_stringdata.size() + << m_emptyStringOffset + << m_emptyStringOffset + << m_emptyStringOffset + << 0x08; + appendString(signature); + ++m_data[methodCountOffset]; +} + +void TestMetaObjectBuilder::finalize() +{ + if (m_finalized) + return; + m_data << 0; // eod + m_finalized = true; +} + +/** + Assigns this builder's contents to the meta-object \a mo. It's up + to the caller to ensure that this builder (and hence, its data) + stays alive as long as needed. +*/ +void TestMetaObjectBuilder::assignContents(QMetaObject &mo) +{ + finalize(); + mo.d.superdata = m_superClass; + mo.d.stringdata = m_stringdata.constData(); + mo.d.data = m_data.constData(); + mo.d.extradata = 0; +} + + +class TestConfigClientInterface; +// For parsing information about skipped tests and expected failures. +class TestConfigParser +{ +public: + static void parse(const QString &path, + TestConfig::Mode mode, + TestConfigClientInterface *client); + +private: + static QString unescape(const QString &); + static bool isKnownSymbol(const QString &); + static bool isDefined(const QString &); + + static QSet<QString> knownSymbols; + static QSet<QString> definedSymbols; +}; + +QSet<QString> TestConfigParser::knownSymbols; +QSet<QString> TestConfigParser::definedSymbols; + +/** + Parses the config file at the given \a path in the given \a mode. + Handling of errors and data is delegated to the given \a client. +*/ +void TestConfigParser::parse(const QString &path, + TestConfig::Mode mode, + TestConfigClientInterface *client) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + return; + QTextStream stream(&file); + int lineNumber = 0; + QString predicate; + const QString separator = QString::fromLatin1(" | "); + while (!stream.atEnd()) { + ++lineNumber; + QString line = stream.readLine(); + if (line.isEmpty()) + continue; + if (line.startsWith('#')) // Comment + continue; + if (line.startsWith('[')) { // Predicate + if (!line.endsWith(']')) { + client->configError(path, "malformed predicate", lineNumber); + return; + } + QString symbol = line.mid(1, line.size()-2); + if (isKnownSymbol(symbol)) { + predicate = symbol; + } else { + qWarning("symbol %s is not known -- add it to TestConfigParser!", qPrintable(symbol)); + predicate = QString(); + } + } else { + if (predicate.isEmpty() || isDefined(predicate)) { + QStringList parts = line.split(separator, QString::KeepEmptyParts); + for (int i = 0; i < parts.size(); ++i) + parts[i] = unescape(parts[i]); + client->configData(mode, parts); + } + } + } +} + +QString TestConfigParser::unescape(const QString &str) +{ + return QString(str).replace("\\n", "\n"); +} + +bool TestConfigParser::isKnownSymbol(const QString &symbol) +{ + if (knownSymbols.isEmpty()) { + knownSymbols + // If you add a symbol here, add a case for it in + // isDefined() as well. + << "Q_OS_LINUX" + << "Q_OS_SOLARIS" + << "Q_OS_WINCE" + << "Q_OS_SYMBIAN" + << "Q_CC_MSVC" + << "Q_CC_MINGW" + ; + } + return knownSymbols.contains(symbol); +} + +bool TestConfigParser::isDefined(const QString &symbol) +{ + if (definedSymbols.isEmpty()) { + definedSymbols +#ifdef Q_OS_LINUX + << "Q_OS_LINUX" +#endif +#ifdef Q_OS_SOLARIS + << "Q_OS_SOLARIS" +#endif +#ifdef Q_OS_WINCE + << "Q_OS_WINCE" +#endif +#ifdef Q_OS_SYMBIAN + << "Q_OS_SYMBIAN" +#endif +#ifdef Q_CC_MSVC + << "Q_CC_MSVC" +#endif +#ifdef Q_CC_MINGW + << "Q_CC_MINGW" +#endif + ; + } + return definedSymbols.contains(symbol); +} + + +QMetaObject AbstractTestSuite::staticMetaObject; + +const QMetaObject *AbstractTestSuite::metaObject() const +{ + return &staticMetaObject; +} + +void *AbstractTestSuite::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, staticMetaObject.d.stringdata)) + return static_cast<void*>(const_cast<AbstractTestSuite*>(this)); + return QObject::qt_metacast(_clname); +} + +int AbstractTestSuite::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: + initTestCase(); + break; + case 1: + cleanupTestCase(); + break; + default: + // If another method is added above, this offset must be adjusted. + runTestFunction(_id - 2); + } + _id -= staticMetaObject.methodCount() - staticMetaObject.methodOffset(); + } + return _id; +} + +AbstractTestSuite::AbstractTestSuite(const QByteArray &className, + const QString &defaultTestsPath, + const QString &defaultConfigPath) + : shouldGenerateExpectedFailures(false), + metaBuilder(new TestMetaObjectBuilder(className, &QObject::staticMetaObject)) +{ + QString testConfigPath = qgetenv("QTSCRIPT_TEST_CONFIG_DIR"); + if (testConfigPath.isEmpty()) + testConfigPath = defaultConfigPath; + QString configSuffix = qgetenv("QTSCRIPT_TEST_CONFIG_SUFFIX"); + skipConfigPath = QString::fromLatin1("%0/skip%1.txt") + .arg(testConfigPath).arg(configSuffix); + expectFailConfigPath = QString::fromLatin1("%0/expect_fail%1.txt") + .arg(testConfigPath).arg(configSuffix); + + QString testsPath = qgetenv("QTSCRIPT_TEST_DIR"); + if (testsPath.isEmpty()) + testsPath = defaultTestsPath; + testsDir = QDir(testsPath); + + addTestFunction("initTestCase"); + addTestFunction("cleanupTestCase"); + + // Subclass constructors should add their custom test functions to + // the meta-object and call finalizeMetaObject(). +} + +AbstractTestSuite::~AbstractTestSuite() +{ + delete metaBuilder; +} + +void AbstractTestSuite::addTestFunction(const QString &name, + DataFunctionCreation dfc) +{ + if (dfc == CreateDataFunction) { + QString dataSignature = QString::fromLatin1("%0_data()").arg(name); + metaBuilder->appendPrivateVoidSlot(dataSignature); + } + QString signature = QString::fromLatin1("%0()").arg(name); + metaBuilder->appendPrivateVoidSlot(signature); +} + +void AbstractTestSuite::finalizeMetaObject() +{ + metaBuilder->assignContents(staticMetaObject); +} + +void AbstractTestSuite::initTestCase() +{ + if (!testsDir.exists()) { + QString message = QString::fromLatin1("tests directory (%0) doesn't exist.") + .arg(testsDir.path()); + QFAIL(qPrintable(message)); + return; + } + + if (QFileInfo(skipConfigPath).exists()) + TestConfigParser::parse(skipConfigPath, TestConfig::Skip, this); + else + createSkipConfigFile(); + + if (QFileInfo(expectFailConfigPath).exists()) + TestConfigParser::parse(expectFailConfigPath, TestConfig::ExpectFail, this); + else + shouldGenerateExpectedFailures = true; +} + +void AbstractTestSuite::cleanupTestCase() +{ + if (shouldGenerateExpectedFailures) + createExpectFailConfigFile(); +} + +void AbstractTestSuite::configError(const QString &path, const QString &message, int lineNumber) +{ + QString output; + output.append(path); + if (lineNumber != -1) + output.append(":").append(QString::number(lineNumber)); + output.append(": ").append(message); + QFAIL(qPrintable(output)); +} + +void AbstractTestSuite::createSkipConfigFile() +{ + QFile file(skipConfigPath); + if (!file.open(QIODevice::WriteOnly)) + return; + QWARN(qPrintable(QString::fromLatin1("creating %0").arg(skipConfigPath))); + QTextStream stream(&file); + + writeSkipConfigFile(stream); + + file.close(); +} + +void AbstractTestSuite::createExpectFailConfigFile() +{ + QFile file(expectFailConfigPath); + if (!file.open(QFile::WriteOnly)) + return; + QWARN(qPrintable(QString::fromLatin1("creating %0").arg(expectFailConfigPath))); + QTextStream stream(&file); + + writeExpectFailConfigFile(stream); + + file.close(); +} + +/*! + Convenience function for reading all contents of a file. + */ +QString AbstractTestSuite::readFile(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::ReadOnly)) + return QString(); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + return stream.readAll(); +} + +/*! + Escapes characters in the string \a str so it's suitable for writing + to a config file. + */ +QString AbstractTestSuite::escape(const QString &str) +{ + return QString(str).replace("\n", "\\n"); +} diff --git a/tests/auto/qscriptv8testsuite/abstracttestsuite.h b/tests/auto/qscriptv8testsuite/abstracttestsuite.h new file mode 100644 index 0000000..b13c61a --- /dev/null +++ b/tests/auto/qscriptv8testsuite/abstracttestsuite.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTTESTSUITE_H +#define ABSTRACTTESTSUITE_H + +#include <QtCore/qobject.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qdir.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> +#include <QtCore/qtextstream.h> + +class TestMetaObjectBuilder; + +namespace TestConfig { +enum Mode { + Skip, + ExpectFail +}; +} + +// For receiving callbacks from the config parser. +class TestConfigClientInterface +{ +public: + virtual ~TestConfigClientInterface() {} + virtual void configData(TestConfig::Mode mode, + const QStringList &parts) = 0; + virtual void configError(const QString &path, + const QString &message, + int lineNumber) = 0; +}; + +class AbstractTestSuite : public QObject, + public TestConfigClientInterface +{ +// No Q_OBJECT macro, we implement the meta-object ourselves. +public: + AbstractTestSuite(const QByteArray &className, + const QString &defaultTestsPath, + const QString &defaultConfigPath); + virtual ~AbstractTestSuite(); + + static QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + + static QString readFile(const QString &); + static QString escape(const QString &); + +protected: + enum DataFunctionCreation { + DontCreateDataFunction, + CreateDataFunction + }; + + void addTestFunction(const QString &, + DataFunctionCreation = DontCreateDataFunction); + void finalizeMetaObject(); + + virtual void initTestCase(); + virtual void cleanupTestCase(); + + virtual void writeSkipConfigFile(QTextStream &) = 0; + virtual void writeExpectFailConfigFile(QTextStream &) = 0; + + virtual void runTestFunction(int index) = 0; + + virtual void configError(const QString &path, const QString &message, int lineNumber); + + QDir testsDir; + bool shouldGenerateExpectedFailures; + +private: + TestMetaObjectBuilder *metaBuilder; + QString skipConfigPath, expectFailConfigPath; + +private: + void createSkipConfigFile(); + void createExpectFailConfigFile(); +}; + +#endif diff --git a/tests/auto/qscriptv8testsuite/abstracttestsuite.pri b/tests/auto/qscriptv8testsuite/abstracttestsuite.pri new file mode 100644 index 0000000..1de5b93 --- /dev/null +++ b/tests/auto/qscriptv8testsuite/abstracttestsuite.pri @@ -0,0 +1,4 @@ +SOURCES += $$PWD/abstracttestsuite.cpp +HEADERS += $$PWD/abstracttestsuite.h +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD diff --git a/tests/auto/qscriptv8testsuite/expect_fail.txt b/tests/auto/qscriptv8testsuite/expect_fail.txt new file mode 100644 index 0000000..a4eee73 --- /dev/null +++ b/tests/auto/qscriptv8testsuite/expect_fail.txt @@ -0,0 +1,16 @@ +# testcase | actual | expected | message +arguments-enum | 2 | 0 +const-redecl | undefined | TypeError | local:'const x; var x' +date-parse | NaN | 946713600000 | Sat, 01-Jan-2000 08:00:00 GMT+00:00 +delete-global-properties | true | false +delete | false | true | delete 100 +function-arguments-null | false | true +function-caller | null | function eval() {\n [native code]\n} +function-prototype | prototype | disconnectconnect +global-const-var-conflicts | false | true +number-tostring | 0 | 0.0000a7c5ac471b4788 +parse-int-float | 1e+21 | 1 +regexp | false | true +string-lastindexof | 0 | -1 +string-split | 4 | 3 | 19 - array length +substr | abcdefghijklmn | diff --git a/tests/auto/qscriptv8testsuite/qscriptv8testsuite.pro b/tests/auto/qscriptv8testsuite/qscriptv8testsuite.pro index 00e2e01..e1c6234 100644 --- a/tests/auto/qscriptv8testsuite/qscriptv8testsuite.pro +++ b/tests/auto/qscriptv8testsuite/qscriptv8testsuite.pro @@ -2,3 +2,4 @@ load(qttest_p4) QT = core script SOURCES += tst_qscriptv8testsuite.cpp RESOURCES += qscriptv8testsuite.qrc +include(abstracttestsuite.pri) diff --git a/tests/auto/qscriptv8testsuite/qscriptv8testsuite.qrc b/tests/auto/qscriptv8testsuite/qscriptv8testsuite.qrc index a894ee5..150ccf0 100644 --- a/tests/auto/qscriptv8testsuite/qscriptv8testsuite.qrc +++ b/tests/auto/qscriptv8testsuite/qscriptv8testsuite.qrc @@ -1,5 +1,7 @@ <!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>tests</file> + <file>expect_fail.txt</file> + <file>skip.txt</file> </qresource> </RCC> diff --git a/tests/auto/qscriptv8testsuite/skip.txt b/tests/auto/qscriptv8testsuite/skip.txt new file mode 100644 index 0000000..3c2cc53 --- /dev/null +++ b/tests/auto/qscriptv8testsuite/skip.txt @@ -0,0 +1,17 @@ +# testcase | message +debug-* | not applicable +mirror-* | not applicable +array-concat | Hangs on JSC backend +array-splice | Hangs on JSC backend +sparse-array-reverse | Hangs on JSC backend +string-case | V8-specific behavior? (Doesn't pass on SpiderMonkey either) + +[Q_OS_WINCE] +deep-recursion | Demands too much memory on WinCE +nested-repetition-count-overflow | Demands too much memory on WinCE +unicode-test | Demands too much memory on WinCE +mul-exhaustive | Demands too much memory on WinCE + +[Q_OS_SYMBIAN] +nested-repetition-count-overflow | Demands too much memory on Symbian +unicode-test | Demands too much memory on Symbian diff --git a/tests/auto/qscriptv8testsuite/tst_qscriptv8testsuite.cpp b/tests/auto/qscriptv8testsuite/tst_qscriptv8testsuite.cpp index 7d0858e..b35fd06 100644 --- a/tests/auto/qscriptv8testsuite/tst_qscriptv8testsuite.cpp +++ b/tests/auto/qscriptv8testsuite/tst_qscriptv8testsuite.cpp @@ -40,68 +40,33 @@ ****************************************************************************/ +#include "abstracttestsuite.h" #include <QtTest/QtTest> -#include <QByteArray> - #include <QtScript> //TESTED_CLASS= //TESTED_FILES= -// Uncomment the following define to have the autotest generate -// addExpectedFailure() code for all the tests that fail. -// This is useful when a whole new test (sub)suite is added. -// The code is stored in addexpectedfailures.cpp. -// Paste the contents into this file after the existing -// addExpectedFailure() calls. - -//#define GENERATE_ADDEXPECTEDFAILURE_CODE - -static QString readFile(const QString &filename) -{ - QFile file(filename); - if (!file.open(QFile::ReadOnly)) - return QString(); - QTextStream stream(&file); - stream.setCodec("UTF-8"); - return stream.readAll(); -} - -static void appendCString(QVector<char> *v, const char *s) -{ - char c; - do { - c = *(s++); - *v << c; - } while (c != '\0'); -} - -struct ExpectedFailure +class tst_QScriptV8TestSuite : public AbstractTestSuite { - ExpectedFailure(const QString &name, const QString &act, - const QString &exp, const QString &msg) - : testName(name), actual(act), expected(exp), message(msg) - { } - - QString testName; - QString actual; - QString expected; - QString message; -}; - -class tst_Suite : public QObject -{ - public: - tst_Suite(); - virtual ~tst_Suite(); + tst_QScriptV8TestSuite(); + virtual ~tst_QScriptV8TestSuite(); + +protected: + struct ExpectedFailure + { + ExpectedFailure(const QString &name, const QString &act, + const QString &exp, const QString &msg) + : testName(name), actual(act), expected(exp), message(msg) + { } + + QString testName; + QString actual; + QString expected; + QString message; + }; - static QMetaObject staticMetaObject; - virtual const QMetaObject *metaObject() const; - virtual void *qt_metacast(const char *); - virtual int qt_metacall(QMetaObject::Call, int, void **argv); - -private: void addExpectedFailure(const QString &testName, const QString &actual, const QString &expected, const QString &message); bool isExpectedFailure(const QString &testName, const QString &actual, @@ -110,34 +75,23 @@ private: void addTestExclusion(const QRegExp &rx, const QString &message); bool isExcludedTest(const QString &testName, QString *message) const; - QDir testsDir; + virtual void initTestCase(); + virtual void configData(TestConfig::Mode mode, const QStringList &parts); + virtual void writeSkipConfigFile(QTextStream &); + virtual void writeExpectFailConfigFile(QTextStream &); + virtual void runTestFunction(int testIndex); + QStringList testNames; QList<ExpectedFailure> expectedFailures; QList<QPair<QRegExp, QString> > testExclusions; QString mjsunitContents; -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - QString generatedAddExpectedFailureCode; -#endif }; -QMetaObject tst_Suite::staticMetaObject; - -Q_GLOBAL_STATIC(QVector<uint>, qt_meta_data_tst_Suite) -Q_GLOBAL_STATIC(QVector<char>, qt_meta_stringdata_tst_Suite) - -const QMetaObject *tst_Suite::metaObject() const -{ - return &staticMetaObject; -} - -void *tst_Suite::qt_metacast(const char *_clname) -{ - if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_tst_Suite()->constData())) - return static_cast<void*>(const_cast<tst_Suite*>(this)); - return QObject::qt_metacast(_clname); -} - +// We expect failing tests to call the fail() function (defined in +// mjsunit.js) with arguments expected, actual, message_opt. This +// function intercepts the call, calls the real fail() function (which +// will throw an exception), and sets the original arguments on the +// exception object so that we can process them later. static QScriptValue qscript_fail(QScriptContext *ctx, QScriptEngine *eng) { QScriptValue realFail = ctx->callee().data(); @@ -146,193 +100,141 @@ static QScriptValue qscript_fail(QScriptContext *ctx, QScriptEngine *eng) Q_ASSERT(eng->hasUncaughtException()); ret.setProperty("expected", ctx->argument(0)); ret.setProperty("actual", ctx->argument(1)); + ret.setProperty("message", ctx->argument(2)); QScriptContextInfo info(ctx->parentContext()->parentContext()); ret.setProperty("lineNumber", info.lineNumber()); return ret; } -int tst_Suite::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +void tst_QScriptV8TestSuite::writeSkipConfigFile(QTextStream &stream) { - _id = QObject::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - QString name = testNames.at(_id); - QString path = testsDir.absoluteFilePath(name + ".js"); - QString excludeMessage; - if (isExcludedTest(name, &excludeMessage)) { - QTest::qSkip(excludeMessage.toLatin1(), QTest::SkipAll, path.toLatin1(), -1); - } else { - QScriptEngine engine; - engine.evaluate(mjsunitContents).toString(); - if (engine.hasUncaughtException()) { - QStringList bt = engine.uncaughtExceptionBacktrace(); - QString err = engine.uncaughtException().toString(); - qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); - } else { - QScriptValue fakeFail = engine.newFunction(qscript_fail); - fakeFail.setData(engine.globalObject().property("fail")); - engine.globalObject().setProperty("fail", fakeFail); - QString contents = readFile(path); - QScriptValue ret = engine.evaluate(contents); - if (engine.hasUncaughtException()) { - if (!ret.isError()) { - Q_ASSERT(ret.instanceOf(engine.globalObject().property("MjsUnitAssertionError"))); - QString actual = ret.property("actual").toString(); - QString expected = ret.property("expected").toString(); - int lineNumber = ret.property("lineNumber").toInt32(); - QString failMessage; - if (isExpectedFailure(name, actual, expected, &failMessage)) { - QTest::qExpectFail("", failMessage.toLatin1(), - QTest::Continue, path.toLatin1(), - lineNumber); - } -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - else { - generatedAddExpectedFailureCode.append( - " addExpectedFailure(\"" + name - + "\", \"" + actual + "\", \"" + expected - + "\", willFixInNextReleaseMessage);\n"); - } -#endif - QTest::qCompare(actual, expected, "actual", "expect", - path.toLatin1(), lineNumber); - } else { - int lineNumber = ret.property("lineNumber").toInt32(); - QTest::qExpectFail("", ret.toString().toLatin1(), - QTest::Continue, path.toLatin1(), lineNumber); - QTest::qVerify(false, ret.toString().toLatin1(), "", path.toLatin1(), lineNumber); - } - } - } - } - _id -= testNames.size(); - } - return _id; + stream << QString::fromLatin1("# testcase | message") << endl; } -tst_Suite::tst_Suite() +void tst_QScriptV8TestSuite::writeExpectFailConfigFile(QTextStream &stream) { - testsDir = QDir(":/tests"); - if (!testsDir.exists()) { - qWarning("*** no tests/ dir!"); - } else { - if (!testsDir.exists("mjsunit.js")) - qWarning("*** no tests/mjsunit.js file!"); - else { - mjsunitContents = readFile(testsDir.absoluteFilePath("mjsunit.js")); - if (mjsunitContents.isEmpty()) - qWarning("*** tests/mjsunit.js is empty!"); - } + stream << QString::fromLatin1("# testcase | actual | expected | message") << endl; + for (int i = 0; i < expectedFailures.size(); ++i) { + const ExpectedFailure &fail = expectedFailures.at(i); + stream << QString::fromLatin1("%0 | %1 | %2") + .arg(fail.testName) + .arg(escape(fail.actual)) + .arg(escape(fail.expected)); + if (!fail.message.isEmpty()) + stream << QString::fromLatin1(" | %0").arg(escape(fail.message)); + stream << endl; } - QString willFixInNextReleaseMessage = QString::fromLatin1("Will fix in next release"); - addExpectedFailure("arguments-enum", "2", "0", willFixInNextReleaseMessage); - addExpectedFailure("const-redecl", "undefined", "TypeError", willFixInNextReleaseMessage); - addExpectedFailure("global-const-var-conflicts", "false", "true", willFixInNextReleaseMessage); - addExpectedFailure("string-lastindexof", "0", "-1", "test is wrong?"); - -#ifndef Q_OS_LINUX - addExpectedFailure("to-precision", "1.235e+27", "1.234e+27", "QTBUG-8053: toPrecision(4) gives wrong result on Mac"); -#endif - -#ifdef Q_OS_SOLARIS - addExpectedFailure("math-min-max", "Infinity", "-Infinity", willFixInNextReleaseMessage); - addExpectedFailure("negate-zero", "false", "true", willFixInNextReleaseMessage); - addExpectedFailure("str-to-num", "Infinity", "-Infinity", willFixInNextReleaseMessage); -#endif - - addTestExclusion("debug-*", "not applicable"); - addTestExclusion("mirror-*", "not applicable"); - - addTestExclusion("array-concat", "Hangs on JSC backend"); - addTestExclusion("array-splice", "Hangs on JSC backend"); - addTestExclusion("sparse-array-reverse", "Hangs on JSC backend"); - - addTestExclusion("string-case", "V8-specific behavior? (Doesn't pass on SpiderMonkey either)"); - -#ifdef Q_OS_WINCE - addTestExclusion("deep-recursion", "Demands too much memory on WinCE"); - addTestExclusion("nested-repetition-count-overflow", "Demands too much memory on WinCE"); - addTestExclusion("unicode-test", "Demands too much memory on WinCE"); - addTestExclusion("mul-exhaustive", "Demands too much memory on WinCE"); -#endif - -#ifdef Q_OS_SYMBIAN - addTestExclusion("nested-repetition-count-overflow", "Demands too much memory on Symbian"); - addTestExclusion("unicode-test", "Demands too much memory on Symbian"); -#endif - // Failures due to switch to JSC as back-end - addExpectedFailure("date-parse", "NaN", "946713600000", willFixInNextReleaseMessage); - addExpectedFailure("delete-global-properties", "true", "false", willFixInNextReleaseMessage); - addExpectedFailure("delete", "false", "true", willFixInNextReleaseMessage); - addExpectedFailure("function-arguments-null", "false", "true", willFixInNextReleaseMessage); - addExpectedFailure("function-caller", "null", "function eval() {\n [native code]\n}", willFixInNextReleaseMessage); - addExpectedFailure("function-prototype", "prototype", "disconnectconnect", willFixInNextReleaseMessage); - addExpectedFailure("number-tostring", "0", "0.0000a7c5ac471b4788", willFixInNextReleaseMessage); - addExpectedFailure("parse-int-float", "1e+21", "1", willFixInNextReleaseMessage); - addExpectedFailure("regexp", "false", "true", willFixInNextReleaseMessage); - addExpectedFailure("smi-negative-zero", "-Infinity", "Infinity", willFixInNextReleaseMessage); - addExpectedFailure("string-split", "4", "3", willFixInNextReleaseMessage); - addExpectedFailure("substr", "abcdefghijklmn", "", willFixInNextReleaseMessage); +} - static const char klass[] = "tst_QScriptV8TestSuite"; +void tst_QScriptV8TestSuite::runTestFunction(int testIndex) +{ + QString name = testNames.at(testIndex); + QString path = testsDir.absoluteFilePath(name + ".js"); - QVector<uint> *data = qt_meta_data_tst_Suite(); - // content: - *data << 1 // revision - << 0 // classname - << 0 << 0 // classinfo - << 0 << 10 // methods (backpatched later) - << 0 << 0 // properties - << 0 << 0 // enums/sets - ; + QString excludeMessage; + if (isExcludedTest(name, &excludeMessage)) { + QTest::qSkip(excludeMessage.toLatin1(), QTest::SkipAll, path.toLatin1(), -1); + return; + } - QVector<char> *stringdata = qt_meta_stringdata_tst_Suite(); - appendCString(stringdata, klass); - appendCString(stringdata, ""); + QScriptEngine engine; + engine.evaluate(mjsunitContents); + if (engine.hasUncaughtException()) { + QStringList bt = engine.uncaughtExceptionBacktrace(); + QString err = engine.uncaughtException().toString(); + qWarning("%s\n%s", qPrintable(err), qPrintable(bt.join("\n"))); + } else { + // Prepare to intercept calls to mjsunit's fail() function. + QScriptValue fakeFail = engine.newFunction(qscript_fail); + fakeFail.setData(engine.globalObject().property("fail")); + engine.globalObject().setProperty("fail", fakeFail); + + QString contents = readFile(path); + QScriptValue ret = engine.evaluate(contents); + if (engine.hasUncaughtException()) { + if (!ret.isError()) { + Q_ASSERT(ret.instanceOf(engine.globalObject().property("MjsUnitAssertionError"))); + QString actual = ret.property("actual").toString(); + QString expected = ret.property("expected").toString(); + int lineNumber = ret.property("lineNumber").toInt32(); + QString failMessage; + if (shouldGenerateExpectedFailures) { + if (ret.property("message").isString()) + failMessage = ret.property("message").toString(); + addExpectedFailure(name, actual, expected, failMessage); + } else if (isExpectedFailure(name, actual, expected, &failMessage)) { + QTest::qExpectFail("", failMessage.toLatin1(), + QTest::Continue, path.toLatin1(), + lineNumber); + } + QTest::qCompare(actual, expected, "actual", "expect", + path.toLatin1(), lineNumber); + } else { + int lineNumber = ret.property("lineNumber").toInt32(); + QTest::qExpectFail("", ret.toString().toLatin1(), + QTest::Continue, path.toLatin1(), lineNumber); + QTest::qVerify(false, ret.toString().toLatin1(), "", path.toLatin1(), lineNumber); + } + } + } +} +tst_QScriptV8TestSuite::tst_QScriptV8TestSuite() + : AbstractTestSuite("tst_QScriptV8TestSuite", + ":/tests", ":/") +{ + // One test function per test file. QFileInfoList testFileInfos; testFileInfos = testsDir.entryInfoList(QStringList() << "*.js", QDir::Files); foreach (QFileInfo tfi, testFileInfos) { QString name = tfi.baseName(); - // slot: signature, parameters, type, tag, flags - QString slot = QString::fromLatin1("%0()").arg(name); - static const int nullbyte = sizeof(klass); - *data << stringdata->size() << nullbyte << nullbyte << nullbyte << 0x08; - appendCString(stringdata, slot.toLatin1()); + addTestFunction(name); testNames.append(name); } - (*data)[4] = testFileInfos.size(); + finalizeMetaObject(); +} - *data << 0; // eod +tst_QScriptV8TestSuite::~tst_QScriptV8TestSuite() +{ +} - // initialize staticMetaObject - staticMetaObject.d.superdata = &QObject::staticMetaObject; - staticMetaObject.d.stringdata = stringdata->constData(); - staticMetaObject.d.data = data->constData(); - staticMetaObject.d.extradata = 0; +void tst_QScriptV8TestSuite::initTestCase() +{ + AbstractTestSuite::initTestCase(); + + // FIXME: These warnings should be QFAIL, but that would make the + // test fail right now. + if (!testsDir.exists("mjsunit.js")) + qWarning("*** no tests/mjsunit.js file!"); + else { + mjsunitContents = readFile(testsDir.absoluteFilePath("mjsunit.js")); + if (mjsunitContents.isEmpty()) + qWarning("*** tests/mjsunit.js is empty!"); + } } -tst_Suite::~tst_Suite() +void tst_QScriptV8TestSuite::configData(TestConfig::Mode mode, const QStringList &parts) { -#ifdef GENERATE_ADDEXPECTEDFAILURE_CODE - if (!generatedAddExpectedFailureCode.isEmpty()) { - QFile file("addexpectedfailures.cpp"); - file.open(QFile::WriteOnly); - QTextStream ts(&file); - ts << generatedAddExpectedFailureCode; + switch (mode) { + case TestConfig::Skip: + addTestExclusion(parts.at(0), parts.value(1)); + break; + + case TestConfig::ExpectFail: + addExpectedFailure(parts.at(0), parts.value(1), + parts.value(2), parts.value(3)); + break; } -#endif } -void tst_Suite::addExpectedFailure(const QString &testName, const QString &actual, +void tst_QScriptV8TestSuite::addExpectedFailure(const QString &testName, const QString &actual, const QString &expected, const QString &message) { expectedFailures.append(ExpectedFailure(testName, actual, expected, message)); } -bool tst_Suite::isExpectedFailure(const QString &testName, const QString &actual, +bool tst_QScriptV8TestSuite::isExpectedFailure(const QString &testName, const QString &actual, const QString &expected, QString *message) const { for (int i = 0; i < expectedFailures.size(); ++i) { @@ -346,17 +248,17 @@ bool tst_Suite::isExpectedFailure(const QString &testName, const QString &actual return false; } -void tst_Suite::addTestExclusion(const QString &testName, const QString &message) +void tst_QScriptV8TestSuite::addTestExclusion(const QString &testName, const QString &message) { testExclusions.append(qMakePair(QRegExp(testName), message)); } -void tst_Suite::addTestExclusion(const QRegExp &rx, const QString &message) +void tst_QScriptV8TestSuite::addTestExclusion(const QRegExp &rx, const QString &message) { testExclusions.append(qMakePair(rx, message)); } -bool tst_Suite::isExcludedTest(const QString &testName, QString *message) const +bool tst_QScriptV8TestSuite::isExcludedTest(const QString &testName, QString *message) const { for (int i = 0; i < testExclusions.size(); ++i) { if (testExclusions.at(i).first.indexIn(testName) != -1) { @@ -368,4 +270,4 @@ bool tst_Suite::isExcludedTest(const QString &testName, QString *message) const return false; } -QTEST_MAIN(tst_Suite) +QTEST_MAIN(tst_QScriptV8TestSuite) diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index 8177d29..739f902 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -390,6 +390,9 @@ void tst_QSslSocket::constructing() QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration(); // verify that changing the default config doesn't affect this socket + // (on Unix, the ca certs might be empty, depending on whether we load + // them on demand or not, so set them explicitly) + socket.setCaCertificates(QSslSocket::systemCaCertificates()); QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>()); QSslSocket::setDefaultCiphers(QList<QSslCipher>()); QVERIFY(!socket.caCertificates().isEmpty()); @@ -518,11 +521,6 @@ void tst_QSslSocket::sslErrors_data() << 993 << (SslErrorList() << QSslError::HostNameMismatch << QSslError::SelfSignedCertificate); - - QTest::newRow("imap.trolltech.com") - << "imap.trolltech.com" - << 993 - << (SslErrorList() << QSslError::SelfSignedCertificateInChain); } void tst_QSslSocket::sslErrors() diff --git a/tests/auto/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro b/tests/auto/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro new file mode 100644 index 0000000..ea62865 --- /dev/null +++ b/tests/auto/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro @@ -0,0 +1,36 @@ +load(qttest_p4) + +SOURCES += tst_qsslsocket_onDemandCertificates_member.cpp +!wince*:win32:LIBS += -lws2_32 +QT += network +QT -= gui + +TARGET = tst_qsslsocket_onDemandCertificates_member + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +wince* { + DEFINES += SRCDIR=\\\"./\\\" + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles +} else:symbian { + TARGET.EPOCHEAPSIZE="0x100 0x1000000" + TARGET.CAPABILITY=NetworkServices + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles + INCLUDEPATH *= $$MW_LAYER_SYSTEMINCLUDE # Needed for e32svr.h in S^3 envs +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/qsslsocket_onDemandCertificates_member/tst_qsslsocket_onDemandCertificates_member.cpp b/tests/auto/qsslsocket_onDemandCertificates_member/tst_qsslsocket_onDemandCertificates_member.cpp new file mode 100644 index 0000000..2a1358d --- /dev/null +++ b/tests/auto/qsslsocket_onDemandCertificates_member/tst_qsslsocket_onDemandCertificates_member.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtNetwork> +#include <QtTest/QtTest> + +#include <QNetworkProxy> +#include <QAuthenticator> + +#include "private/qhostinfo_p.h" + +#include "../network-settings.h" + +#ifdef Q_OS_SYMBIAN +#define SRCDIR "" +#endif + +#ifndef QT_NO_OPENSSL +class QSslSocketPtr: public QSharedPointer<QSslSocket> +{ +public: + inline QSslSocketPtr(QSslSocket *ptr = 0) + : QSharedPointer<QSslSocket>(ptr) + { } + + inline operator QSslSocket *() const { return data(); } +}; +#endif + +class tst_QSslSocket_onDemandCertificates_member : public QObject +{ + Q_OBJECT + + int proxyAuthCalled; + +public: + tst_QSslSocket_onDemandCertificates_member(); + virtual ~tst_QSslSocket_onDemandCertificates_member(); + +#ifndef QT_NO_OPENSSL + QSslSocketPtr newSocket(); +#endif + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + +#ifndef QT_NO_OPENSSL +private slots: + void onDemandRootCertLoadingMemberMethods(); + +private: + QSslSocket *socket; +#endif // QT_NO_OPENSSL +}; + +tst_QSslSocket_onDemandCertificates_member::tst_QSslSocket_onDemandCertificates_member() +{ + Q_SET_DEFAULT_IAP +} + +tst_QSslSocket_onDemandCertificates_member::~tst_QSslSocket_onDemandCertificates_member() +{ +} + +enum ProxyTests { + NoProxy = 0x00, + Socks5Proxy = 0x01, + HttpProxy = 0x02, + TypeMask = 0x0f, + + NoAuth = 0x00, + AuthBasic = 0x10, + AuthNtlm = 0x20, + AuthMask = 0xf0 +}; + +void tst_QSslSocket_onDemandCertificates_member::initTestCase_data() +{ + QTest::addColumn<bool>("setProxy"); + QTest::addColumn<int>("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy); + QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic); + + QTest::newRow("WithHttpProxy") << true << int(HttpProxy); + QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic); + // uncomment the line below when NTLM works +// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm); +} + +void tst_QSslSocket_onDemandCertificates_member::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + QString testServer = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QNetworkProxy proxy; + + switch (proxyType) { + case Socks5Proxy: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, testServer, 1080); + break; + + case Socks5Proxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, testServer, 1081); + break; + + case HttpProxy | NoAuth: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3128); + break; + + case HttpProxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3129); + break; + + case HttpProxy | AuthNtlm: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3130); + break; + } + QNetworkProxy::setApplicationProxy(proxy); + } + + qt_qhostinfo_clear_cache(); +} + +void tst_QSslSocket_onDemandCertificates_member::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +#ifndef QT_NO_OPENSSL +QSslSocketPtr tst_QSslSocket_onDemandCertificates_member::newSocket() +{ + QSslSocket *socket = new QSslSocket; + + proxyAuthCalled = 0; + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::DirectConnection); + + return QSslSocketPtr(socket); +} +#endif + +void tst_QSslSocket_onDemandCertificates_member::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + ++proxyAuthCalled; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslSocket_onDemandCertificates_member::onDemandRootCertLoadingMemberMethods() +{ + QString host("qt.nokia.com"); + + // not using any root certs -> should not work + QSslSocketPtr socket2 = newSocket(); + this->socket = socket2; + socket2->setCaCertificates(QList<QSslCertificate>()); + socket2->connectToHostEncrypted(host, 443); + QVERIFY(!socket2->waitForEncrypted()); + + // default: using on demand loading -> should work + QSslSocketPtr socket = newSocket(); + this->socket = socket; + socket->connectToHostEncrypted(host, 443); + QVERIFY(socket->waitForEncrypted()); + + // not using any root certs again -> should not work + QSslSocketPtr socket3 = newSocket(); + this->socket = socket3; + socket3->setCaCertificates(QList<QSslCertificate>()); + socket3->connectToHostEncrypted(host, 443); + QVERIFY(!socket3->waitForEncrypted()); +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslSocket_onDemandCertificates_member) +#include "tst_qsslsocket_onDemandCertificates_member.moc" diff --git a/tests/auto/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro b/tests/auto/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro new file mode 100644 index 0000000..13990cb --- /dev/null +++ b/tests/auto/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro @@ -0,0 +1,36 @@ +load(qttest_p4) + +SOURCES += tst_qsslsocket_onDemandCertificates_static.cpp +!wince*:win32:LIBS += -lws2_32 +QT += network +QT -= gui + +TARGET = tst_qsslsocket_onDemandCertificates_static + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +wince* { + DEFINES += SRCDIR=\\\"./\\\" + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles +} else:symbian { + TARGET.EPOCHEAPSIZE="0x100 0x1000000" + TARGET.CAPABILITY=NetworkServices + + certFiles.files = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles + INCLUDEPATH *= $$MW_LAYER_SYSTEMINCLUDE # Needed for e32svr.h in S^3 envs +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/qsslsocket_onDemandCertificates_static/tst_qsslsocket_onDemandCertificates_static.cpp b/tests/auto/qsslsocket_onDemandCertificates_static/tst_qsslsocket_onDemandCertificates_static.cpp new file mode 100644 index 0000000..8259977 --- /dev/null +++ b/tests/auto/qsslsocket_onDemandCertificates_static/tst_qsslsocket_onDemandCertificates_static.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtNetwork> +#include <QtTest/QtTest> + +#include <QNetworkProxy> +#include <QAuthenticator> + +#include "private/qhostinfo_p.h" + +#include "../network-settings.h" + +#ifdef Q_OS_SYMBIAN +#define SRCDIR "" +#endif + +#ifndef QT_NO_OPENSSL +class QSslSocketPtr: public QSharedPointer<QSslSocket> +{ +public: + inline QSslSocketPtr(QSslSocket *ptr = 0) + : QSharedPointer<QSslSocket>(ptr) + { } + + inline operator QSslSocket *() const { return data(); } +}; +#endif + +class tst_QSslSocket_onDemandCertificates_static : public QObject +{ + Q_OBJECT + + int proxyAuthCalled; + +public: + tst_QSslSocket_onDemandCertificates_static(); + virtual ~tst_QSslSocket_onDemandCertificates_static(); + +#ifndef QT_NO_OPENSSL + QSslSocketPtr newSocket(); +#endif + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + +#ifndef QT_NO_OPENSSL +private slots: + void onDemandRootCertLoadingStaticMethods(); + +private: + QSslSocket *socket; +#endif // QT_NO_OPENSSL +}; + +tst_QSslSocket_onDemandCertificates_static::tst_QSslSocket_onDemandCertificates_static() +{ + Q_SET_DEFAULT_IAP +} + +tst_QSslSocket_onDemandCertificates_static::~tst_QSslSocket_onDemandCertificates_static() +{ +} + +enum ProxyTests { + NoProxy = 0x00, + Socks5Proxy = 0x01, + HttpProxy = 0x02, + TypeMask = 0x0f, + + NoAuth = 0x00, + AuthBasic = 0x10, + AuthNtlm = 0x20, + AuthMask = 0xf0 +}; + +void tst_QSslSocket_onDemandCertificates_static::initTestCase_data() +{ + QTest::addColumn<bool>("setProxy"); + QTest::addColumn<int>("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy); + QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic); + + QTest::newRow("WithHttpProxy") << true << int(HttpProxy); + QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic); + // uncomment the line below when NTLM works +// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm); +} + +void tst_QSslSocket_onDemandCertificates_static::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + QString testServer = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QNetworkProxy proxy; + + switch (proxyType) { + case Socks5Proxy: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, testServer, 1080); + break; + + case Socks5Proxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, testServer, 1081); + break; + + case HttpProxy | NoAuth: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3128); + break; + + case HttpProxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3129); + break; + + case HttpProxy | AuthNtlm: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, testServer, 3130); + break; + } + QNetworkProxy::setApplicationProxy(proxy); + } + + qt_qhostinfo_clear_cache(); +} + +void tst_QSslSocket_onDemandCertificates_static::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +#ifndef QT_NO_OPENSSL +QSslSocketPtr tst_QSslSocket_onDemandCertificates_static::newSocket() +{ + QSslSocket *socket = new QSslSocket; + + proxyAuthCalled = 0; + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::DirectConnection); + + return QSslSocketPtr(socket); +} +#endif + +void tst_QSslSocket_onDemandCertificates_static::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + ++proxyAuthCalled; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +#ifndef QT_NO_OPENSSL + +void tst_QSslSocket_onDemandCertificates_static::onDemandRootCertLoadingStaticMethods() +{ + QString host("qt.nokia.com"); + + // not using any root certs -> should not work + QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>()); + QSslSocketPtr socket = newSocket(); + this->socket = socket; + socket->connectToHostEncrypted(host, 443); + QVERIFY(!socket->waitForEncrypted()); + + // using system root certs -> should work + QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates()); + QSslSocketPtr socket2 = newSocket(); + this->socket = socket2; + socket2->connectToHostEncrypted(host, 443); + QVERIFY(socket2->waitForEncrypted()); + + // not using any root certs again -> should not work + QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>()); + QSslSocketPtr socket3 = newSocket(); + this->socket = socket3; + socket3->connectToHostEncrypted(host, 443); + QVERIFY(!socket3->waitForEncrypted()); +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslSocket_onDemandCertificates_static) +#include "tst_qsslsocket_onDemandCertificates_static.moc" diff --git a/tests/auto/qtimer/tst_qtimer.cpp b/tests/auto/qtimer/tst_qtimer.cpp index 6cff7c53..c2acc18 100644 --- a/tests/auto/qtimer/tst_qtimer.cpp +++ b/tests/auto/qtimer/tst_qtimer.cpp @@ -50,6 +50,8 @@ #include <unistd.h> #endif +#include "../../shared/util.h" + //TESTED_CLASS= //TESTED_FILES= @@ -272,9 +274,9 @@ void tst_QTimer::livelock() QFETCH(int, interval); LiveLockTester tester(interval); QTest::qWait(180); // we have to use wait here, since we're testing timers with a non-zero timeout - QCOMPARE(tester.timeoutsForFirst, 1); + QTRY_COMPARE(tester.timeoutsForFirst, 1); QCOMPARE(tester.timeoutsForExtra, 0); - QCOMPARE(tester.timeoutsForSecond, 1); + QTRY_COMPARE(tester.timeoutsForSecond, 1); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) if (QSysInfo::WindowsVersion < QSysInfo::WV_XP) QEXPECT_FAIL("non-zero timer", "Multimedia timers are not available on Windows 2000", Continue); @@ -724,7 +726,7 @@ void tst_QTimer::QTBUG13633_dontBlockEvents() { DontBlockEvents t; QTest::qWait(60); - QVERIFY(t.total > 2); + QTRY_VERIFY(t.total > 2); } class SlotRepeater : public QObject { diff --git a/tests/benchmarks/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/benchmarks/network/ssl/qsslsocket/tst_qsslsocket.cpp index 6cb62d0..041b61a 100644 --- a/tests/benchmarks/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/benchmarks/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -46,7 +46,7 @@ #include <qsslsocket.h> -// #include "../../../../auto/network-settings.h" +#include "../../../../auto/network-settings.h" //TESTED_CLASS= //TESTED_FILES= @@ -65,6 +65,7 @@ public slots: void init(); void cleanup(); private slots: + void rootCertLoading(); void systemCaCertificates(); }; @@ -89,6 +90,16 @@ void tst_QSslSocket::cleanup() } //---------------------------------------------------------------------------------- + +void tst_QSslSocket::rootCertLoading() +{ + QBENCHMARK_ONCE { + QSslSocket socket; + socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + socket.waitForEncrypted(); + } +} + void tst_QSslSocket::systemCaCertificates() { // The results of this test change if the benchmarking system changes too much. diff --git a/tests/benchmarks/script/sunspider/tst_sunspider.cpp b/tests/benchmarks/script/sunspider/tst_sunspider.cpp index 0df19b6..9e2bb6f 100644 --- a/tests/benchmarks/script/sunspider/tst_sunspider.cpp +++ b/tests/benchmarks/script/sunspider/tst_sunspider.cpp @@ -110,7 +110,7 @@ void tst_SunSpider::benchmark_data() void tst_SunSpider::benchmark() { QFETCH(QString, testName); - QString testContents = readFile(testsDir.absoluteFilePath(testName + ".js")); + QString testContents = readFile(testsDir.filePath(testName + ".js")); QVERIFY(!testContents.isEmpty()); QScriptEngine engine; diff --git a/tests/benchmarks/script/v8/tst_v8.cpp b/tests/benchmarks/script/v8/tst_v8.cpp index 841e2f3..b9cb859 100644 --- a/tests/benchmarks/script/v8/tst_v8.cpp +++ b/tests/benchmarks/script/v8/tst_v8.cpp @@ -115,10 +115,10 @@ void tst_V8::benchmark() { QFETCH(QString, testName); - QString baseDotJsContents = readFile(testsDir.absoluteFilePath("base.js")); + QString baseDotJsContents = readFile(testsDir.filePath("base.js")); QVERIFY(!baseDotJsContents.isEmpty()); - QString testContents = readFile(testsDir.absoluteFilePath(testName + ".js")); + QString testContents = readFile(testsDir.filePath(testName + ".js")); QVERIFY(!testContents.isEmpty()); QScriptEngine engine; diff --git a/tools/designer/src/components/formeditor/qdesigner_resource.cpp b/tools/designer/src/components/formeditor/qdesigner_resource.cpp index 0426b8c..714a556 100644 --- a/tools/designer/src/components/formeditor/qdesigner_resource.cpp +++ b/tools/designer/src/components/formeditor/qdesigner_resource.cpp @@ -2278,6 +2278,7 @@ void QDesignerResource::createResources(DomResources *resources) path = core()->dialogGui()->getOpenFileName(dialogParent, fileDialogTitle, fi.absolutePath(), fileDialogPattern); if (path.isEmpty()) break; + m_formWindow->setProperty("_q_resourcepathchanged", QVariant(true)); } else { break; } diff --git a/tools/designer/src/designer/qdesigner_formwindow.cpp b/tools/designer/src/designer/qdesigner_formwindow.cpp index 1fbdcec..4770d2a 100644 --- a/tools/designer/src/designer/qdesigner_formwindow.cpp +++ b/tools/designer/src/designer/qdesigner_formwindow.cpp @@ -159,6 +159,7 @@ void QDesignerFormWindow::firstShow() if (m_editor) { connect(m_editor, SIGNAL(fileNameChanged(QString)), this, SLOT(updateWindowTitle(QString))); updateWindowTitle(m_editor->fileName()); + updateChanged(); } } show(); diff --git a/tools/designer/src/designer/qdesigner_workbench.cpp b/tools/designer/src/designer/qdesigner_workbench.cpp index 836feb7..840667a 100644 --- a/tools/designer/src/designer/qdesigner_workbench.cpp +++ b/tools/designer/src/designer/qdesigner_workbench.cpp @@ -966,7 +966,9 @@ QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName, return 0; } *uic3Converted = editor->fileName().isEmpty(); - editor->setDirty(false); + // Did user specify another (missing) resource path -> set dirty. + const bool dirty = editor->property("_q_resourcepathchanged").toBool(); + editor->setDirty(dirty); resizeForm(formWindow, editor->mainContainer()); formWindowManager->setActiveFormWindow(editor); return formWindow; |