Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativexmlhttprequest / tst_qdeclarativexmlhttprequest.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <qtest.h>
43 #include <QDeclarativeEngine>
44 #include <QDeclarativeComponent>
45 #include <QDebug>
46 #include <QScopedPointer>
47 #include <QNetworkCookieJar>
48 #include "testhttpserver.h"
49 #include "../../shared/util.h"
50
51 #define SERVER_PORT 14445
52
53 class tst_qdeclarativexmlhttprequest : public QDeclarativeDataTest
54 {
55     Q_OBJECT
56 public:
57     tst_qdeclarativexmlhttprequest() {}
58
59 private slots:
60     void domExceptionCodes();
61     void callbackException();
62     void callbackException_data();
63     void staticStateValues();
64     void instanceStateValues();
65     void constructor();
66     void defaultState();
67     void open();
68     void open_data();
69     void open_invalid_method();
70     void open_sync();
71     void open_arg_count();
72     void setRequestHeader();
73     void setRequestHeader_caseInsensitive();
74     void setRequestHeader_unsent();
75     void setRequestHeader_illegalName_data();
76     void setRequestHeader_illegalName();
77     void setRequestHeader_sent();
78     void setRequestHeader_args();
79     void send_unsent();
80     void send_alreadySent();
81     void send_ignoreData();
82     void send_withdata();
83     void send_withdata_data();
84     void abort();
85     void abort_unsent();
86     void abort_opened();
87     void getResponseHeader();
88     void getResponseHeader_unsent();
89     void getResponseHeader_sent();
90     void getResponseHeader_args();
91     void getAllResponseHeaders();
92     void getAllResponseHeaders_unsent();
93     void getAllResponseHeaders_sent();
94     void getAllResponseHeaders_args();
95     void status();
96     void status_data();
97     void statusText();
98     void statusText_data();
99     void responseText();
100     void responseText_data();
101     void responseXML_invalid();
102     void invalidMethodUsage();
103     void redirects();
104     void nonUtf8();
105     void nonUtf8_data();
106
107     // Attributes
108     void document();
109     void element();
110     void attr();
111     void text();
112     void cdata();
113
114     // Crashes
115     // void outstanding_request_at_shutdown();
116
117     // void network_errors()
118     // void readyState()
119
120 private:
121     QDeclarativeEngine engine;
122 };
123
124 // Test that the dom exception codes are correct
125 void tst_qdeclarativexmlhttprequest::domExceptionCodes()
126 {
127     QDeclarativeComponent component(&engine, testFileUrl("domExceptionCodes.qml"));
128     QObject *object = component.create();
129     QVERIFY(object != 0);
130
131     QCOMPARE(object->property("index_size_err").toInt(), 1);
132     QCOMPARE(object->property("domstring_size_err").toInt(), 2);
133     QCOMPARE(object->property("hierarchy_request_err").toInt(), 3);
134     QCOMPARE(object->property("wrong_document_err").toInt(), 4);
135     QCOMPARE(object->property("invalid_character_err").toInt(), 5);
136     QCOMPARE(object->property("no_data_allowed_err").toInt(), 6);
137     QCOMPARE(object->property("no_modification_allowed_err").toInt(), 7);
138     QCOMPARE(object->property("not_found_err").toInt(), 8);
139     QCOMPARE(object->property("not_supported_err").toInt(), 9);
140     QCOMPARE(object->property("inuse_attribute_err").toInt(), 10);
141     QCOMPARE(object->property("invalid_state_err").toInt(), 11);
142     QCOMPARE(object->property("syntax_err").toInt(), 12);
143     QCOMPARE(object->property("invalid_modification_err").toInt(), 13);
144     QCOMPARE(object->property("namespace_err").toInt(), 14);
145     QCOMPARE(object->property("invalid_access_err").toInt(), 15);
146     QCOMPARE(object->property("validation_err").toInt(), 16);
147     QCOMPARE(object->property("type_mismatch_err").toInt(), 17);
148
149     delete object;
150 }
151
152 void tst_qdeclarativexmlhttprequest::callbackException_data()
153 {
154     QTest::addColumn<QString>("which");
155     QTest::addColumn<int>("line");
156
157     QTest::newRow("on-opened") << "1" << 15;
158     QTest::newRow("on-loading") << "3" << 15;
159     QTest::newRow("on-done") << "4" << 15;
160 }
161
162 void tst_qdeclarativexmlhttprequest::callbackException()
163 {
164     // Test exception reporting for exceptions thrown at various points.
165
166     QFETCH(QString, which);
167     QFETCH(int, line);
168     
169     QString expect = testFileUrl("callbackException.qml").toString() + ":"+QString::number(line)+": Error: Exception from Callback";
170     QTest::ignoreMessage(QtWarningMsg, expect.toLatin1());
171
172     QDeclarativeComponent component(&engine, testFileUrl("callbackException.qml"));
173     QObject *object = component.beginCreate(engine.rootContext());
174     QVERIFY(object != 0);
175     object->setProperty("url", "testdocument.html");
176     object->setProperty("which", which);
177     component.completeCreate();
178
179     QTRY_VERIFY(object->property("threw").toBool() == true);
180
181     delete object;
182 }
183
184 // Test that the state value properties on the XMLHttpRequest constructor have the correct values.
185 // ### WebKit does not do this, but it seems to fit the standard and QML better
186 void tst_qdeclarativexmlhttprequest::staticStateValues()
187 {
188     QDeclarativeComponent component(&engine, testFileUrl("staticStateValues.qml"));
189     QObject *object = component.create();
190     QVERIFY(object != 0);
191
192     QCOMPARE(object->property("unsent").toInt(), 0);
193     QCOMPARE(object->property("opened").toInt(), 1);
194     QCOMPARE(object->property("headers_received").toInt(), 2);
195     QCOMPARE(object->property("loading").toInt(), 3);
196     QCOMPARE(object->property("done").toInt(), 4);
197
198     delete object;
199 }
200
201 // Test that the state value properties on instances have the correct values.
202 void tst_qdeclarativexmlhttprequest::instanceStateValues()
203 {
204     QDeclarativeComponent component(&engine, testFileUrl("instanceStateValues.qml"));
205     QObject *object = component.create();
206     QVERIFY(object != 0);
207
208     QCOMPARE(object->property("unsent").toInt(), 0);
209     QCOMPARE(object->property("opened").toInt(), 1);
210     QCOMPARE(object->property("headers_received").toInt(), 2);
211     QCOMPARE(object->property("loading").toInt(), 3);
212     QCOMPARE(object->property("done").toInt(), 4);
213
214     delete object;
215 }
216
217 // Test calling constructor 
218 void tst_qdeclarativexmlhttprequest::constructor()
219 {
220     QDeclarativeComponent component(&engine, testFileUrl("constructor.qml"));
221     QObject *object = component.create();
222     QVERIFY(object != 0);
223
224     QCOMPARE(object->property("calledAsConstructor").toBool(), true);
225     QCOMPARE(object->property("calledAsFunction").toBool(), true);
226
227     delete object;
228 }
229
230 // Test that all the properties are set correctly before any request is sent
231 void tst_qdeclarativexmlhttprequest::defaultState()
232 {
233     QDeclarativeComponent component(&engine, testFileUrl("defaultState.qml"));
234     QObject *object = component.create();
235     QVERIFY(object != 0);
236
237     QCOMPARE(object->property("readState").toInt(), 0);
238     QCOMPARE(object->property("statusIsException").toBool(), true);
239     QCOMPARE(object->property("statusTextIsException").toBool(), true);
240     QCOMPARE(object->property("responseText").toString(), QString());
241     QCOMPARE(object->property("responseXMLIsNull").toBool(), true);
242
243     delete object;
244 }
245
246 // Test valid XMLHttpRequest.open() calls
247 void tst_qdeclarativexmlhttprequest::open()
248 {
249     QFETCH(QUrl, qmlFile);
250     QFETCH(QString, url);
251     QFETCH(bool, remote);
252
253     QScopedPointer<TestHTTPServer> server; // ensure deletion in case test fails
254     if (remote) {
255         server.reset(new TestHTTPServer(SERVER_PORT));
256         QVERIFY(server->isValid());
257         QVERIFY(server->wait(testFileUrl("open_network.expect"),
258                              testFileUrl("open_network.reply"),
259                              testFileUrl("testdocument.html")));
260     }
261
262     QDeclarativeComponent component(&engine, qmlFile);
263     QObject *object = component.beginCreate(engine.rootContext());
264     QVERIFY(object != 0);
265     object->setProperty("url", url);
266     component.completeCreate();
267
268     QCOMPARE(object->property("readyState").toBool(), true);
269     QCOMPARE(object->property("openedState").toBool(), true);
270     QCOMPARE(object->property("status").toBool(), true);
271     QCOMPARE(object->property("statusText").toBool(), true);
272     QCOMPARE(object->property("responseText").toBool(), true);
273     QCOMPARE(object->property("responseXML").toBool(), true);
274
275     QTRY_VERIFY(object->property("dataOK").toBool() == true);
276
277     delete object;
278 }
279
280 void tst_qdeclarativexmlhttprequest::open_data()
281 {
282     QTest::addColumn<QUrl>("qmlFile");
283     QTest::addColumn<QString>("url");
284     QTest::addColumn<bool>("remote");
285
286     QTest::newRow("Relative url)") << testFileUrl("open.qml") << "testdocument.html" << false;
287     QTest::newRow("Absolute url)") << testFileUrl("open.qml") << testFileUrl("testdocument.html").toString() << false;
288     QTest::newRow("Absolute network url)") << testFileUrl("open.qml") << "http://127.0.0.1:14445/testdocument.html" << true;
289
290     // ### Check that the username/password were sent to the server
291     QTest::newRow("User/pass") << testFileUrl("open_user.qml") << "http://127.0.0.1:14445/testdocument.html" << true;
292 }
293
294 // Test that calling XMLHttpRequest.open() with an invalid method raises an exception
295 void tst_qdeclarativexmlhttprequest::open_invalid_method()
296 {
297     QDeclarativeComponent component(&engine, testFileUrl("open_invalid_method.qml"));
298     QObject *object = component.create();
299     QVERIFY(object != 0);
300
301     QCOMPARE(object->property("exceptionThrown").toBool(), true);
302
303     delete object;
304 }
305
306 // Test that calling XMLHttpRequest.open() with sync raises an exception
307 void tst_qdeclarativexmlhttprequest::open_sync()
308 {
309     QDeclarativeComponent component(&engine, testFileUrl("open_sync.qml"));
310     QObject *object = component.create();
311     QVERIFY(object != 0);
312
313     QCOMPARE(object->property("exceptionThrown").toBool(), true);
314
315     delete object;
316 }
317
318 // Calling with incorrect arg count raises an exception
319 void tst_qdeclarativexmlhttprequest::open_arg_count()
320 {
321     {
322         QDeclarativeComponent component(&engine, testFileUrl("open_arg_count.1.qml"));
323         QObject *object = component.create();
324         QVERIFY(object != 0);
325
326         QCOMPARE(object->property("exceptionThrown").toBool(), true);
327
328         delete object;
329     }
330
331     {
332         QDeclarativeComponent component(&engine, testFileUrl("open_arg_count.2.qml"));
333         QObject *object = component.create();
334         QVERIFY(object != 0);
335
336         QCOMPARE(object->property("exceptionThrown").toBool(), true);
337
338         delete object;
339     }
340 }
341
342 // Test valid setRequestHeader() calls
343 void tst_qdeclarativexmlhttprequest::setRequestHeader()
344 {
345     TestHTTPServer server(SERVER_PORT);
346     QVERIFY(server.isValid());
347     QVERIFY(server.wait(testFileUrl("setRequestHeader.expect"),
348                         testFileUrl("setRequestHeader.reply"),
349                         testFileUrl("testdocument.html")));
350
351     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader.qml"));
352     QObject *object = component.beginCreate(engine.rootContext());
353     QVERIFY(object != 0);
354     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
355     component.completeCreate();
356
357     QTRY_VERIFY(object->property("dataOK").toBool() == true);
358
359     delete object;
360 }
361
362 // Test valid setRequestHeader() calls with different header cases
363 void tst_qdeclarativexmlhttprequest::setRequestHeader_caseInsensitive()
364 {
365     TestHTTPServer server(SERVER_PORT);
366     QVERIFY(server.isValid());
367     QVERIFY(server.wait(testFileUrl("setRequestHeader.expect"),
368                         testFileUrl("setRequestHeader.reply"),
369                         testFileUrl("testdocument.html")));
370
371     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader_caseInsensitive.qml"));
372     QObject *object = component.beginCreate(engine.rootContext());
373     QVERIFY(object != 0);
374     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
375     component.completeCreate();
376
377     QTRY_VERIFY(object->property("dataOK").toBool() == true);
378
379     delete object;
380 }
381 // Test setting headers before open() throws exception
382 void tst_qdeclarativexmlhttprequest::setRequestHeader_unsent()
383 {
384     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader_unsent.qml"));
385     QObject *object = component.create();
386     QVERIFY(object != 0);
387
388     QCOMPARE(object->property("test").toBool(), true);
389
390     delete object;
391 }
392
393 void tst_qdeclarativexmlhttprequest::setRequestHeader_illegalName_data()
394 {
395     QTest::addColumn<QString>("name");
396
397     QTest::newRow("Accept-Charset") << "AccePT-CHArset";
398     QTest::newRow("Accept-Encoding") << "AccEpt-EnCOding";
399     QTest::newRow("Connection") << "ConnECtion";
400     QTest::newRow("Content-Length") << "ContEnt-LenGth";
401     QTest::newRow("Cookie") << "CookIe";
402     QTest::newRow("Cookie2") << "CoOkie2";
403     QTest::newRow("Content-Transfer-Encoding") << "ConteNT-tRANSFER-eNCOding";
404     QTest::newRow("Date") << "DaTE";
405     QTest::newRow("Expect") << "ExPect";
406     QTest::newRow("Host") << "HoST";
407     QTest::newRow("Keep-Alive") << "KEEP-aLive";
408     QTest::newRow("Referer") << "ReferEr";
409     QTest::newRow("TE") << "Te";
410     QTest::newRow("Trailer") << "TraILEr";
411     QTest::newRow("Transfer-Encoding") << "tRANsfer-Encoding";
412     QTest::newRow("Upgrade") << "UpgrADe";
413     QTest::newRow("User-Agent") << "uSEr-Agent";
414     QTest::newRow("Via") << "vIa";
415     QTest::newRow("Proxy-") << "ProXy-";
416     QTest::newRow("Sec-") << "SeC-";
417     QTest::newRow("Proxy-*") << "Proxy-BLAH";
418     QTest::newRow("Sec-*") << "Sec-F";
419 }
420
421 // Tests that using illegal header names has no effect
422 void tst_qdeclarativexmlhttprequest::setRequestHeader_illegalName()
423 {
424     QFETCH(QString, name);
425
426     TestHTTPServer server(SERVER_PORT);
427     QVERIFY(server.isValid());
428     QVERIFY(server.wait(testFileUrl("open_network.expect"),
429                         testFileUrl("open_network.reply"),
430                         testFileUrl("testdocument.html")));
431
432     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader_illegalName.qml"));
433     QObject *object = component.beginCreate(engine.rootContext());
434     QVERIFY(object != 0);
435     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
436     object->setProperty("header", name);
437     component.completeCreate();
438
439     QCOMPARE(object->property("readyState").toBool(), true);
440     QCOMPARE(object->property("openedState").toBool(), true);
441     QCOMPARE(object->property("status").toBool(), true);
442     QCOMPARE(object->property("statusText").toBool(), true);
443     QCOMPARE(object->property("responseText").toBool(), true);
444     QCOMPARE(object->property("responseXML").toBool(), true);
445
446     QTRY_VERIFY(object->property("dataOK").toBool() == true);
447
448     delete object;
449 }
450
451 // Test that attempting to set a header after a request is sent throws an exception
452 void tst_qdeclarativexmlhttprequest::setRequestHeader_sent()
453 {
454     TestHTTPServer server(SERVER_PORT);
455     QVERIFY(server.isValid());
456     QVERIFY(server.wait(testFileUrl("open_network.expect"),
457                         testFileUrl("open_network.reply"),
458                         testFileUrl("testdocument.html")));
459
460     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader_sent.qml"));
461     QObject *object = component.beginCreate(engine.rootContext());
462     QVERIFY(object != 0);
463     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
464     component.completeCreate();
465
466     QCOMPARE(object->property("test").toBool(), true);
467     
468     QTRY_VERIFY(object->property("dataOK").toBool() == true);
469
470     delete object;
471 }
472
473 // Invalid arg count throws exception
474 void tst_qdeclarativexmlhttprequest::setRequestHeader_args()
475 {
476     QDeclarativeComponent component(&engine, testFileUrl("setRequestHeader_args.qml"));
477     QObject *object = component.create();
478     QVERIFY(object != 0);
479
480     QCOMPARE(object->property("exceptionThrown").toBool(), true);
481
482     delete object;
483 }
484
485 // Test that calling send() in UNSENT state throws an exception
486 void tst_qdeclarativexmlhttprequest::send_unsent()
487 {
488     QDeclarativeComponent component(&engine, testFileUrl("send_unsent.qml"));
489     QObject *object = component.create();
490     QVERIFY(object != 0);
491
492     QCOMPARE(object->property("test").toBool(), true);
493
494     delete object;
495 }
496
497 // Test attempting to resend a sent request throws an exception
498 void tst_qdeclarativexmlhttprequest::send_alreadySent()
499 {
500     QDeclarativeComponent component(&engine, testFileUrl("send_alreadySent.qml"));
501     QObject *object = component.create();
502     QVERIFY(object != 0);
503
504     QCOMPARE(object->property("test").toBool(), true);
505     QTRY_VERIFY(object->property("dataOK").toBool() == true);
506
507     delete object;
508 }
509
510 // Test that sends for GET, HEAD and DELETE ignore data
511 void tst_qdeclarativexmlhttprequest::send_ignoreData()
512 {
513     {
514         TestHTTPServer server(SERVER_PORT);
515         QVERIFY(server.isValid());
516         QVERIFY(server.wait(testFileUrl("send_ignoreData_GET.expect"),
517                             testFileUrl("send_ignoreData.reply"),
518                             testFileUrl("testdocument.html")));
519
520         QDeclarativeComponent component(&engine, testFileUrl("send_ignoreData.qml"));
521         QObject *object = component.beginCreate(engine.rootContext());
522         QVERIFY(object != 0);
523         object->setProperty("reqType", "GET");
524         object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
525         component.completeCreate();
526
527         QTRY_VERIFY(object->property("dataOK").toBool() == true);
528
529         delete object;
530     }
531
532     {
533         TestHTTPServer server(SERVER_PORT);
534         QVERIFY(server.isValid());
535         QVERIFY(server.wait(testFileUrl("send_ignoreData_HEAD.expect"),
536                             testFileUrl("send_ignoreData.reply"),
537                             QUrl()));
538
539         QDeclarativeComponent component(&engine, testFileUrl("send_ignoreData.qml"));
540         QObject *object = component.beginCreate(engine.rootContext());
541         QVERIFY(object != 0);
542         object->setProperty("reqType", "HEAD");
543         object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
544         component.completeCreate();
545
546         QTRY_VERIFY(object->property("dataOK").toBool() == true);
547
548         delete object;
549     }
550
551     {
552         TestHTTPServer server(SERVER_PORT);
553         QVERIFY(server.isValid());
554         QVERIFY(server.wait(testFileUrl("send_ignoreData_DELETE.expect"),
555                             testFileUrl("send_ignoreData.reply"),
556                             QUrl()));
557
558         QDeclarativeComponent component(&engine, testFileUrl("send_ignoreData.qml"));
559         QObject *object = component.beginCreate(engine.rootContext());
560         QVERIFY(object != 0);
561         object->setProperty("reqType", "DELETE");
562         object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
563         component.completeCreate();
564
565         QTRY_VERIFY(object->property("dataOK").toBool() == true);
566
567         delete object;
568     }
569 }
570
571 // Test that send()'ing data works
572 void tst_qdeclarativexmlhttprequest::send_withdata()
573 {
574     QFETCH(QString, file_expected);
575     QFETCH(QString, file_qml);
576
577     TestHTTPServer server(SERVER_PORT);
578     QVERIFY(server.isValid());
579     QVERIFY(server.wait(testFileUrl(file_expected),
580                         testFileUrl("send_data.reply"),
581                         testFileUrl("testdocument.html")));
582
583     QDeclarativeComponent component(&engine, testFileUrl(file_qml));
584     QObject *object = component.beginCreate(engine.rootContext());
585     QVERIFY(object != 0);
586     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
587     component.completeCreate();
588
589     QTRY_VERIFY(object->property("dataOK").toBool() == true);
590
591     delete object;
592 }
593
594 void tst_qdeclarativexmlhttprequest::send_withdata_data()
595 {
596     QTest::addColumn<QString>("file_expected");
597     QTest::addColumn<QString>("file_qml");
598
599     QTest::newRow("No content-type") << "send_data.1.expect" << "send_data.1.qml";
600     QTest::newRow("Correct content-type") << "send_data.1.expect" << "send_data.2.qml";
601     QTest::newRow("Incorrect content-type") << "send_data.1.expect" << "send_data.3.qml";
602     QTest::newRow("Correct content-type - out of order") << "send_data.4.expect" << "send_data.4.qml";
603     QTest::newRow("Incorrect content-type - out of order") << "send_data.4.expect" << "send_data.5.qml";
604     QTest::newRow("PUT") << "send_data.6.expect" << "send_data.6.qml";
605     QTest::newRow("Correct content-type - no charset") << "send_data.1.expect" << "send_data.7.qml";
606 }
607
608 // Test abort() has no effect in unsent state
609 void tst_qdeclarativexmlhttprequest::abort_unsent()
610 {
611     QDeclarativeComponent component(&engine, testFileUrl("abort_unsent.qml"));
612     QObject *object = component.beginCreate(engine.rootContext());
613     QVERIFY(object != 0);
614     object->setProperty("url", "testdocument.html");
615     component.completeCreate();
616
617     QCOMPARE(object->property("readyState").toBool(), true);
618     QCOMPARE(object->property("openedState").toBool(), true);
619     QCOMPARE(object->property("status").toBool(), true);
620     QCOMPARE(object->property("statusText").toBool(), true);
621     QCOMPARE(object->property("responseText").toBool(), true);
622     QCOMPARE(object->property("responseXML").toBool(), true);
623
624     QTRY_VERIFY(object->property("dataOK").toBool() == true);
625
626     delete object;
627 }
628
629 // Test abort() cancels an open (but unsent) request
630 void tst_qdeclarativexmlhttprequest::abort_opened()
631 {
632     QDeclarativeComponent component(&engine, testFileUrl("abort_opened.qml"));
633     QObject *object = component.beginCreate(engine.rootContext());
634     QVERIFY(object != 0);
635     object->setProperty("url", "testdocument.html");
636     component.completeCreate();
637
638     QCOMPARE(object->property("readyState").toBool(), true);
639     QCOMPARE(object->property("openedState").toBool(), true);
640     QCOMPARE(object->property("status").toBool(), true);
641     QCOMPARE(object->property("statusText").toBool(), true);
642     QCOMPARE(object->property("responseText").toBool(), true);
643     QCOMPARE(object->property("responseXML").toBool(), true);
644
645     QTRY_VERIFY(object->property("dataOK").toBool() == true);
646
647     delete object;
648 }
649
650 // Test abort() aborts in progress send
651 void tst_qdeclarativexmlhttprequest::abort()
652 {
653     TestHTTPServer server(SERVER_PORT);
654     QVERIFY(server.isValid());
655     QVERIFY(server.wait(testFileUrl("abort.expect"),
656                         testFileUrl("abort.reply"),
657                         testFileUrl("testdocument.html")));
658
659     QDeclarativeComponent component(&engine, testFileUrl("abort.qml"));
660     QObject *object = component.beginCreate(engine.rootContext());
661     QVERIFY(object != 0);
662     object->setProperty("urlDummy", "http://127.0.0.1:14449/testdocument.html");
663     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
664     component.completeCreate();
665
666     QCOMPARE(object->property("seenDone").toBool(), true);
667     QCOMPARE(object->property("didNotSeeUnsent").toBool(), true);
668     QCOMPARE(object->property("endStateUnsent").toBool(), true);
669
670     QTRY_VERIFY(object->property("dataOK").toBool() == true);
671
672     delete object;
673 }
674
675 void tst_qdeclarativexmlhttprequest::getResponseHeader()
676 {
677     QDeclarativeEngine engine; // Avoid cookie contamination
678
679     TestHTTPServer server(SERVER_PORT);
680     QVERIFY(server.isValid());
681     QVERIFY(server.wait(testFileUrl("getResponseHeader.expect"),
682                         testFileUrl("getResponseHeader.reply"),
683                         testFileUrl("testdocument.html")));
684
685
686     QDeclarativeComponent component(&engine, testFileUrl("getResponseHeader.qml"));
687     QObject *object = component.beginCreate(engine.rootContext());
688     QVERIFY(object != 0);
689     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
690     component.completeCreate();
691
692     QCOMPARE(object->property("unsentException").toBool(), true);
693     QCOMPARE(object->property("openedException").toBool(), true);
694     QCOMPARE(object->property("readyState").toBool(), true);
695     QCOMPARE(object->property("openedState").toBool(), true);
696
697     QTRY_VERIFY(object->property("dataOK").toBool() == true);
698
699     QCOMPARE(object->property("headersReceivedState").toBool(), true);
700     QCOMPARE(object->property("headersReceivedNullHeader").toBool(), true);
701     QCOMPARE(object->property("headersReceivedValidHeader").toBool(), true);
702     QCOMPARE(object->property("headersReceivedMultiValidHeader").toBool(), true);
703     QCOMPARE(object->property("headersReceivedCookieHeader").toBool(), true);
704
705     QCOMPARE(object->property("doneState").toBool(), true);
706     QCOMPARE(object->property("doneNullHeader").toBool(), true);
707     QCOMPARE(object->property("doneValidHeader").toBool(), true);
708     QCOMPARE(object->property("doneMultiValidHeader").toBool(), true);
709     QCOMPARE(object->property("doneCookieHeader").toBool(), true);
710
711     delete object;
712 }
713
714 // Test getResponseHeader throws an exception in an invalid state
715 void tst_qdeclarativexmlhttprequest::getResponseHeader_unsent()
716 {
717     QDeclarativeComponent component(&engine, testFileUrl("getResponseHeader_unsent.qml"));
718     QObject *object = component.create();
719     QVERIFY(object != 0);
720
721     QCOMPARE(object->property("test").toBool(), true);
722
723     delete object;
724 }
725
726 // Test getResponseHeader throws an exception in an invalid state
727 void tst_qdeclarativexmlhttprequest::getResponseHeader_sent()
728 {
729     QDeclarativeComponent component(&engine, testFileUrl("getResponseHeader_sent.qml"));
730     QObject *object = component.create();
731     QVERIFY(object != 0);
732
733     QCOMPARE(object->property("test").toBool(), true);
734
735     delete object;
736 }
737
738 // Invalid arg count throws exception
739 void tst_qdeclarativexmlhttprequest::getResponseHeader_args()
740 {
741     QDeclarativeComponent component(&engine, testFileUrl("getResponseHeader_args.qml"));
742     QObject *object = component.create();
743     QVERIFY(object != 0);
744
745     QTRY_VERIFY(object->property("exceptionThrown").toBool() == true);
746
747     delete object;
748 }
749
750 void tst_qdeclarativexmlhttprequest::getAllResponseHeaders()
751 {
752     QDeclarativeEngine engine; // Avoid cookie contamination
753
754     TestHTTPServer server(SERVER_PORT);
755     QVERIFY(server.isValid());
756     QVERIFY(server.wait(testFileUrl("getResponseHeader.expect"),
757                         testFileUrl("getResponseHeader.reply"),
758                         testFileUrl("testdocument.html")));
759
760     QDeclarativeComponent component(&engine, testFileUrl("getAllResponseHeaders.qml"));
761     QObject *object = component.beginCreate(engine.rootContext());
762     QVERIFY(object != 0);
763     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
764     component.completeCreate();
765
766     QCOMPARE(object->property("unsentException").toBool(), true);
767     QCOMPARE(object->property("openedException").toBool(), true);
768     QCOMPARE(object->property("readyState").toBool(), true);
769     QCOMPARE(object->property("openedState").toBool(), true);
770
771     QTRY_VERIFY(object->property("dataOK").toBool() == true);
772
773     QCOMPARE(object->property("headersReceivedState").toBool(), true);
774     QCOMPARE(object->property("headersReceivedHeader").toBool(), true);
775
776     QCOMPARE(object->property("doneState").toBool(), true);
777     QCOMPARE(object->property("doneHeader").toBool(), true);
778
779     delete object;
780 }
781
782 // Test getAllResponseHeaders throws an exception in an invalid state
783 void tst_qdeclarativexmlhttprequest::getAllResponseHeaders_unsent()
784 {
785     QDeclarativeComponent component(&engine, testFileUrl("getAllResponseHeaders_unsent.qml"));
786     QObject *object = component.create();
787     QVERIFY(object != 0);
788
789     QCOMPARE(object->property("test").toBool(), true);
790
791     delete object;
792 }
793
794 // Test getAllResponseHeaders throws an exception in an invalid state
795 void tst_qdeclarativexmlhttprequest::getAllResponseHeaders_sent()
796 {
797     QDeclarativeComponent component(&engine, testFileUrl("getAllResponseHeaders_sent.qml"));
798     QObject *object = component.create();
799     QVERIFY(object != 0);
800
801     QCOMPARE(object->property("test").toBool(), true);
802
803     delete object;
804 }
805
806 // Invalid arg count throws exception
807 void tst_qdeclarativexmlhttprequest::getAllResponseHeaders_args()
808 {
809     QDeclarativeComponent component(&engine, testFileUrl("getAllResponseHeaders_args.qml"));
810     QObject *object = component.create();
811     QVERIFY(object != 0);
812
813     QTRY_VERIFY(object->property("exceptionThrown").toBool() == true);
814
815     delete object;
816 }
817
818 void tst_qdeclarativexmlhttprequest::status()
819 {
820     QFETCH(QUrl, replyUrl);
821     QFETCH(int, status);
822
823     TestHTTPServer server(SERVER_PORT);
824     QVERIFY(server.isValid());
825     QVERIFY(server.wait(testFileUrl("status.expect"),
826                         replyUrl,
827                         testFileUrl("testdocument.html")));
828
829     QDeclarativeComponent component(&engine, testFileUrl("status.qml"));
830     QObject *object = component.beginCreate(engine.rootContext());
831     QVERIFY(object != 0);
832     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
833     object->setProperty("expectedStatus", status);
834     component.completeCreate();
835
836     QTRY_VERIFY(object->property("dataOK").toBool() == true);
837
838     QCOMPARE(object->property("unsentException").toBool(), true);
839     QCOMPARE(object->property("openedException").toBool(), true);
840     QCOMPARE(object->property("sentException").toBool(), true);
841     QCOMPARE(object->property("headersReceived").toBool(), true);
842     QCOMPARE(object->property("loading").toBool(), true);
843     QCOMPARE(object->property("done").toBool(), true);
844     QCOMPARE(object->property("resetException").toBool(), true);
845
846     delete object;
847 }
848
849 void tst_qdeclarativexmlhttprequest::status_data()
850 {
851     QTest::addColumn<QUrl>("replyUrl");
852     QTest::addColumn<int>("status");
853
854     QTest::newRow("OK") << testFileUrl("status.200.reply") << 200;
855     QTest::newRow("Not Found") << testFileUrl("status.404.reply") << 404;
856     QTest::newRow("Bad Request") << testFileUrl("status.400.reply") << 400;
857 }
858
859 void tst_qdeclarativexmlhttprequest::statusText()
860 {
861     QFETCH(QUrl, replyUrl);
862     QFETCH(QString, statusText);
863
864     TestHTTPServer server(SERVER_PORT);
865     QVERIFY(server.isValid());
866     QVERIFY(server.wait(testFileUrl("status.expect"),
867                         replyUrl, 
868                         testFileUrl("testdocument.html")));
869
870     QDeclarativeComponent component(&engine, testFileUrl("statusText.qml"));
871     QObject *object = component.beginCreate(engine.rootContext());
872     QVERIFY(object != 0);
873     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
874     object->setProperty("expectedStatus", statusText);
875     component.completeCreate();
876
877     QTRY_VERIFY(object->property("dataOK").toBool() == true);
878
879     QCOMPARE(object->property("unsentException").toBool(), true);
880     QCOMPARE(object->property("openedException").toBool(), true);
881     QCOMPARE(object->property("sentException").toBool(), true);
882     QCOMPARE(object->property("headersReceived").toBool(), true);
883     QCOMPARE(object->property("loading").toBool(), true);
884     QCOMPARE(object->property("done").toBool(), true);
885     QCOMPARE(object->property("resetException").toBool(), true);
886
887     delete object;
888 }
889
890 void tst_qdeclarativexmlhttprequest::statusText_data()
891 {
892     QTest::addColumn<QUrl>("replyUrl");
893     QTest::addColumn<QString>("statusText");
894
895     QTest::newRow("OK") << testFileUrl("status.200.reply") << "OK";
896     QTest::newRow("Not Found") << testFileUrl("status.404.reply") << "Document not found";
897     QTest::newRow("Bad Request") << testFileUrl("status.400.reply") << "Bad request";
898 }
899
900 void tst_qdeclarativexmlhttprequest::responseText()
901 {
902     QFETCH(QUrl, replyUrl);
903     QFETCH(QUrl, bodyUrl);
904     QFETCH(QString, responseText);
905
906     TestHTTPServer server(SERVER_PORT);
907     QVERIFY(server.isValid());
908     QVERIFY(server.wait(testFileUrl("status.expect"),
909                         replyUrl, 
910                         bodyUrl));
911
912     QDeclarativeComponent component(&engine, testFileUrl("responseText.qml"));
913     QObject *object = component.beginCreate(engine.rootContext());
914     QVERIFY(object != 0);
915     object->setProperty("url", "http://127.0.0.1:14445/testdocument.html");
916     object->setProperty("expectedText", responseText);
917     component.completeCreate();
918
919     QTRY_VERIFY(object->property("dataOK").toBool() == true);
920
921     QCOMPARE(object->property("unsent").toBool(), true);
922     QCOMPARE(object->property("opened").toBool(), true);
923     QCOMPARE(object->property("sent").toBool(), true);
924     QCOMPARE(object->property("headersReceived").toBool(), true);
925     QCOMPARE(object->property("loading").toBool(), true);
926     QCOMPARE(object->property("done").toBool(), true);
927     QCOMPARE(object->property("reset").toBool(), true);
928
929     delete object;
930 }
931
932 void tst_qdeclarativexmlhttprequest::responseText_data()
933 {
934     QTest::addColumn<QUrl>("replyUrl");
935     QTest::addColumn<QUrl>("bodyUrl");
936     QTest::addColumn<QString>("responseText");
937
938     QTest::newRow("OK") << testFileUrl("status.200.reply") << testFileUrl("testdocument.html") << "QML Rocks!\n";
939     QTest::newRow("empty body") << testFileUrl("status.200.reply") << QUrl() << "";
940     QTest::newRow("Not Found") << testFileUrl("status.404.reply") << testFileUrl("testdocument.html") << "";
941     QTest::newRow("Bad Request") << testFileUrl("status.404.reply") << testFileUrl("testdocument.html") << "";
942 }
943
944 void tst_qdeclarativexmlhttprequest::nonUtf8()
945 {
946     QFETCH(QString, fileName);
947     QFETCH(QString, responseText);
948     QFETCH(QString, xmlRootNodeValue);
949
950     QDeclarativeComponent component(&engine, testFileUrl("utf16.qml"));
951     QObject *object = component.create();
952     QVERIFY(object != 0);
953
954     object->setProperty("fileName", fileName);
955     QMetaObject::invokeMethod(object, "startRequest");
956
957     QTRY_VERIFY(object->property("dataOK").toBool() == true);
958     
959     QCOMPARE(object->property("responseText").toString(), responseText);
960
961     if (!xmlRootNodeValue.isEmpty()) {
962         QString rootNodeValue = object->property("responseXmlRootNodeValue").toString();
963         QCOMPARE(rootNodeValue, xmlRootNodeValue);
964     }
965
966     delete object;
967 }
968
969 void tst_qdeclarativexmlhttprequest::nonUtf8_data()
970 {
971     QTest::addColumn<QString>("fileName");
972     QTest::addColumn<QString>("responseText");
973     QTest::addColumn<QString>("xmlRootNodeValue");
974
975     QString uc;
976     uc.resize(3);
977     uc[0] = QChar(0x10e3);
978     uc[1] = QChar(' ');
979     uc[2] = QChar(0x03a3);
980
981     QTest::newRow("responseText") << "utf16.html" << uc + '\n' << "";
982     QTest::newRow("responseXML") << "utf16.xml" << "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone='yes'?>\n<root>\n" + uc + "\n</root>\n" << QString('\n' + uc + '\n');
983 }
984
985 // Test that calling hte XMLHttpRequest methods on a non-XMLHttpRequest object
986 // throws an exception
987 void tst_qdeclarativexmlhttprequest::invalidMethodUsage()
988 {
989     QDeclarativeComponent component(&engine, testFileUrl("invalidMethodUsage.qml"));
990     QObject *object = component.create();
991     QVERIFY(object != 0);
992
993     QCOMPARE(object->property("readyState").toBool(), true);
994     QCOMPARE(object->property("status").toBool(), true);
995     QCOMPARE(object->property("statusText").toBool(), true);
996     QCOMPARE(object->property("responseText").toBool(), true);
997     QCOMPARE(object->property("responseXML").toBool(), true);
998
999     QCOMPARE(object->property("open").toBool(), true);
1000     QCOMPARE(object->property("setRequestHeader").toBool(), true);
1001     QCOMPARE(object->property("send").toBool(), true);
1002     QCOMPARE(object->property("abort").toBool(), true);
1003     QCOMPARE(object->property("getResponseHeader").toBool(), true);
1004     QCOMPARE(object->property("getAllResponseHeaders").toBool(), true);
1005
1006     delete object;
1007 }
1008
1009 // Test that XMLHttpRequest transparently redirects
1010 void tst_qdeclarativexmlhttprequest::redirects()
1011 {
1012     {
1013         TestHTTPServer server(SERVER_PORT);
1014         QVERIFY(server.isValid());
1015         server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirecttarget.html");
1016         server.serveDirectory(dataDirectory());
1017
1018         QDeclarativeComponent component(&engine, testFileUrl("redirects.qml"));
1019         QObject *object = component.beginCreate(engine.rootContext());
1020         QVERIFY(object != 0);
1021         object->setProperty("url", "http://127.0.0.1:14445/redirect.html");
1022         object->setProperty("expectedText", "");
1023         component.completeCreate();
1024
1025         QTRY_VERIFY(object->property("done").toBool() == true);
1026         QCOMPARE(object->property("dataOK").toBool(), true);
1027
1028         delete object;
1029     }
1030
1031     {
1032         TestHTTPServer server(SERVER_PORT);
1033         QVERIFY(server.isValid());
1034         server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirectmissing.html");
1035         server.serveDirectory(dataDirectory());
1036
1037         QDeclarativeComponent component(&engine, testFileUrl("redirectError.qml"));
1038         QObject *object = component.beginCreate(engine.rootContext());
1039         QVERIFY(object != 0);
1040         object->setProperty("url", "http://127.0.0.1:14445/redirect.html");
1041         object->setProperty("expectedText", "");
1042         component.completeCreate();
1043
1044         QTRY_VERIFY(object->property("done").toBool() == true);
1045         QCOMPARE(object->property("dataOK").toBool(), true);
1046
1047         delete object;
1048     }
1049
1050     {
1051         TestHTTPServer server(SERVER_PORT);
1052         QVERIFY(server.isValid());
1053         server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirect.html");
1054         server.serveDirectory(dataDirectory());
1055
1056         QDeclarativeComponent component(&engine, testFileUrl("redirectRecur.qml"));
1057         QObject *object = component.beginCreate(engine.rootContext());
1058         QVERIFY(object != 0);
1059         object->setProperty("url", "http://127.0.0.1:14445/redirect.html");
1060         object->setProperty("expectedText", "");
1061         component.completeCreate();
1062
1063         for (int ii = 0; ii < 60; ++ii) { 
1064             if (object->property("done").toBool()) break; 
1065             QTest::qWait(50); 
1066         } 
1067         QVERIFY(object->property("done").toBool() == true);
1068
1069         QCOMPARE(object->property("dataOK").toBool(), true);
1070
1071         delete object;
1072     }
1073 }
1074
1075 void tst_qdeclarativexmlhttprequest::responseXML_invalid()
1076 {
1077     QDeclarativeComponent component(&engine, testFileUrl("responseXML_invalid.qml"));
1078     QObject *object = component.create();
1079     QVERIFY(object != 0);
1080
1081     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1082
1083     QCOMPARE(object->property("xmlNull").toBool(), true);
1084
1085     delete object;
1086 }
1087
1088 // Test the Document DOM element
1089 void tst_qdeclarativexmlhttprequest::document()
1090 {
1091     QDeclarativeComponent component(&engine, testFileUrl("document.qml"));
1092     QObject *object = component.create();
1093     QVERIFY(object != 0);
1094
1095     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1096
1097     QCOMPARE(object->property("xmlTest").toBool(), true);
1098
1099     delete object;
1100 }
1101
1102 // Test the Element DOM element
1103 void tst_qdeclarativexmlhttprequest::element()
1104 {
1105     QDeclarativeComponent component(&engine, testFileUrl("element.qml"));
1106     QObject *object = component.create();
1107     QVERIFY(object != 0);
1108
1109     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1110
1111     QCOMPARE(object->property("xmlTest").toBool(), true);
1112
1113     delete object;
1114 }
1115
1116 // Test the Attr DOM element
1117 void tst_qdeclarativexmlhttprequest::attr()
1118 {
1119     QDeclarativeComponent component(&engine, testFileUrl("attr.qml"));
1120     QObject *object = component.create();
1121     QVERIFY(object != 0);
1122
1123     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1124
1125     QCOMPARE(object->property("xmlTest").toBool(), true);
1126
1127     delete object;
1128 }
1129
1130 // Test the Text DOM element
1131 void tst_qdeclarativexmlhttprequest::text()
1132 {
1133     QDeclarativeComponent component(&engine, testFileUrl("text.qml"));
1134     QObject *object = component.create();
1135     QVERIFY(object != 0);
1136
1137     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1138
1139     QCOMPARE(object->property("xmlTest").toBool(), true);
1140
1141     delete object;
1142 }
1143
1144 // Test the CDataSection DOM element
1145 void tst_qdeclarativexmlhttprequest::cdata()
1146 {
1147     QDeclarativeComponent component(&engine, testFileUrl("cdata.qml"));
1148     QObject *object = component.create();
1149     QVERIFY(object != 0);
1150
1151     QTRY_VERIFY(object->property("dataOK").toBool() == true);
1152
1153     QCOMPARE(object->property("xmlTest").toBool(), true);
1154
1155     delete object;
1156 }
1157
1158 QTEST_MAIN(tst_qdeclarativexmlhttprequest)
1159
1160 #include "tst_qdeclarativexmlhttprequest.moc"