summaryrefslogtreecommitdiffstats
path: root/tests/auto/network
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network')
-rw-r--r--tests/auto/network/access/CMakeLists.txt2
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp2
-rw-r--r--tests/auto/network/access/qformdatabuilder/CMakeLists.txt22
-rw-r--r--tests/auto/network/access/qformdatabuilder/document.docxbin0 -> 10548 bytes
-rw-r--r--tests/auto/network/access/qformdatabuilder/image1.jpgbin0 -> 518 bytes
-rw-r--r--tests/auto/network/access/qformdatabuilder/rfc3252.txt1
-rw-r--r--tests/auto/network/access/qformdatabuilder/sheet.xlsxbin0 -> 8534 bytes
-rw-r--r--tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp362
-rw-r--r--tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp3
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsabin52422 -> 53629 bytes
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp10
-rw-r--r--tests/auto/network/access/qnetworkreply_local/CMakeLists.txt9
-rw-r--r--tests/auto/network/access/qnetworkreply_local/minihttpserver.h246
-rw-r--r--tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp177
-rw-r--r--tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp2
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp8
16 files changed, 836 insertions, 8 deletions
diff --git a/tests/auto/network/access/CMakeLists.txt b/tests/auto/network/access/CMakeLists.txt
index 3ae66e1308..ed99aa8746 100644
--- a/tests/auto/network/access/CMakeLists.txt
+++ b/tests/auto/network/access/CMakeLists.txt
@@ -13,6 +13,8 @@ add_subdirectory(qnetworkreply)
add_subdirectory(qnetworkcachemetadata)
add_subdirectory(qabstractnetworkcache)
if(QT_FEATURE_http)
+ add_subdirectory(qnetworkreply_local)
+ add_subdirectory(qformdatabuilder)
add_subdirectory(qnetworkrequestfactory)
add_subdirectory(qrestaccessmanager)
endif()
diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp
index d9e82330b2..396a6f2fda 100644
--- a/tests/auto/network/access/http2/tst_http2.cpp
+++ b/tests/auto/network/access/http2/tst_http2.cpp
@@ -1277,7 +1277,7 @@ void tst_Http2::unsupportedAuthenticateChallenge()
bool authenticationRequested = false;
connect(manager.get(), &QNetworkAccessManager::authenticationRequired, reply.get(),
- [&](QNetworkReply *, QAuthenticator *auth) {
+ [&](QNetworkReply *, QAuthenticator *) {
authenticationRequested = true;
});
diff --git a/tests/auto/network/access/qformdatabuilder/CMakeLists.txt b/tests/auto/network/access/qformdatabuilder/CMakeLists.txt
new file mode 100644
index 0000000000..dde2dc10e0
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qformdatabuilder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qformdatabuilder
+ SOURCES
+ tst_qformdatabuilder.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Network
+ TESTDATA
+ rfc3252.txt
+ image1.jpg
+ document.docx
+ sheet.xlsx
+)
+
diff --git a/tests/auto/network/access/qformdatabuilder/document.docx b/tests/auto/network/access/qformdatabuilder/document.docx
new file mode 100644
index 0000000000..49005f14b6
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/document.docx
Binary files differ
diff --git a/tests/auto/network/access/qformdatabuilder/image1.jpg b/tests/auto/network/access/qformdatabuilder/image1.jpg
new file mode 100644
index 0000000000..d1d27bf7cf
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/image1.jpg
Binary files differ
diff --git a/tests/auto/network/access/qformdatabuilder/rfc3252.txt b/tests/auto/network/access/qformdatabuilder/rfc3252.txt
new file mode 100644
index 0000000000..5436ce5b26
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/rfc3252.txt
@@ -0,0 +1 @@
+some text for reference
diff --git a/tests/auto/network/access/qformdatabuilder/sheet.xlsx b/tests/auto/network/access/qformdatabuilder/sheet.xlsx
new file mode 100644
index 0000000000..2cb1ec7361
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/sheet.xlsx
Binary files differ
diff --git a/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp
new file mode 100644
index 0000000000..154edc806c
--- /dev/null
+++ b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp
@@ -0,0 +1,362 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtNetwork/qformdatabuilder.h>
+
+#include <QtCore/qbuffer.h>
+#include <QtCore/qfile.h>
+
+#include <QtTest/qtest.h>
+
+using namespace Qt::StringLiterals;
+
+class tst_QFormDataBuilder : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void generateQHttpPartWithDevice_data();
+ void generateQHttpPartWithDevice();
+
+ void escapesBackslashAndQuotesInFilenameAndName_data();
+ void escapesBackslashAndQuotesInFilenameAndName();
+
+ void picksUtf8FilenameEncodingIfAsciiDontSuffice_data();
+ void picksUtf8FilenameEncodingIfAsciiDontSuffice();
+
+ void setHeadersDoesNotAffectHeaderFieldsManagedByBuilder_data();
+ void setHeadersDoesNotAffectHeaderFieldsManagedByBuilder();
+
+ void specifyMimeType_data();
+ void specifyMimeType();
+
+ void picksUtf8NameEncodingIfAsciiDoesNotSuffice_data();
+ void picksUtf8NameEncodingIfAsciiDoesNotSuffice();
+};
+
+void tst_QFormDataBuilder::generateQHttpPartWithDevice_data()
+{
+ QTest::addColumn<QLatin1StringView>("name_data");
+ QTest::addColumn<QString>("real_file_name");
+ QTest::addColumn<QString>("body_name_data");
+ QTest::addColumn<QString>("expected_content_type_data");
+ QTest::addColumn<QString>("expected_content_disposition_data");
+ QTest::addColumn<QString>("content_disposition_must_not_contain_data");
+
+ QTest::newRow("txt-ascii") << "text"_L1 << u"rfc3252.txt"_s << u"rfc3252.txt"_s << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="rfc3252.txt")"_s
+ << u"filename*"_s;
+ QTest::newRow("txt-latin") << "text"_L1 << u"rfc3252.txt"_s << u"szöveg.txt"_s << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="szöveg.txt"; filename*=UTF-8''sz%C3%B6veg.txt)"_s
+ << u""_s;
+ QTest::newRow("txt-unicode") << "text"_L1 << u"rfc3252.txt"_s << u"テキスト.txt"_s << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="テキスト.txt"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.txt)"_s
+ << u""_s;
+
+ QTest::newRow("jpg-ascii") << "image"_L1 << u"image1.jpg"_s << u"image1.jpg"_s << u"image/jpeg"_s
+ << uR"(form-data; name="image"; filename="image1.jpg")"_s
+ << u"filename*"_s;
+ QTest::newRow("jpg-latin") << "image"_L1 << u"image1.jpg"_s << u"kép.jpg"_s << u"image/jpeg"_s
+ << uR"(form-data; name="image"; filename="kép.jpg"; filename*=UTF-8''k%C3%A9p.jpg)"_s
+ << u""_s;
+ QTest::newRow("jpg-unicode") << "image"_L1 << u"image1.jpg"_s << u"絵.jpg"_s << u"image/jpeg"_s
+ << uR"(form-data; name="image"; filename="絵.jpg"; filename*=UTF-8''%E7%B5%B5.jpg)"_s
+ << u""_s;
+
+ QTest::newRow("doc-ascii") << "text"_L1 << u"document.docx"_s << u"word.docx"_s
+ << u"application/vnd.openxmlformats-officedocument.wordprocessingml.document"_s
+ << uR"(form-data; name="text"; filename="word.docx")"_s
+ << u"filename*"_s;
+ QTest::newRow("doc-latin") << "text"_L1 << u"document.docx"_s << u"szöveg.docx"_s
+ << u"application/vnd.openxmlformats-officedocument.wordprocessingml.document"_s
+ << uR"(form-data; name="text"; filename="szöveg.docx"; filename*=UTF-8''sz%C3%B6veg.docx)"_s
+ << u""_s;
+ QTest::newRow("doc-unicode") << "text"_L1 << u"document.docx"_s << u"テキスト.docx"_s
+ << u"application/vnd.openxmlformats-officedocument.wordprocessingml.document"_s
+ << uR"(form-data; name="text"; filename="テキスト.docx"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.docx)"_s
+ << u""_s;
+
+ QTest::newRow("xls-ascii") << "spreadsheet"_L1 << u"sheet.xlsx"_s << u"sheet.xlsx"_s
+ << u"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_s
+ << uR"(form-data; name="spreadsheet"; filename="sheet.xlsx")"_s
+ << u"filename*"_s;
+ QTest::newRow("xls-latin") << "spreadsheet"_L1 << u"sheet.xlsx"_s << u"szöveg.xlsx"_s
+ << u"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_s
+ << uR"(form-data; name="spreadsheet"; filename="szöveg.xlsx"; filename*=UTF-8''sz%C3%B6veg.xlsx)"_s
+ << u""_s;
+ QTest::newRow("xls-unicode") << "spreadsheet"_L1 << u"sheet.xlsx"_s << u"テキスト.xlsx"_s
+ << u"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_s
+ << uR"(form-data; name="spreadsheet"; filename="テキスト.xlsx"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.xlsx)"_s
+ << u""_s;
+}
+
+void tst_QFormDataBuilder::generateQHttpPartWithDevice()
+{
+ QFETCH(const QLatin1StringView, name_data);
+ QFETCH(const QString, real_file_name);
+ QFETCH(const QString, body_name_data);
+ QFETCH(const QString, expected_content_type_data);
+ QFETCH(const QString, expected_content_disposition_data);
+ QFETCH(const QString, content_disposition_must_not_contain_data);
+
+ QString testData = QFileInfo(QFINDTESTDATA(real_file_name)).absoluteFilePath();
+ QFile data_file(testData);
+
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBodyDevice(&data_file, body_name_data);
+ const QHttpPart httpPart = qfdpb.build();
+
+ const auto msg = QDebug::toString(httpPart);
+ QVERIFY(msg.contains(expected_content_type_data));
+ QVERIFY(msg.contains(expected_content_disposition_data));
+ if (!content_disposition_must_not_contain_data.isEmpty())
+ QVERIFY(!msg.contains(content_disposition_must_not_contain_data));
+}
+
+void tst_QFormDataBuilder::escapesBackslashAndQuotesInFilenameAndName_data()
+{
+ QTest::addColumn<QLatin1StringView>("name_data");
+ QTest::addColumn<QString>("body_name_data");
+ QTest::addColumn<QString>("expected_content_type_data");
+ QTest::addColumn<QString>("expected_content_disposition_data");
+
+ QTest::newRow("quote") << "t\"ext"_L1 << uR"(rfc32"52.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\"ext"; filename="rfc32\"52.txt")"_s;
+
+ QTest::newRow("slash") << "t\\ext"_L1 << uR"(rfc32\52.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\\ext"; filename="rfc32\\52.txt")"_s;
+
+ QTest::newRow("quotes") << "t\"e\"xt"_L1 << uR"(rfc3"25"2.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\"e\"xt"; filename="rfc3\"25\"2.txt")"_s;
+
+ QTest::newRow("slashes") << "t\\\\ext"_L1 << uR"(rfc32\\52.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\\\\ext"; filename="rfc32\\\\52.txt")"_s;
+
+ QTest::newRow("quote-slash") << "t\"ex\\t"_L1 << uR"(rfc"32\52.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\"ex\\t"; filename="rfc\"32\\52.txt")"_s;
+
+ QTest::newRow("quotes-slashes") << "t\"e\"x\\t\\"_L1 << uR"(r"f"c3\2\52.txt)"_s << u"text/plain"_s
+ << uR"(form-data; name="t\"e\"x\\t\\"; filename="r\"f\"c3\\2\\52.txt")"_s;
+}
+
+void tst_QFormDataBuilder::escapesBackslashAndQuotesInFilenameAndName()
+{
+ QFETCH(const QLatin1StringView, name_data);
+ QFETCH(const QString, body_name_data);
+ QFETCH(const QString, expected_content_type_data);
+ QFETCH(const QString, expected_content_disposition_data);
+
+ QFile dummy_file(body_name_data);
+
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBodyDevice(&dummy_file, body_name_data);
+ const QHttpPart httpPart = qfdpb.build();
+
+ const auto msg = QDebug::toString(httpPart);
+ QVERIFY(msg.contains(expected_content_type_data));
+ QVERIFY(msg.contains(expected_content_disposition_data));
+}
+
+void tst_QFormDataBuilder::picksUtf8FilenameEncodingIfAsciiDontSuffice_data()
+{
+ QTest::addColumn<QLatin1StringView>("name_data");
+ QTest::addColumn<QAnyStringView>("body_name_data");
+ QTest::addColumn<QString>("expected_content_type_data");
+ QTest::addColumn<QString>("expected_content_disposition_data");
+ QTest::addColumn<QString>("content_disposition_must_not_contain_data");
+
+ QTest::newRow("latin1-ascii") << "text"_L1 << QAnyStringView("rfc3252.txt"_L1) << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="rfc3252.txt")"_s
+ << u"filename*"_s;
+ QTest::newRow("u8-ascii") << "text"_L1 << QAnyStringView(u8"rfc3252.txt") << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="rfc3252.txt")"_s
+ << u"filename*"_s;
+ QTest::newRow("u-ascii") << "text"_L1 << QAnyStringView(u"rfc3252.txt") << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="rfc3252.txt")"_s
+ << u"filename*"_s;
+
+ // 0xF6 is 'ö', use hex value with Latin-1 to avoid interpretation as UTF-8 (0xC3 0xB6)
+ QTest::newRow("latin1-latin") << "text"_L1 << QAnyStringView("sz\xF6veg.txt"_L1) << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="szöveg.txt"; filename*=UTF-8''sz%C3%B6veg.txt)"_s
+ << u""_s;
+ QTest::newRow("u8-latin") << "text"_L1 << QAnyStringView(u8"szöveg.txt") << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="szöveg.txt"; filename*=UTF-8''sz%C3%B6veg.txt)"_s
+ << u""_s;
+ QTest::newRow("u-latin") << "text"_L1 << QAnyStringView(u"szöveg.txt") << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="szöveg.txt"; filename*=UTF-8''sz%C3%B6veg.txt)"_s
+ << u""_s;
+
+ QTest::newRow("u8-u8") << "text"_L1 << QAnyStringView(u8"テキスト.txt") << u"text/plain"_s
+ << uR"(form-data; name="text"; filename="テキスト.txt"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.txt)"_s
+ << u""_s;
+}
+
+void tst_QFormDataBuilder::picksUtf8FilenameEncodingIfAsciiDontSuffice()
+{
+ QFETCH(const QLatin1StringView, name_data);
+ QFETCH(const QAnyStringView, body_name_data);
+ QFETCH(const QString, expected_content_type_data);
+ QFETCH(const QString, expected_content_disposition_data);
+ QFETCH(const QString, content_disposition_must_not_contain_data);
+
+ QBuffer buff;
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBodyDevice(&buff, body_name_data);
+ const QHttpPart httpPart = qfdpb.build();
+
+ const auto msg = QDebug::toString(httpPart);
+ QVERIFY2(msg.contains(expected_content_type_data),
+ qPrintable(u"content-type not found : "_s + expected_content_type_data));
+ QVERIFY2(msg.contains(expected_content_disposition_data),
+ qPrintable(u"content-disposition not found : "_s + expected_content_disposition_data));
+ if (!content_disposition_must_not_contain_data.isEmpty()) {
+ QVERIFY2(!msg.contains(content_disposition_must_not_contain_data),
+ qPrintable(u"content-disposition contained data it shouldn't : "_s
+ + content_disposition_must_not_contain_data));
+ }
+}
+
+void tst_QFormDataBuilder::setHeadersDoesNotAffectHeaderFieldsManagedByBuilder_data()
+{
+ QTest::addColumn<QLatin1StringView>("name_data");
+ QTest::addColumn<QAnyStringView>("body_name_data");
+ QTest::addColumn<bool>("overwrite");
+ QTest::addColumn<bool>("extra_headers");
+ QTest::addColumn<QStringList>("expected_headers");
+
+ QTest::newRow("content-disposition-is-set-by-default")
+ << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << false << false
+ << QStringList{
+ uR"("content-disposition":"form-data; name=\"text\"; filename=\"rfc3252.txt\"")"_s,
+ uR"("content-type":"text/plain")"_s};
+
+ QTest::newRow("default-overwrites-preset-content-disposition")
+ << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << true << false
+ << QStringList{
+ uR"("content-disposition":"form-data; name=\"text\"; filename=\"rfc3252.txt\"")"_s,
+ uR"("content-type":"text/plain")"_s};
+
+ QTest::newRow("added-extra-header")
+ << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << false << true
+ << QStringList{
+ uR"("content-disposition":"form-data; name=\"text\"; filename=\"rfc3252.txt\"")"_s,
+ uR"("content-type":"text/plain")"_s,
+ uR"("content-length":"70")"_s};
+
+ QTest::newRow("extra-header-and-overwrite")
+ << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << true << true
+ << QStringList{
+ uR"("content-disposition":"form-data; name=\"text\"; filename=\"rfc3252.txt\"")"_s,
+ uR"("content-type":"text/plain")"_s,
+ uR"("content-length":"70")"_s};
+}
+
+void tst_QFormDataBuilder::setHeadersDoesNotAffectHeaderFieldsManagedByBuilder()
+{
+ QFETCH(const QLatin1StringView, name_data);
+ QFETCH(const QAnyStringView, body_name_data);
+ QFETCH(const bool, overwrite);
+ QFETCH(const bool, extra_headers);
+ QFETCH(const QStringList, expected_headers);
+
+ QBuffer buff;
+
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBodyDevice(&buff, body_name_data);
+
+ if (overwrite || extra_headers) {
+ QHttpHeaders headers;
+
+ if (overwrite) {
+ headers.append(QHttpHeaders::WellKnownHeader::ContentType, "attachment");
+ qfdpb.setHeaders(headers);
+ }
+
+ if (extra_headers) {
+ headers.append(QHttpHeaders::WellKnownHeader::ContentLength, "70");
+ qfdpb.setHeaders(std::move(headers));
+ }
+ }
+
+ const QHttpPart httpPart = qfdpb.build();
+
+ const auto msg = QDebug::toString(httpPart);
+ for (const auto &header : expected_headers)
+ QVERIFY2(msg.contains(header), qPrintable(header));
+}
+
+void tst_QFormDataBuilder::specifyMimeType_data()
+{
+ QTest::addColumn<QLatin1StringView>("name_data");
+ QTest::addColumn<QAnyStringView>("body_name_data");
+ QTest::addColumn<QAnyStringView>("mime_type");
+ QTest::addColumn<QString>("expected_content_type_data");
+
+ QTest::newRow("not-specified") << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << QAnyStringView("text/plain"_L1) << uR"("content-type":"text/plain")"_s;
+ QTest::newRow("mime-specified") << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << QAnyStringView("text/plain"_L1) << uR"("content-type":"text/plain")"_s;
+ // wrong mime type specified but it is not overridden by the deduction
+ QTest::newRow("wrong-mime-specified") << "text"_L1 << QAnyStringView("rfc3252.txt"_L1)
+ << QAnyStringView("image/jpeg"_L1) << uR"("content-type":"image/jpeg)"_s;
+}
+
+void tst_QFormDataBuilder::specifyMimeType()
+{
+ QFETCH(const QLatin1StringView, name_data);
+ QFETCH(const QAnyStringView, body_name_data);
+ QFETCH(const QAnyStringView, mime_type);
+ QFETCH(const QString, expected_content_type_data);
+
+ QBuffer buff;
+
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBodyDevice(&buff, body_name_data);
+
+ if (!mime_type.empty())
+ qfdpb.setBodyDevice(&buff, body_name_data, mime_type);
+ else
+ qfdpb.setBodyDevice(&buff, body_name_data);
+
+ const QHttpPart httpPart = qfdpb.build();
+
+ const auto msg = QDebug::toString(httpPart);
+ QVERIFY(msg.contains(expected_content_type_data));
+}
+
+void tst_QFormDataBuilder::picksUtf8NameEncodingIfAsciiDoesNotSuffice_data()
+{
+ QTest::addColumn<QAnyStringView>("name_data");
+ QTest::addColumn<QString>("expected_content_disposition_data");
+
+ QTest::newRow("latin1-ascii") << QAnyStringView("text"_L1) << uR"(form-data; name="text")"_s;
+ QTest::newRow("u8-ascii") << QAnyStringView(u8"text") << uR"(form-data; name="text")"_s;
+ QTest::newRow("u-ascii") << QAnyStringView(u"text") << uR"(form-data; name="text")"_s;
+
+ // 0xF6 is 'ö', use hex value with Latin-1 to avoid interpretation as UTF-8
+ QTest::newRow("latin1-latin") << QAnyStringView("t\xF6xt"_L1) << uR"(form-data; name="töxt")"_s;
+ QTest::newRow("u8-latin") << QAnyStringView(u8"töxt") << uR"(form-data; name="töxt")"_s;
+ QTest::newRow("u-latin") << QAnyStringView(u"töxt") << uR"(form-data; name="töxt")"_s;
+
+ QTest::newRow("u8-u8") << QAnyStringView(u8"テキスト") << uR"(form-data; name="テキスト")"_s;
+}
+
+void tst_QFormDataBuilder::picksUtf8NameEncodingIfAsciiDoesNotSuffice()
+{
+ QFETCH(const QAnyStringView, name_data);
+ QFETCH(const QString, expected_content_disposition_data);
+
+ QFormDataBuilder qfdb;
+ QFormDataPartBuilder &qfdpb = qfdb.part(name_data).setBody("some"_ba);
+ auto msg = QDebug::toString(qfdpb.build());
+
+ QVERIFY2(msg.contains(expected_content_disposition_data),
+ qPrintable(u"content-disposition not found : "_s + expected_content_disposition_data));
+}
+
+QTEST_MAIN(tst_QFormDataBuilder)
+#include "tst_qformdatabuilder.moc"
diff --git a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
index 457d30feeb..5b6be3c7b4 100644
--- a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
+++ b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
@@ -436,9 +436,6 @@ void tst_QHttpHeaders::headerValueField()
QRegularExpression re("HTTP header value contained illegal character*");
TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x08" "bar"); // BS
TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x1B" "bar"); // ESC
- // Disallowed UTF-8 characters
- TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"€");
- TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
// Disallowed UTF-16 characters
TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"€");
TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
diff --git a/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
index 2bd4ca05f3..f891b16963 100644
--- a/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
+++ b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
Binary files differ
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index a26d2c809b..06d0847acd 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -6682,7 +6682,6 @@ void tst_QNetworkReply::httpConnectionCount()
int pendingConnectionCount = 0;
- using namespace std::chrono_literals;
const auto newPendingConnection = [&server]() { return server->hasPendingConnections(); };
// If we have http2 enabled then the second connection will take a little
// longer to be established because we will wait for the first one to finish
@@ -10300,6 +10299,9 @@ void tst_QNetworkReply::contentEncoding()
#if QT_CONFIG(http)
void tst_QNetworkReply::contentEncodingBigPayload_data()
{
+#if Q_PROCESSOR_WORDSIZE <= 4
+ QSKIP("Allocating 4GB leads to std::bad_alloc on a 32b architecture");
+#endif
QTest::addColumn<QByteArray>("encoding");
QTest::addColumn<QString>("path");
QTest::addColumn<qint64>("expectedSize");
@@ -10337,7 +10339,7 @@ void tst_QNetworkReply::contentEncodingBigPayload()
request.setDecompressedSafetyCheckThreshold(-1);
QNetworkReplyPtr reply(manager.get(request));
- QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15s);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QFETCH(qint64, expectedSize);
@@ -10462,7 +10464,7 @@ void tst_QNetworkReply::contentEncodingError()
QUrl(QLatin1String("http://localhost:%1").arg(QString::number(server.serverPort()))));
QNetworkReplyPtr reply(manager.get(request));
- QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15s);
QTEST(reply->error(), "expectedError");
}
@@ -10532,7 +10534,7 @@ void tst_QNetworkReply::notFoundWithCompression()
QUrl(QLatin1String("http://localhost:%1").arg(QString::number(server.serverPort()))));
QNetworkReplyPtr reply(manager.get(request));
- QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15s);
QCOMPARE(reply->error(), QNetworkReply::ContentNotFoundError);
QFETCH(QByteArray, expected);
diff --git a/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt b/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt
new file mode 100644
index 0000000000..13a60afb13
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt
@@ -0,0 +1,9 @@
+qt_internal_add_test(tst_qnetworkreply_local
+ SOURCES
+ minihttpserver.h
+ tst_qnetworkreply_local.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::NetworkPrivate
+ BUNDLE_ANDROID_OPENSSL_LIBS
+)
diff --git a/tests/auto/network/access/qnetworkreply_local/minihttpserver.h b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h
new file mode 100644
index 0000000000..eb0697a6f8
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h
@@ -0,0 +1,246 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MINIHTTPSERVER_H
+#define MINIHTTPSERVER_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtNetwork/qlocalsocket.h>
+#if QT_CONFIG(ssl)
+# include <QtNetwork/qsslsocket.h>
+#endif
+#if QT_CONFIG(localserver)
+# include <QtNetwork/qlocalserver.h>
+#endif
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qhash.h>
+
+#include <utility>
+
+static inline QByteArray default200Response()
+{
+ return QByteArrayLiteral("HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: 12\r\n"
+ "\r\n"
+ "Hello World!");
+}
+class MiniHttpServerV2 : public QObject
+{
+ Q_OBJECT
+
+public:
+ struct State;
+
+#if QT_CONFIG(localserver)
+ void bind(QLocalServer *server)
+ {
+ Q_ASSERT(!localServer);
+ localServer = server;
+ connect(server, &QLocalServer::newConnection, this,
+ &MiniHttpServerV2::incomingLocalConnection);
+ }
+#endif
+
+ void bind(QTcpServer *server)
+ {
+ Q_ASSERT(!tcpServer);
+ tcpServer = server;
+ connect(server, &QTcpServer::pendingConnectionAvailable, this,
+ &MiniHttpServerV2::incomingConnection);
+ }
+
+ void setDataToTransmit(QByteArray data) { dataToTransmit = std::move(data); }
+
+ void clearServerState()
+ {
+ auto copy = std::exchange(clientStates, {});
+ for (auto [socket, _] : copy.asKeyValueRange()) {
+ if (auto *tcpSocket = qobject_cast<QTcpSocket *>(socket))
+ tcpSocket->disconnectFromHost();
+ else if (auto *localSocket = qobject_cast<QLocalSocket *>(socket))
+ localSocket->disconnectFromServer();
+ else
+ Q_UNREACHABLE_RETURN();
+ socket->deleteLater();
+ }
+ }
+
+ bool hasPendingConnections() const
+ {
+ return
+#if QT_CONFIG(localserver)
+ (localServer && localServer->hasPendingConnections()) ||
+#endif
+ (tcpServer && tcpServer->hasPendingConnections());
+ }
+
+ QString addressForScheme(QStringView scheme) const
+ {
+ using namespace Qt::StringLiterals;
+ if (scheme.startsWith("unix"_L1) || scheme.startsWith("local"_L1)) {
+#if QT_CONFIG(localserver)
+ if (localServer)
+ return localServer->serverName();
+#endif
+ } else if (scheme == "http"_L1) {
+ if (tcpServer)
+ return "%1:%2"_L1.arg(tcpServer->serverAddress().toString(),
+ QString::number(tcpServer->serverPort()));
+ }
+ return {};
+ }
+
+ QList<State> peerStates() const { return clientStates.values(); }
+
+protected:
+#if QT_CONFIG(localserver)
+ void incomingLocalConnection()
+ {
+ auto *socket = localServer->nextPendingConnection();
+ connectSocketSignals(socket);
+ }
+#endif
+
+ void incomingConnection()
+ {
+ auto *socket = tcpServer->nextPendingConnection();
+ connectSocketSignals(socket);
+ }
+
+ void reply(QIODevice *socket)
+ {
+ Q_ASSERT(socket);
+ if (dataToTransmit.isEmpty()) {
+ emit socket->bytesWritten(0); // emulate having written the data
+ return;
+ }
+ if (!stopTransfer)
+ socket->write(dataToTransmit);
+ }
+
+private:
+ void connectSocketSignals(QIODevice *socket)
+ {
+ connect(socket, &QIODevice::readyRead, this, [this, socket]() { readyReadSlot(socket); });
+ connect(socket, &QIODevice::bytesWritten, this,
+ [this, socket]() { bytesWrittenSlot(socket); });
+#if QT_CONFIG(ssl)
+ if (auto *sslSocket = qobject_cast<QSslSocket *>(socket))
+ connect(sslSocket, &QSslSocket::sslErrors, this, &MiniHttpServerV2::slotSslErrors);
+#endif
+
+ if (auto *tcpSocket = qobject_cast<QTcpSocket *>(socket)) {
+ connect(tcpSocket, &QAbstractSocket::errorOccurred, this, &MiniHttpServerV2::slotError);
+ } else if (auto *localSocket = qobject_cast<QLocalSocket *>(socket)) {
+ connect(localSocket, &QLocalSocket::errorOccurred, this,
+ [this](QLocalSocket::LocalSocketError error) {
+ slotError(QAbstractSocket::SocketError(error));
+ });
+ } else {
+ Q_UNREACHABLE_RETURN();
+ }
+ }
+
+ void parseContentLength(State &st, QByteArrayView header)
+ {
+ qsizetype index = header.indexOf("\r\ncontent-length:");
+ if (index == -1)
+ return;
+ st.foundContentLength = true;
+
+ index += sizeof("\r\ncontent-length:") - 1;
+ const auto *end = std::find(header.cbegin() + index, header.cend(), '\r');
+ QByteArrayView num = header.mid(index, std::distance(header.cbegin() + index, end));
+ bool ok = false;
+ st.contentLength = num.toInt(&ok);
+ if (!ok)
+ st.contentLength = -1;
+ }
+
+private slots:
+#if QT_CONFIG(ssl)
+ void slotSslErrors(const QList<QSslError> &errors)
+ {
+ QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(currentClient);
+ qDebug() << "slotSslErrors" << currentClient->errorString() << errors;
+ }
+#endif
+ void slotError(QAbstractSocket::SocketError err)
+ {
+ QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(currentClient);
+ qDebug() << "slotError" << err << currentClient->errorString();
+ }
+
+public slots:
+
+ void readyReadSlot(QIODevice *socket)
+ {
+ if (stopTransfer)
+ return;
+ State &st = clientStates[socket];
+ st.receivedData += socket->readAll();
+ const qsizetype doubleEndlPos = st.receivedData.indexOf("\r\n\r\n");
+
+ if (doubleEndlPos != -1) {
+ const qsizetype endOfHeader = doubleEndlPos + 4;
+ st.contentRead = st.receivedData.size() - endOfHeader;
+
+ if (!st.checkedContentLength) {
+ parseContentLength(st, QByteArrayView(st.receivedData).first(endOfHeader));
+ st.checkedContentLength = true;
+ }
+
+ if (st.contentRead < st.contentLength)
+ return;
+
+ // multiple requests incoming, remove the bytes of the current one
+ if (multiple)
+ st.receivedData.remove(0, endOfHeader);
+
+ reply(socket);
+ }
+ }
+
+ void bytesWrittenSlot(QIODevice *socket)
+ {
+ // Disconnect and delete in next cycle (else Windows clients will fail with
+ // RemoteHostClosedError).
+ if (doClose && socket->bytesToWrite() == 0) {
+ disconnect(socket, nullptr, this, nullptr);
+ socket->deleteLater();
+ }
+ }
+
+private:
+ QByteArray dataToTransmit = default200Response();
+
+ QTcpServer *tcpServer = nullptr;
+#if QT_CONFIG(localserver)
+ QLocalServer *localServer = nullptr;
+#endif
+
+ QHash<QIODevice *, State> clientStates;
+
+public:
+ struct State
+ {
+ QByteArray receivedData;
+ qsizetype contentLength = 0;
+ qsizetype contentRead = 0;
+ bool checkedContentLength = false;
+ bool foundContentLength = false;
+ };
+
+ bool doClose = true;
+ bool multiple = false;
+ bool stopTransfer = false;
+};
+
+#endif // MINIHTTPSERVER_H
diff --git a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp
new file mode 100644
index 0000000000..6d78c81593
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp
@@ -0,0 +1,177 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtTest/qtest.h>
+
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+#include "minihttpserver.h"
+
+using namespace Qt::StringLiterals;
+
+/*
+ The tests here are meant to be self-contained, using servers in the same
+ process if needed. This enables externals to more easily run the tests too.
+*/
+class tst_QNetworkReply_local : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase_data();
+
+ void get();
+ void post();
+
+#if QT_CONFIG(localserver)
+ void fullServerName_data();
+ void fullServerName();
+#endif
+};
+
+void tst_QNetworkReply_local::initTestCase_data()
+{
+ QTest::addColumn<QString>("scheme");
+
+ QTest::newRow("http") << "http";
+#if QT_CONFIG(localserver)
+ QTest::newRow("unix") << "unix+http";
+ QTest::newRow("local") << "local+http"; // equivalent to unix, but test that it works
+#endif
+}
+
+static std::unique_ptr<MiniHttpServerV2> getServerForCurrentScheme()
+{
+ auto server = std::make_unique<MiniHttpServerV2>();
+ QFETCH_GLOBAL(QString, scheme);
+ if (scheme.startsWith("unix"_L1) || scheme.startsWith("local"_L1)) {
+#if QT_CONFIG(localserver)
+ QLocalServer *localServer = new QLocalServer(server.get());
+ localServer->listen(u"qt_networkreply_test_"_s
+ % QLatin1StringView(QTest::currentTestFunction())
+ % QString::number(QCoreApplication::applicationPid()));
+ server->bind(localServer);
+#endif
+ } else if (scheme == "http") {
+ QTcpServer *tcpServer = new QTcpServer(server.get());
+ tcpServer->listen(QHostAddress::LocalHost, 0);
+ server->bind(tcpServer);
+ }
+ return server;
+}
+
+static QUrl getUrlForCurrentScheme(MiniHttpServerV2 *server)
+{
+ QFETCH_GLOBAL(QString, scheme);
+ const QString address = server->addressForScheme(scheme);
+ const QString urlString = QLatin1StringView("%1://%2").arg(scheme, address);
+ return { urlString };
+}
+
+void tst_QNetworkReply_local::get()
+{
+ std::unique_ptr<MiniHttpServerV2> server = getServerForCurrentScheme();
+ const QUrl url = getUrlForCurrentScheme(server.get());
+
+ QNetworkAccessManager manager;
+ std::unique_ptr<QNetworkReply> reply(manager.get(QNetworkRequest(url)));
+
+ const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); });
+ QVERIFY(res);
+
+ QCOMPARE(reply->readAll(), QByteArray("Hello World!"));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+}
+
+void tst_QNetworkReply_local::post()
+{
+ std::unique_ptr<MiniHttpServerV2> server = getServerForCurrentScheme();
+ const QUrl url = getUrlForCurrentScheme(server.get());
+
+ QNetworkAccessManager manager;
+ const QByteArray payload = "Hello from the other side!"_ba;
+ QNetworkRequest req(url);
+ req.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
+ std::unique_ptr<QNetworkReply> reply(manager.post(req, payload));
+
+ const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); });
+ QVERIFY(res);
+
+ QCOMPARE(reply->readAll(), QByteArray("Hello World!"));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+
+ auto states = server->peerStates();
+ QCOMPARE(states.size(), 1);
+
+ const auto &firstRequest = states.at(0);
+
+ QVERIFY(firstRequest.checkedContentLength);
+ QCOMPARE(firstRequest.contentLength, payload.size());
+ QCOMPARE_GT(firstRequest.receivedData.size(), payload.size() + 4);
+ QCOMPARE(firstRequest.receivedData.last(payload.size() + 4), "\r\n\r\n" + payload);
+}
+
+#if QT_CONFIG(localserver)
+void tst_QNetworkReply_local::fullServerName_data()
+{
+#if defined(Q_OS_ANDROID) || defined(QT_PLATFORM_UIKIT)
+ QSKIP("While partially supported, the test as-is doesn't make sense on this platform.");
+#else
+
+ QTest::addColumn<QString>("hostAndPath");
+
+ QTest::newRow("dummy-host") << u"://irrelevant/test"_s;
+ QTest::newRow("no-host") << u":///test"_s;
+#endif
+}
+
+void tst_QNetworkReply_local::fullServerName()
+{
+ QFETCH_GLOBAL(QString, scheme);
+ if (!scheme.startsWith("unix"_L1) && !scheme.startsWith("local"_L1))
+ return; // only relevant for local sockets
+
+ MiniHttpServerV2 server;
+ QLocalServer localServer;
+
+ QString path;
+#ifdef Q_OS_WIN
+ path = uR"(\\.\pipe\qt_networkreply_test_fullServerName)"_s
+ % QString::number(QCoreApplication::applicationPid());
+#else
+ path = u"/tmp/qt_networkreply_test_fullServerName"_s
+ % QString::number(QCoreApplication::applicationPid()) % u".sock"_s;
+#endif
+
+ QVERIFY(localServer.listen(path));
+ server.bind(&localServer);
+
+ QFETCH(QString, hostAndPath);
+ QUrl url(scheme % hostAndPath);
+ QNetworkRequest req(url);
+ req.setAttribute(QNetworkRequest::FullLocalServerNameAttribute, path);
+
+ QNetworkAccessManager manager;
+ std::unique_ptr<QNetworkReply> reply(manager.get(req));
+
+ const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); });
+ QVERIFY(res);
+
+ QCOMPARE(reply->readAll(), QByteArray("Hello World!"));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+
+ const QByteArray receivedData = server.peerStates().at(0).receivedData;
+ const QByteArray expectedGet = "GET " % url.path().toUtf8() % " HTTP/1.1\r\n";
+ QVERIFY(receivedData.startsWith(expectedGet));
+
+ const QByteArray expectedHost = "host: " % url.host().toUtf8() % "\r\n";
+ QVERIFY(receivedData.contains(expectedHost));
+}
+#endif
+
+QTEST_MAIN(tst_QNetworkReply_local)
+
+#include "tst_qnetworkreply_local.moc"
+#include "moc_minihttpserver.cpp"
diff --git a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
index 0b6d6f339b..f0b02ae91d 100644
--- a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
+++ b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
@@ -3,8 +3,10 @@
#include <QTest>
+#if QT_CONFIG(http)
#include <QtNetwork/QHttp1Configuration>
#include <QtNetwork/QHttp2Configuration>
+#endif
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkCookie>
diff --git a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
index 141ca25021..e5bbf3467c 100644
--- a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
+++ b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp
@@ -197,6 +197,14 @@ void tst_QNetworkInterface::localAddress_data()
} else if (!ipv6 || entry.prefixLength() != 64) {
continue;
} else {
+#ifdef Q_OS_ANDROID
+ // Android seem to not allow IPv6 connection from interfaces other than wlan,
+ // if it's connected, and wlan is connected by default on Android emulators,
+ // so prefer selecting wlan in this test.
+ const QString scopeId = addr.scopeId();
+ if (!scopeId.isEmpty() && !scopeId.startsWith("wlan"))
+ continue;
+#endif
// add a random node in this IPv6 network
quint64 randomid = qFromBigEndian(Q_UINT64_C(0x8f41f072e5733caa));
QIPv6Address ip6 = addr.toIPv6Address();