From 2ecf32919fd3747f537df14c938d2520d240a993 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 13 Nov 2009 17:07:15 +1000 Subject: XMLHttpRequest tests --- src/declarative/qml/qmlxmlhttprequest.cpp | 47 ++++-- .../declarative/xmlhttprequest/data/document.qml | 3 + .../declarative/xmlhttprequest/data/element.qml | 22 +++ .../data/getAllResponseHeaders_args.qml | 23 +++ .../data/getAllResponseHeaders_sent.qml | 20 +++ .../data/getAllResponseHeaders_unsent.qml | 16 +++ .../xmlhttprequest/data/getResponseHeader_args.qml | 23 +++ .../xmlhttprequest/data/getResponseHeader_sent.qml | 20 +++ .../data/getResponseHeader_unsent.qml | 16 +++ .../xmlhttprequest/data/invalidMethodUsage.qml | 160 +++++++++++++++++++++ .../declarative/xmlhttprequest/data/open_user.qml | 53 +++++++ .../xmlhttprequest/data/send_data.4.qml | 1 - .../xmlhttprequest/data/send_data.7.qml | 23 +++ .../xmlhttprequest/data/setRequestHeader_args.qml | 18 +++ .../data/setRequestHeader_illegalName.qml | 2 + .../xmlhttprequest/tst_xmlhttprequest.cpp | 159 ++++++++++++++++++++ 16 files changed, 590 insertions(+), 16 deletions(-) create mode 100644 tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_args.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_sent.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_unsent.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/getResponseHeader_args.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/getResponseHeader_sent.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/getResponseHeader_unsent.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/invalidMethodUsage.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/open_user.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/send_data.7.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/setRequestHeader_args.qml diff --git a/src/declarative/qml/qmlxmlhttprequest.cpp b/src/declarative/qml/qmlxmlhttprequest.cpp index 848533c..72af43d 100644 --- a/src/declarative/qml/qmlxmlhttprequest.cpp +++ b/src/declarative/qml/qmlxmlhttprequest.cpp @@ -91,16 +91,16 @@ namespace { -class NodeImpl : public QmlRefCount +class DocumentImpl; +class NodeImpl { public: - NodeImpl() : type(Element), parent(0) {} + NodeImpl() : type(Element), document(0), parent(0) {} virtual ~NodeImpl() { - if (parent) D(parent); for (int ii = 0; ii < children.count(); ++ii) - D(children.at(ii)); + delete children.at(ii); for (int ii = 0; ii < attributes.count(); ++ii) - D(attributes.at(ii)); + delete attributes.at(ii); } // These numbers are copied from the Node IDL definition @@ -125,18 +125,22 @@ public: QString data; + void addref(); + void release(); + + DocumentImpl *document; NodeImpl *parent; QList children; QList attributes; }; -class DocumentImpl : public NodeImpl +class DocumentImpl : public QmlRefCount, public NodeImpl { public: DocumentImpl() : root(0) { type = Document; } virtual ~DocumentImpl() { - if (root) D(root); + if (root) delete root; } QString version; @@ -144,6 +148,9 @@ public: bool isStandalone; NodeImpl *root; + + void addref() { QmlRefCount::addref(); } + void release() { QmlRefCount::release(); } }; class NamedNodeMap @@ -312,6 +319,16 @@ Q_DECLARE_METATYPE(Node); Q_DECLARE_METATYPE(NodeList); Q_DECLARE_METATYPE(NamedNodeMap); +void NodeImpl::addref() +{ + A(document); +} + +void NodeImpl::release() +{ + D(document); +} + QScriptValue Node::nodeName(QScriptContext *context, QScriptEngine *engine) { Node node = qscriptvalue_cast(context->thisObject()); @@ -616,6 +633,7 @@ QScriptValue Document::load(QScriptEngine *engine, const QString &data) case QXmlStreamReader::StartDocument: Q_ASSERT(!document); document = new DocumentImpl; + document->document = document; document->version = reader.documentVersion().toString(); document->encoding = reader.documentEncoding().toString(); document->isStandalone = reader.isStandaloneDocument(); @@ -626,25 +644,25 @@ QScriptValue Document::load(QScriptEngine *engine, const QString &data) { Q_ASSERT(document); NodeImpl *node = new NodeImpl; + node->document = document; node->namespaceUri = reader.namespaceUri().toString(); node->name = reader.name().toString(); if (nodeStack.isEmpty()) { document->root = node; } else { node->parent = nodeStack.top(); - A(node->parent); node->parent->children.append(node); } nodeStack.append(node); foreach (const QXmlStreamAttribute &a, reader.attributes()) { NodeImpl *attr = new NodeImpl; + attr->document = document; attr->type = NodeImpl::Attr; attr->namespaceUri = a.namespaceUri().toString(); attr->name = a.name().toString(); attr->data = a.value().toString(); attr->parent = node; - A(attr->parent); node->attributes.append(attr); } } @@ -655,9 +673,9 @@ QScriptValue Document::load(QScriptEngine *engine, const QString &data) case QXmlStreamReader::Characters: { NodeImpl *node = new NodeImpl; + node->document = document; node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text; node->parent = nodeStack.top(); - A(node->parent); node->parent->children.append(node); node->data = reader.text().toString(); } @@ -827,8 +845,7 @@ NamedNodeMapClass::QueryFlags NamedNodeMapClass::queryProperty(const QScriptValu return 0; NamedNodeMap map = qscriptvalue_cast(object.data()); - if (map.isNull()) - return 0; + Q_ASSERT(!map.isNull()); bool ok = false; QString nameString = name.toString(); @@ -1307,7 +1324,7 @@ static QScriptValue qmlxmlhttprequest_setRequestHeader(QScriptContext *context, THROW_REFERENCE("Not an XMLHttpRequest object"); if (context->argumentCount() != 2) - THROW_SYNTAX("Incorrect argument count"); + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); if (request->readyState() != QmlXMLHttpRequest::Opened || @@ -1388,7 +1405,7 @@ static QScriptValue qmlxmlhttprequest_getResponseHeader(QScriptContext *context, THROW_REFERENCE("Not an XMLHttpRequest object"); if (context->argumentCount() != 1) - THROW_SYNTAX("Incorrect argument count"); + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); if (request->readyState() != QmlXMLHttpRequest::Loading && request->readyState() != QmlXMLHttpRequest::Done && @@ -1408,7 +1425,7 @@ static QScriptValue qmlxmlhttprequest_getAllResponseHeaders(QScriptContext *cont THROW_REFERENCE("Not an XMLHttpRequest object"); if (context->argumentCount() != 0) - THROW_SYNTAX("Incorrect argument count"); + THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); if (request->readyState() != QmlXMLHttpRequest::Loading && request->readyState() != QmlXMLHttpRequest::Done && diff --git a/tests/auto/declarative/xmlhttprequest/data/document.qml b/tests/auto/declarative/xmlhttprequest/data/document.qml index 7601a10..ce9e35f 100644 --- a/tests/auto/declarative/xmlhttprequest/data/document.qml +++ b/tests/auto/declarative/xmlhttprequest/data/document.qml @@ -25,6 +25,9 @@ Object { if (document.nodeValue != null) return; + if (document.parentNode != null) + return; + // ### Test other node properties // ### test encoding (what is a valid qt encoding?) xmlTest = true; diff --git a/tests/auto/declarative/xmlhttprequest/data/element.qml b/tests/auto/declarative/xmlhttprequest/data/element.qml index 79620bf..228db18 100644 --- a/tests/auto/declarative/xmlhttprequest/data/element.qml +++ b/tests/auto/declarative/xmlhttprequest/data/element.qml @@ -32,6 +32,15 @@ Object { if (e.childNodes[childTagNames.length + 1] != null) return; + // Check writing fails + e.childNodes[0] = null; + if (e.childNodes[0] == null) + return; + + e.childNodes[10] = 10; + if (e.childNodes[10] != null) + return; + if (e.firstChild.tagName != e.childNodes[0].tagName) return; @@ -70,6 +79,19 @@ Object { if (attrIdx2 != null) return; + // Check writing fails + e.attributes[0] = null; + if (e.attributes[0] == null) + return; + + e.attributes["attr"] = null; + if (e.attributes["attr"] == null) + return; + + e.attributes["attr3"] = 10; + if (e.attributes["attr3"] != null) + return; + // Check person and fruit sub elements if (person.parentNode.nodeName != "root") return; diff --git a/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_args.qml b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_args.qml new file mode 100644 index 0000000..c2cf898 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_args.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +Object { + property bool exceptionThrown: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", "testdocument.html"); + x.send(); + + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + try { + x.getAllResponseHeaders("Test-header"); + } catch (e) { + if (e.code == DOMException.SYNTAX_ERR) + exceptionThrown = true; + } + } + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_sent.qml b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_sent.qml new file mode 100644 index 0000000..9583f9d --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_sent.qml @@ -0,0 +1,20 @@ +import Qt 4.6 + +Object { + property bool test: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", "testdocument.html"); + x.send(); + + try { + x.getAllResponseHeaders(); + } catch (e) { + if (e.code == DOMException.INVALID_STATE_ERR) + test = true; + } + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_unsent.qml b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_unsent.qml new file mode 100644 index 0000000..fac5259 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getAllResponseHeaders_unsent.qml @@ -0,0 +1,16 @@ +import Qt 4.6 + +Object { + property bool test: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + try { + x.getAllResponseHeaders(); + } catch (e) { + if (e.code == DOMException.INVALID_STATE_ERR) + test = true; + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_args.qml b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_args.qml new file mode 100644 index 0000000..ca7aed8 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_args.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +Object { + property bool exceptionThrown: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", "testdocument.html"); + x.send(); + + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + try { + x.getResponseHeader(); + } catch (e) { + if (e.code == DOMException.SYNTAX_ERR) + exceptionThrown = true; + } + } + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_sent.qml b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_sent.qml new file mode 100644 index 0000000..148a19c --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_sent.qml @@ -0,0 +1,20 @@ +import Qt 4.6 + +Object { + property bool test: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", "testdocument.html"); + x.send(); + + try { + x.getResponseHeader("Test-header"); + } catch (e) { + if (e.code == DOMException.INVALID_STATE_ERR) + test = true; + } + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_unsent.qml b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_unsent.qml new file mode 100644 index 0000000..5abdf0a --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/getResponseHeader_unsent.qml @@ -0,0 +1,16 @@ +import Qt 4.6 + +Object { + property bool test: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + try { + x.getResponseHeader("Test-header"); + } catch (e) { + if (e.code == DOMException.INVALID_STATE_ERR) + test = true; + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/invalidMethodUsage.qml b/tests/auto/declarative/xmlhttprequest/data/invalidMethodUsage.qml new file mode 100644 index 0000000..893eb8b --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/invalidMethodUsage.qml @@ -0,0 +1,160 @@ +import Qt 4.6 + +Object { + property bool onreadystatechange: false + property bool readyState: false + property bool status: false + property bool statusText: false + property bool responseText: false + property bool responseXML: false + + property bool open: false + property bool setRequestHeader: false + property bool send: false + property bool abort: false + property bool getResponseHeader: false + property bool getAllResponseHeaders: false + + Component.onCompleted: { + var o = 10; + + try { + XMLHttpRequest.prototype.onreadystatechange + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + onreadystatechange = true; + } + try { + XMLHttpRequest.prototype.readyState + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + readyState = true; + } + try { + XMLHttpRequest.prototype.status + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + status = true; + } + try { + XMLHttpRequest.prototype.statusText + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + statusText = true; + } + try { + XMLHttpRequest.prototype.responseText + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + responseText = true; + } + try { + XMLHttpRequest.prototype.responseXML + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + responseXML = true; + } + + try { + XMLHttpRequest.prototype.open.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + open = true; + } + + try { + XMLHttpRequest.prototype.setRequestHeader.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + setRequestHeader = true; + } + + try { + XMLHttpRequest.prototype.send.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + send = true; + } + + try { + XMLHttpRequest.prototype.abort.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + abort = true; + } + + try { + XMLHttpRequest.prototype.getResponseHeader.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + getResponseHeader = true; + } + + try { + XMLHttpRequest.prototype.getAllResponseHeaders.call(o); + } catch (e) { + if (!(e instanceof ReferenceError)) + return; + + if (e.message != "Not an XMLHttpRequest object") + return; + + getAllResponseHeaders = true; + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/open_user.qml b/tests/auto/declarative/xmlhttprequest/data/open_user.qml new file mode 100644 index 0000000..cc90433 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/open_user.qml @@ -0,0 +1,53 @@ +import Qt 4.6 + +Object { + property string url + + property bool readyState: false + property bool openedState: false + + property bool status: false + property bool statusText: false + property bool responseText: false + property bool responseXML: false + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + if (x.readyState == XMLHttpRequest.UNSENT) + readyState = true; + + x.open("GET", url, true, "username", "password"); + + if (x.readyState == XMLHttpRequest.OPENED) + openedState = true; + + try { + var a = x.status; + } catch (error) { + if (error.code == DOMException.INVALID_STATE_ERR) + status = true; + } + try { + var a = x.statusText; + } catch (error) { + if (error.code == DOMException.INVALID_STATE_ERR) + statusText = true; + } + responseText = (x.responseText == ""); + responseXML = (x.responseXML == null); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText == "QML Rocks!\n"); + } + } + + + x.send() + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/send_data.4.qml b/tests/auto/declarative/xmlhttprequest/data/send_data.4.qml index 375f2fa..4705007 100644 --- a/tests/auto/declarative/xmlhttprequest/data/send_data.4.qml +++ b/tests/auto/declarative/xmlhttprequest/data/send_data.4.qml @@ -21,4 +21,3 @@ Object { } } - diff --git a/tests/auto/declarative/xmlhttprequest/data/send_data.7.qml b/tests/auto/declarative/xmlhttprequest/data/send_data.7.qml new file mode 100644 index 0000000..3a2ba56 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/send_data.7.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +Object { + property string url + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("POST", url); + x.setRequestHeader("Content-Type", "text/plain"); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText == "QML Rocks!\n"); + } + } + + x.send("My Sent Data"); + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_args.qml b/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_args.qml new file mode 100644 index 0000000..6824af2 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_args.qml @@ -0,0 +1,18 @@ +import Qt 4.6 + +Object { + property bool exceptionThrown: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", "testdocument.html"); + + try { + x.setRequestHeader("Test-header"); + } catch (e) { + if (e.code == DOMException.SYNTAX_ERR) + exceptionThrown = true; + } + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_illegalName.qml b/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_illegalName.qml index cf5ebcc..8029bc4 100644 --- a/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_illegalName.qml +++ b/tests/auto/declarative/xmlhttprequest/data/setRequestHeader_illegalName.qml @@ -22,6 +22,8 @@ Object { x.open("GET", url); + x.setRequestHeader(header, "Value"); + if (x.readyState == XMLHttpRequest.OPENED) openedState = true; diff --git a/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp b/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp index 100a11b..e74d1c5 100644 --- a/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp +++ b/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp @@ -71,6 +71,7 @@ private slots: void setRequestHeader_illegalName_data(); void setRequestHeader_illegalName(); void setRequestHeader_sent(); + void setRequestHeader_args(); void send_unsent(); void send_alreadySent(); void send_ignoreData(); @@ -79,7 +80,13 @@ private slots: void abort_unsent(); void abort_opened(); void getResponseHeader(); + void getResponseHeader_unsent(); + void getResponseHeader_sent(); + void getResponseHeader_args(); void getAllResponseHeaders(); + void getAllResponseHeaders_unsent(); + void getAllResponseHeaders_sent(); + void getAllResponseHeaders_args(); void status(); void statusText(); void responseText(); @@ -283,6 +290,34 @@ void tst_xmlhttprequest::open() delete object; } + + // User/pass + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + QVERIFY(server.wait(TEST_FILE("open_network.expect"), + TEST_FILE("open_network.reply"), + TEST_FILE("testdocument.html"))); + + QmlComponent component(&engine, TEST_FILE("open_user.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); + component.completeCreate(); + + QCOMPARE(object->property("readyState").toBool(), true); + QCOMPARE(object->property("openedState").toBool(), true); + QCOMPARE(object->property("status").toBool(), true); + QCOMPARE(object->property("statusText").toBool(), true); + QCOMPARE(object->property("responseText").toBool(), true); + QCOMPARE(object->property("responseXML").toBool(), true); + + TRY_WAIT(object->property("dataOK").toBool() == true); + + // ### Check that the username/password were sent to the server + + delete object; + } } // Test that calling XMLHttpRequest.open() with an invalid method raises an exception @@ -445,6 +480,18 @@ void tst_xmlhttprequest::setRequestHeader_sent() delete object; } +// Invalid arg count throws exception +void tst_xmlhttprequest::setRequestHeader_args() +{ + QmlComponent component(&engine, TEST_FILE("setRequestHeader_args.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("exceptionThrown").toBool(), true); + + delete object; +} + // Test that calling send() in UNSENT state throws an exception void tst_xmlhttprequest::send_unsent() { @@ -628,6 +675,25 @@ void tst_xmlhttprequest::send_withdata() delete object; } + + // Correct content-type - no charset + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + QVERIFY(server.wait(TEST_FILE("send_data.1.expect"), + TEST_FILE("send_data.reply"), + TEST_FILE("testdocument.html"))); + + QmlComponent component(&engine, TEST_FILE("send_data.7.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); + component.completeCreate(); + + TRY_WAIT(object->property("dataOK").toBool() == true); + + delete object; + } } // Test abort() has no effect in unsent state @@ -736,6 +802,42 @@ void tst_xmlhttprequest::getResponseHeader() delete object; } +// Test getResponseHeader throws an exception in an invalid state +void tst_xmlhttprequest::getResponseHeader_unsent() +{ + QmlComponent component(&engine, TEST_FILE("getResponseHeader_unsent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Test getResponseHeader throws an exception in an invalid state +void tst_xmlhttprequest::getResponseHeader_sent() +{ + QmlComponent component(&engine, TEST_FILE("getResponseHeader_sent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Invalid arg count throws exception +void tst_xmlhttprequest::getResponseHeader_args() +{ + QmlComponent component(&engine, TEST_FILE("getResponseHeader_args.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + TRY_WAIT(object->property("exceptionThrown").toBool() == true); + + delete object; +} + void tst_xmlhttprequest::getAllResponseHeaders() { QmlEngine engine; // Avoid cookie contamination @@ -768,6 +870,42 @@ void tst_xmlhttprequest::getAllResponseHeaders() delete object; } +// Test getAllResponseHeaders throws an exception in an invalid state +void tst_xmlhttprequest::getAllResponseHeaders_unsent() +{ + QmlComponent component(&engine, TEST_FILE("getAllResponseHeaders_unsent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Test getAllResponseHeaders throws an exception in an invalid state +void tst_xmlhttprequest::getAllResponseHeaders_sent() +{ + QmlComponent component(&engine, TEST_FILE("getAllResponseHeaders_sent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Invalid arg count throws exception +void tst_xmlhttprequest::getAllResponseHeaders_args() +{ + QmlComponent component(&engine, TEST_FILE("getAllResponseHeaders_args.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + TRY_WAIT(object->property("exceptionThrown").toBool() == true); + + delete object; +} + void tst_xmlhttprequest::status() { { @@ -966,8 +1104,29 @@ void tst_xmlhttprequest::responseText() } } +// Test that calling hte XMLHttpRequest methods on a non-XMLHttpRequest object +// throws an exception void tst_xmlhttprequest::invalidMethodUsage() { + QmlComponent component(&engine, TEST_FILE("invalidMethodUsage.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("onreadystatechange").toBool(), true); + QCOMPARE(object->property("readyState").toBool(), true); + QCOMPARE(object->property("status").toBool(), true); + QCOMPARE(object->property("statusText").toBool(), true); + QCOMPARE(object->property("responseText").toBool(), true); + QCOMPARE(object->property("responseXML").toBool(), true); + + QCOMPARE(object->property("open").toBool(), true); + QCOMPARE(object->property("setRequestHeader").toBool(), true); + QCOMPARE(object->property("send").toBool(), true); + QCOMPARE(object->property("abort").toBool(), true); + QCOMPARE(object->property("getResponseHeader").toBool(), true); + QCOMPARE(object->property("getAllResponseHeaders").toBool(), true); + + delete object; } void tst_xmlhttprequest::responseXML_invalid() -- cgit v0.12 From cbb41abbf76b7fc1b1821a0246076aa88cc4327f Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 13 Nov 2009 17:07:58 +1000 Subject: Don't enable gcov flags --- src/declarative/declarative.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index 62ae289..da8434f 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -9,8 +9,8 @@ solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui QtXml -QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors -LIBS += -lgcov +# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors +# LIBS += -lgcov INCLUDEPATH += ../../include/QtDeclarative -- cgit v0.12 From 1e28371e8cd568292bb75667e7a2b19fd7953748 Mon Sep 17 00:00:00 2001 From: Andreas Aardal Hanssen Date: Fri, 13 Nov 2009 12:21:48 +0100 Subject: Build on non-g++ compilers. Uncommenting these lines might have been a mistake during the merge (sha1 14821863b830b85ccb29df217a52f1ca52c9c421). Reviewed-by: kkoehne --- src/declarative/declarative.pro | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index 62ae289..5c74314 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -9,8 +9,10 @@ solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui QtXml -QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors -LIBS += -lgcov +# *-g++* { +# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors +# LIBS += -lgcov +# } INCLUDEPATH += ../../include/QtDeclarative -- cgit v0.12 From 946eab3e327bddcce10ed838462c0ca56ff011f0 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 13 Nov 2009 13:44:40 +0100 Subject: Avoid clipping last pixel of text in QML in some cases We would floor the width of the text layout and clip the output to this, hence cutting away the last pixel of the text in cases where the text width was not pixel aligned. Task-number: QTBUG-5431 Reviewed-by: Gunnar --- src/declarative/graphicsitems/qmlgraphicstext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/declarative/graphicsitems/qmlgraphicstext.cpp b/src/declarative/graphicsitems/qmlgraphicstext.cpp index 504eb2a..d0aec8d 100644 --- a/src/declarative/graphicsitems/qmlgraphicstext.cpp +++ b/src/declarative/graphicsitems/qmlgraphicstext.cpp @@ -50,6 +50,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE QML_DEFINE_TYPE(Qt,4,6,Text,QmlGraphicsText) @@ -600,7 +601,7 @@ QSize QmlGraphicsTextPrivate::setupTextLayout(QTextLayout *layout) line.setPosition(QPointF(0, height)); height += int(line.height()); } - return QSize((int)widthUsed, height); + return QSize(qCeil(widthUsed), height); } QPixmap QmlGraphicsTextPrivate::wrappedTextImage(bool drawStyle) -- cgit v0.12 From 48738dd945f8eb3112ce295c37a67632121020e7 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Mon, 16 Nov 2009 11:53:49 +1000 Subject: QmlEngine tests --- src/declarative/qml/qmlengine.cpp | 16 +- tests/auto/declarative/declarative.pro | 1 + tests/auto/declarative/qmlengine/qmlengine.pro | 8 + tests/auto/declarative/qmlengine/tst_qmlengine.cpp | 176 +++++++++++++++++++++ 4 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 tests/auto/declarative/qmlengine/qmlengine.pro create mode 100644 tests/auto/declarative/qmlengine/tst_qmlengine.cpp diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index b6f3bde..66d4990 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -427,22 +427,16 @@ QmlContext *QmlEngine::contextForObject(const QObject *object) */ void QmlEngine::setContextForObject(QObject *object, QmlContext *context) { - QObjectPrivate *priv = QObjectPrivate::get(object); - - QmlDeclarativeData *data = - static_cast(priv->declarativeData); + if (!object || !context) + return; - if (data && data->context) { + QmlDeclarativeData *data = QmlDeclarativeData::get(object, true); + if (data->context) { qWarning("QmlEngine::setContextForObject(): Object already has a QmlContext"); return; } - if (!data) { - priv->declarativeData = new QmlDeclarativeData(context); - } else { - data->context = context; - } - + data->context = context; context->d_func()->contextObjects.append(object); } diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index bcf908d..ec2c7d0 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -18,6 +18,7 @@ SUBDIRS += \ qmldom \ # Cover qmleasefollow \ # Cover qmlecmascript \ # Cover + qmlengine \ # Cover qmlerror \ # Cover qmlfontloader \ # Cover qmlgraphicsborderimage \ # Cover diff --git a/tests/auto/declarative/qmlengine/qmlengine.pro b/tests/auto/declarative/qmlengine/qmlengine.pro new file mode 100644 index 0000000..21d55a4 --- /dev/null +++ b/tests/auto/declarative/qmlengine/qmlengine.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative webkit network +macx:CONFIG -= app_bundle + +SOURCES += tst_qmlengine.cpp + +# Define SRCDIR equal to test's source directory +DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/auto/declarative/qmlengine/tst_qmlengine.cpp b/tests/auto/declarative/qmlengine/tst_qmlengine.cpp new file mode 100644 index 0000000..ef1eee5 --- /dev/null +++ b/tests/auto/declarative/qmlengine/tst_qmlengine.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 +#include +#include +#include +#include +#include +#include +#include + +class tst_qmlengine : public QObject +{ + Q_OBJECT +public: + tst_qmlengine() {} + +private slots: + void rootContext(); + void networkAccessManager(); + void baseUrl(); + void contextForObject(); + void offlineStoragePath(); +}; + +void tst_qmlengine::rootContext() +{ + QmlEngine engine; + + QVERIFY(engine.rootContext()); + + QCOMPARE(engine.rootContext()->engine(), &engine); + QVERIFY(engine.rootContext()->parentContext() == 0); +} + +void tst_qmlengine::networkAccessManager() +{ + QmlEngine *engine = new QmlEngine; + + // Test QmlEngine created manager + QPointer manager = engine->networkAccessManager(); + QVERIFY(manager != 0); + + // Test non-QmlEngine owner manager + QNetworkAccessManager localManager; + engine->setNetworkAccessManager(&localManager); + QVERIFY(manager == 0); + QVERIFY(engine->networkAccessManager() == &localManager); + + // Test QmlEngine owned manager + QPointer ownedManager = new QNetworkAccessManager(engine); + QVERIFY(ownedManager != 0); + engine->setNetworkAccessManager(ownedManager); + QVERIFY(engine->networkAccessManager() == ownedManager); + engine->setNetworkAccessManager(&localManager); + QVERIFY(ownedManager == 0); + QVERIFY(engine->networkAccessManager() == &localManager); + + // Test setting a null manager + engine->setNetworkAccessManager(0); + QVERIFY(engine->networkAccessManager() != 0); +} + +void tst_qmlengine::baseUrl() +{ + QmlEngine engine; + + QUrl cwd = QUrl::fromLocalFile(QDir::currentPath() + QDir::separator()); + + QCOMPARE(engine.baseUrl(), cwd); + QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd.resolved(QUrl("main.qml"))); + + QDir dir = QDir::current(); + dir.cdUp(); + QVERIFY(dir != QDir::current()); + QDir::setCurrent(dir.path()); + QVERIFY(QDir::current() == dir); + + QUrl cwd2 = QUrl::fromLocalFile(QDir::currentPath() + QDir::separator()); + QCOMPARE(engine.baseUrl(), cwd2); + QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd2.resolved(QUrl("main.qml"))); + + engine.setBaseUrl(cwd); + QCOMPARE(engine.baseUrl(), cwd); + QCOMPARE(engine.rootContext()->resolvedUrl(QUrl("main.qml")), cwd.resolved(QUrl("main.qml"))); +} + +void tst_qmlengine::contextForObject() +{ + QmlEngine *engine = new QmlEngine; + + // Test null-object + QVERIFY(QmlEngine::contextForObject(0) == 0); + + // Test an object with no context + QObject object; + QVERIFY(QmlEngine::contextForObject(&object) == 0); + + // Test setting null-object + QmlEngine::setContextForObject(0, engine->rootContext()); + + // Test setting null-context + QmlEngine::setContextForObject(&object, 0); + + // Test setting context + QmlEngine::setContextForObject(&object, engine->rootContext()); + QVERIFY(QmlEngine::contextForObject(&object) == engine->rootContext()); + + QmlContext context(engine->rootContext()); + + // Try changing context + QTest::ignoreMessage(QtWarningMsg, "QmlEngine::setContextForObject(): Object already has a QmlContext"); + QmlEngine::setContextForObject(&object, &context); + QVERIFY(QmlEngine::contextForObject(&object) == engine->rootContext()); + + // Delete context + delete engine; engine = 0; + QVERIFY(QmlEngine::contextForObject(&object) == 0); +} + +void tst_qmlengine::offlineStoragePath() +{ + QmlEngine engine; + + QDir dir(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + dir.cd("QML"); + dir.cd("OfflineStorage"); + + QCOMPARE(engine.offlineStoragePath(), dir.path()); + + engine.setOfflineStoragePath(QDir::homePath()); + QCOMPARE(engine.offlineStoragePath(), QDir::homePath()); +} + +QTEST_MAIN(tst_qmlengine) + +#include "tst_qmlengine.moc" -- cgit v0.12 From 66eb8e1a135f5ac54be2ea088bae9c82708c4258 Mon Sep 17 00:00:00 2001 From: Bill King Date: Mon, 16 Nov 2009 13:25:17 +1000 Subject: Update documentation to display slashes. Extra slashes needed for dos dir separators. --- doc/src/declarative/network.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/declarative/network.qdoc b/doc/src/declarative/network.qdoc index a81eb0f..4ed5ca2 100644 --- a/doc/src/declarative/network.qdoc +++ b/doc/src/declarative/network.qdoc @@ -91,8 +91,8 @@ Image { \endqml The \l Image source property will be assigned \tt{http://example.com/mystuff/images/logo.png}, -but while the QML is being developed, in say \tt C:\User\Fred\Documents\MyStuff\test.qml, it will be assigned -\tt C:\User\Fred\Documents\MyStuff\images\logo.png. +but while the QML is being developed, in say \tt C:\\User\\Fred\\Documents\\MyStuff\\test.qml, it will be assigned +\tt C:\\User\\Fred\\Documents\\MyStuff\\images\\logo.png. If the string assigned to a URL is already an absolute URL, then "resolving" does not change it and the URL is assigned directly. -- cgit v0.12 From efb25bac3131b4ca39a44a16526d88c814986d60 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Mon, 16 Nov 2009 13:50:00 +1000 Subject: Autotests. --- src/declarative/util/qmlstate.cpp | 7 +++++-- .../declarative/qmlbinding/data/test-binding.qml | 2 +- tests/auto/declarative/qmlbinding/tst_qmlbinding.cpp | 6 ++++++ .../qmlsystempalette/tst_qmlsystempalette.cpp | 2 ++ tests/auto/declarative/states/data/explicit.qml | 1 + tests/auto/declarative/states/tst_states.cpp | 20 ++++++++++++++++++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/declarative/util/qmlstate.cpp b/src/declarative/util/qmlstate.cpp index 7cc548f..1f5dbad 100644 --- a/src/declarative/util/qmlstate.cpp +++ b/src/declarative/util/qmlstate.cpp @@ -433,8 +433,11 @@ void QmlState::apply(QmlStateGroup *group, QmlTransition *trans, QmlState *rever // Output for debugging if (stateChangeDebug()) { foreach(const Action &action, applyList) { - qWarning() << " Action:" << action.property.object() - << action.property.name() << action.toValue; + if (action.event) + qWarning() << " Action event:" << action.event; + else + qWarning() << " Action:" << action.property.object() + << action.property.name() << action.toValue; } } diff --git a/tests/auto/declarative/qmlbinding/data/test-binding.qml b/tests/auto/declarative/qmlbinding/data/test-binding.qml index d2a22f5..e9101e4 100644 --- a/tests/auto/declarative/qmlbinding/data/test-binding.qml +++ b/tests/auto/declarative/qmlbinding/data/test-binding.qml @@ -10,7 +10,7 @@ Rectangle { Rectangle { id: r1; width: 1; height: 1; color: "yellow" } Rectangle { id: r2; width: 1; height: 1; color: "red" } - Binding { target: screen; property: "text"; value: s1.text } + Binding { target: screen; property: "text"; value: s1.text; objectName: "binding1" } Binding { target: screen; property: "color"; value: r1.color } Binding { target: screen; property: "color"; when: screen.changeColor == true; value: r2.color } } diff --git a/tests/auto/declarative/qmlbinding/tst_qmlbinding.cpp b/tests/auto/declarative/qmlbinding/tst_qmlbinding.cpp index e5aacc7..0a8508a 100644 --- a/tests/auto/declarative/qmlbinding/tst_qmlbinding.cpp +++ b/tests/auto/declarative/qmlbinding/tst_qmlbinding.cpp @@ -77,6 +77,12 @@ void tst_qmlbinding::binding() rect->setProperty("changeColor", true); QCOMPARE(rect->color(), QColor("red")); + QmlBind *binding = qobject_cast(rect->findChild("binding1")); + QVERIFY(binding != 0); + QCOMPARE(binding->object(), rect); + QCOMPARE(binding->property(), QLatin1String("text")); + QCOMPARE(binding->value().toString(), QLatin1String("Hello")); + delete rect; } diff --git a/tests/auto/declarative/qmlsystempalette/tst_qmlsystempalette.cpp b/tests/auto/declarative/qmlsystempalette/tst_qmlsystempalette.cpp index 2648463..008dfb3 100644 --- a/tests/auto/declarative/qmlsystempalette/tst_qmlsystempalette.cpp +++ b/tests/auto/declarative/qmlsystempalette/tst_qmlsystempalette.cpp @@ -103,6 +103,7 @@ void tst_qmlsystempalette::inactivePalette() QmlSystemPalette *object = qobject_cast(component.create()); QVERIFY(object != 0); + QVERIFY(object->colorGroup() == QmlSystemPalette::Inactive); QPalette palette; palette.setCurrentColorGroup(QPalette::Inactive); @@ -131,6 +132,7 @@ void tst_qmlsystempalette::disabledPalette() QmlSystemPalette *object = qobject_cast(component.create()); QVERIFY(object != 0); + QVERIFY(object->colorGroup() == QmlSystemPalette::Disabled); QPalette palette; palette.setCurrentColorGroup(QPalette::Disabled); diff --git a/tests/auto/declarative/states/data/explicit.qml b/tests/auto/declarative/states/data/explicit.qml index 271115a..ca7e274 100644 --- a/tests/auto/declarative/states/data/explicit.qml +++ b/tests/auto/declarative/states/data/explicit.qml @@ -7,6 +7,7 @@ Rectangle { states: State { name: "blue" PropertyChanges { + objectName: "changes" target: MyRectangle; explicit: true color: sourceColor } diff --git a/tests/auto/declarative/states/tst_states.cpp b/tests/auto/declarative/states/tst_states.cpp index 3d8f303..fe90191 100644 --- a/tests/auto/declarative/states/tst_states.cpp +++ b/tests/auto/declarative/states/tst_states.cpp @@ -42,6 +42,7 @@ #include #include #include +#include class tst_states : public QObject { @@ -61,6 +62,7 @@ private slots: void script(); void restoreEntryValues(); void explicitChanges(); + void propertyErrors(); }; void tst_states::basicChanges() @@ -544,6 +546,10 @@ void tst_states::explicitChanges() QmlGraphicsRectangle *rect = qobject_cast(rectComponent.create()); QVERIFY(rect != 0); + QmlPropertyChanges *changes = qobject_cast(rect->findChild("changes")); + QVERIFY(changes != 0); + QVERIFY(changes->isExplicit()); + QCOMPARE(rect->color(),QColor("red")); rect->setState("blue"); @@ -561,6 +567,20 @@ void tst_states::explicitChanges() QCOMPARE(rect->color(),QColor("yellow")); } +void tst_states::propertyErrors() +{ + QmlEngine engine; + QmlComponent rectComponent(&engine, SRCDIR "/data/propertyErrors.qml"); + QmlGraphicsRectangle *rect = qobject_cast(rectComponent.create()); + QVERIFY(rect != 0); + + QCOMPARE(rect->color(),QColor("red")); + + QTest::ignoreMessage(QtWarningMsg, "QML QmlPropertyChanges (file://" SRCDIR "/data/propertyErrors.qml:8:9) Cannot assign to non-existant property \"colr\""); + QTest::ignoreMessage(QtWarningMsg, "QML QmlPropertyChanges (file://" SRCDIR "/data/propertyErrors.qml:8:9) Cannot assign to read-only property \"wantsFocus\""); + rect->setState("blue"); +} + QTEST_MAIN(tst_states) #include "tst_states.moc" -- cgit v0.12