Compare commits

...

21 Commits

Author SHA1 Message Date
bdm-oslandia
04ed226dc0 TEMP: add assert to this code as it seems useless 2025-06-27 09:19:57 +02:00
bdm-oslandia
7ce305f67f test(qgsdatasourceuri): add uri in uri test 2025-06-27 09:19:57 +02:00
bdm-oslandia
7579c7c6bc fix(httpheaders): add missing urlencode to headers when updating QUrlQuery 2025-06-27 09:19:57 +02:00
bdm-oslandia
230b4aa45e fix(QgsProjectStorageRegistry): project uri can be urlencoded and starts with XXX%3A 2025-06-27 09:19:57 +02:00
Max Tobias Weber
6fceaee3d0 fix double URL-encoding issue with curly braces in z,x,y placeholders of VectorTile Sources in QgsDataSurceUri 2025-06-27 09:19:57 +02:00
Max Tobias Weber
46ead260af fix most tests in tests/src/core/testqgsvectortilelayer.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
e02db8c490 fix tests tests/src/core/testqgshttpheaders.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
71fab90c74 fix test tests/src/server/wms/test_qgsserver_wms_parameters.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
2b366b066e fix test tests/src/python/test_qgsvectortile.py 2025-06-27 09:19:57 +02:00
Max Tobias Weber
a00fb44415 fix test tests/src/core/testqgsvectortileconnection.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
1e8aa1b60a fix test tests/src/core/testqgstiledsceneconnection.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
1e189669ab fix test tests/src/core/testqgssensorthingsconnection.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
987bfa729b fix test tests/src/core/testqgshttpheaders.cpp 2025-06-27 09:19:57 +02:00
Max Tobias Weber
ebf2256cdb adjust tests to QgsDataSourceUri full value URL-encoding 2025-06-27 09:19:56 +02:00
Max Tobias Weber
c44a773399 add decoding step before adding the "url" value to QgsDataSourceUri in TestQgsIdentify::identifyVectorTile(), as QgsDataSourceUri now reliably returns the values that were provided. 2025-06-27 09:19:56 +02:00
Max Tobias Weber
3f5ea98662 make sure QgsDataSourceUri values are FullyUrlDecoded when parsing QgsDataSourceUri strings in QgsDataSourceUri::setEncodedUri 2025-06-27 09:19:56 +02:00
Max Tobias Weber
5866c60b19 move QUrl::toPercentEncoding and QUrl::fromPercentEncoding steps from QgsDataSourceUri param setter and getter functions to URI serialization QgsDataSourceUri::encodedUri() 2025-06-27 09:19:56 +02:00
Max Tobias Weber
03eb6aa338 fix faulty assertion of URI parameter values not being fully URL-encoded in tests/src/providers/testqgswmsprovider.cpp 2025-06-27 09:19:56 +02:00
Max Tobias Weber
2f1ccd7a7d fix faulty assertion in tests/src/core/testqgsdatasourceuri.cpp
URl-encoded in URL-parameter values characters will be URL-decoded on retrieval.
2025-06-27 09:19:56 +02:00
Max Tobias Weber
96e64bfba1 ensure values in QgsDataSourceUri are URL-encoded when assigned and properly decoded when retrieved, preventing any potential loss of information. 2025-06-27 09:19:56 +02:00
Max Tobias Weber
d73288ebc7 make fromPercentEncoding in qgswmsprovider conditional.
We should not decode an entire URL if it includes parameters, as special characters in the query string (like `&`, `=`) must remain URL-encoded to avoid breaking the structure of the request. Within the URL-parameter values must never be URL-decoded as it leads to a loss of information, this is also the case if only non reserved characters are decoded. There is no way to know whether those characters have been encoded in the first place.
2025-06-27 09:19:56 +02:00
17 changed files with 150 additions and 54 deletions

View File

@ -73,7 +73,7 @@ bool QgsHttpHeaders::updateUrlQuery( QUrlQuery &uri ) const
{
for ( auto ite = mHeaders.constBegin(); ite != mHeaders.constEnd(); ++ite )
{
uri.addQueryItem( QgsHttpHeaders::PARAM_PREFIX + ite.key().toUtf8(), ite.value().toString().toUtf8() );
uri.addQueryItem( QgsHttpHeaders::PARAM_PREFIX + ite.key().toUtf8(), QUrl::toPercentEncoding( ite.value().toString() ) );
}
return true;
}

View File

@ -33,8 +33,7 @@ QgsProjectStorage *QgsProjectStorageRegistry::projectStorageFromUri( const QStri
for ( auto it = mBackends.constBegin(); it != mBackends.constEnd(); ++it )
{
QgsProjectStorage *storage = it.value();
const QString scheme = storage->type() + ':';
if ( uri.startsWith( scheme ) )
if ( uri.startsWith( storage->type() + ':' ) || uri.startsWith( storage->type() + "%3A" ) )
return storage;
}

View File

@ -701,17 +701,17 @@ QByteArray QgsDataSourceUri::encodedUri() const
QUrlQuery url;
for ( auto it = mParams.constBegin(); it != mParams.constEnd(); ++it )
{
url.addQueryItem( it.key(), it.value() );
url.addQueryItem( it.key(), QUrl::toPercentEncoding( it.value() ) );
}
if ( !mUsername.isEmpty() )
url.addQueryItem( QStringLiteral( "username" ), mUsername );
url.addQueryItem( QStringLiteral( "username" ), QUrl::toPercentEncoding( mUsername ) );
if ( !mPassword.isEmpty() )
url.addQueryItem( QStringLiteral( "password" ), mPassword );
url.addQueryItem( QStringLiteral( "password" ), QUrl::toPercentEncoding( mPassword ) );
if ( !mAuthConfigId.isEmpty() )
url.addQueryItem( QStringLiteral( "authcfg" ), mAuthConfigId );
url.addQueryItem( QStringLiteral( "authcfg" ), QUrl::toPercentEncoding( mAuthConfigId ) );
mHttpHeaders.updateUrlQuery( url );
@ -731,7 +731,7 @@ void QgsDataSourceUri::setEncodedUri( const QByteArray &uri )
mHttpHeaders.setFromUrlQuery( query );
const auto constQueryItems = query.queryItems();
const auto constQueryItems = query.queryItems( QUrl::ComponentFormattingOption::FullyDecoded );
for ( const QPair<QString, QString> &item : constQueryItems )
{
if ( !item.first.startsWith( QgsHttpHeaders::PARAM_PREFIX ) && item.first != QgsHttpHeaders::KEY_REFERER )
@ -928,7 +928,7 @@ QString QgsDataSourceUri::param( const QString &key ) const
else if ( key == QLatin1String( "authcfg" ) && !mAuthConfigId.isEmpty() )
return mAuthConfigId;
return mParams.value( key );
return mParams.value( key ).toUtf8();
}
QStringList QgsDataSourceUri::params( const QString &key ) const

View File

@ -147,7 +147,7 @@ QString QgsVectorTileProviderMetadata::absoluteToRelativeUri( const QString &uri
// relative path will become "file:./x.txt"
const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString( QUrl::DecodeReserved ) );
return dsUri.encodedUri();
}
}

View File

@ -316,7 +316,7 @@ QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QStri
// relative path will become "file:./x.txt"
const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString( QUrl::DecodeReserved ) );
return dsUri.encodedUri();
}
@ -335,7 +335,7 @@ QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QStri
{
const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString( QUrl::DecodeReserved ) );
return dsUri.encodedUri();
}

View File

@ -276,7 +276,12 @@ QgsWmsProvider::QgsWmsProvider( QString const &uri, const ProviderOptions &optio
QString QgsWmsProvider::prepareUri( QString uri )
{
// some services provide a percent/url encoded (legend) uri string, always decode here
uri = QUrl::fromPercentEncoding( uri.toUtf8() );
if ( uri.startsWith( "https%3A%2F%2F" ) || uri.startsWith( "http%3A%2F%2F" ) )
{
qDebug() << "========================================================================= QgsWmsProvider::prepareUri" << uri;
uri = QUrl::fromPercentEncoding( uri.toUtf8() );
Q_ASSERT( false );
}
if ( isUrlForWMTS( uri ) )
{

View File

@ -933,7 +933,9 @@ void TestQgsIdentify::identifyVectorTile()
const QString vtPath = QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/vector_tile/{z}-{x}-{y}.pbf" );
QgsDataSourceUri dsUri;
dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( vtPath ).toString() );
// The values need to be passed to QgsDataSourceUri::setParam() in the same format they are expected to be retrieved.
// QUrl::fromPercentEncoding() is needed here because QUrl::fromLocalFile(vtPath).toString() returns the curly braces in an URL-encoded format.
dsUri.setParam( QStringLiteral( "url" ), QUrl::fromPercentEncoding( QUrl::fromLocalFile( vtPath ).toString().toUtf8() ) );
QgsVectorTileLayer *tempLayer = new QgsVectorTileLayer( dsUri.encodedUri(), QStringLiteral( "testlayer" ) );
QVERIFY( tempLayer->isValid() );

View File

@ -38,6 +38,7 @@ class TestQgsDataSourceUri : public QObject
void checkParameterKeys();
void checkRemovePassword();
void checkUnicodeUri();
void checkUriInUri();
};
void TestQgsDataSourceUri::checkparser_data()
@ -775,7 +776,7 @@ void TestQgsDataSourceUri::checkAuthParams()
// issue GH #53654
QgsDataSourceUri uri5;
uri5.setEncodedUri( QStringLiteral( "zmax=14&zmin=0&styleUrl=http://localhost:8000/&f=application%2Fvnd.geoserver.mbstyle%2Bjson" ) );
QCOMPARE( uri5.param( QStringLiteral( "f" ) ), QStringLiteral( "application%2Fvnd.geoserver.mbstyle%2Bjson" ) );
QCOMPARE( uri5.param( QStringLiteral( "f" ) ), QStringLiteral( "application/vnd.geoserver.mbstyle+json" ) );
uri5.setEncodedUri( QStringLiteral( "zmax=14&zmin=0&styleUrl=http://localhost:8000/&f=application/vnd.geoserver.mbstyle+json" ) );
QCOMPARE( uri5.param( QStringLiteral( "f" ) ), QStringLiteral( "application/vnd.geoserver.mbstyle+json" ) );
@ -822,6 +823,83 @@ void TestQgsDataSourceUri::checkUnicodeUri()
QCOMPARE( uri.param( QStringLiteral( "url" ) ), QStringLiteral( "file:///directory/テスト.mbtiles" ) );
}
void TestQgsDataSourceUri::checkUriInUri()
{
QString dataUri = QStringLiteral( "dpiMode=7&url=%1&SERVICE=WMS&REQUEST=GetCapabilities&username=username&password=qgis%C3%A8%C3%A9" );
// If the 'url' field references a QGIS server then the 'MAP' parameter can contain an url to the project file.
// When the project is saved in a postgresql db, the connection url will also contains '&' and '='.
{
QgsDataSourceUri uri;
// here the project url is encoded but the whole serverUrl is not encoded.
// The OGC server will receive a call with this url: http://localhost:8000/ows/?MAP=postgresql://?service=qgis_test&dbname&schema=project&project=luxembourg&SERVICE=WMS&REQUEST=GetCapabilities
// from the OGC server POV the 'schema' and 'project' keys will be parsed as main query parameters for 'http://localhost:8000/ows/?'
// and not associated to the project file uri.
QString project = "postgresql://?service=qgis_test&dbname&schema=project&project=luxembourg";
QString projectEnc = QUrl::toPercentEncoding( project );
QString serverUrl = QString( "http://localhost:8000/ows/?MAP=%1" );
uri.setEncodedUri( dataUri.arg( serverUrl.arg( projectEnc ) ) );
QCOMPARE( uri.param( QStringLiteral( "username" ) ), QStringLiteral( "username" ) );
QCOMPARE( uri.username(), QStringLiteral( "username" ) );
QCOMPARE( uri.param( QStringLiteral( "password" ) ), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri.password(), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri.param( QStringLiteral( "SERVICE" ) ), QStringLiteral( "WMS" ) );
QCOMPARE( uri.param( QStringLiteral( "REQUEST" ) ), QStringLiteral( "GetCapabilities" ) );
// not enough encoded at the beginning ==> bad encoding at the end
QCOMPARE( uri.param( QStringLiteral( "url" ) ), serverUrl.arg( project ) );
QgsDataSourceUri uri2;
// here the project url is encoded and the whole serverUrl is also encoded.
// The OGC server will receive a call with this url: http://localhost:8000/ows/?MAP=postgresql%3A%2F%2F%3Fservice%3Dqgis_test%26dbname%26schema%3Dproject%26project%3Dluxembourg&SERVICE=WMS&REQUEST=GetCapabilities
// and will be able to decode all parameters
QString serverUrlEnc = QUrl::toPercentEncoding( serverUrl.arg( projectEnc ) );
uri2.setEncodedUri( dataUri.arg( serverUrlEnc ) );
QCOMPARE( uri2.param( QStringLiteral( "username" ) ), QStringLiteral( "username" ) );
QCOMPARE( uri2.username(), QStringLiteral( "username" ) );
QCOMPARE( uri2.param( QStringLiteral( "password" ) ), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri2.password(), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri2.param( QStringLiteral( "SERVICE" ) ), QStringLiteral( "WMS" ) );
QCOMPARE( uri2.param( QStringLiteral( "REQUEST" ) ), QStringLiteral( "GetCapabilities" ) );
QCOMPARE( uri2.param( QStringLiteral( "url" ) ), serverUrl.arg( projectEnc ) );
}
// same as above but with extra param at the end of the
{
QgsDataSourceUri uri;
// here the project url is encoded but the whole serverUrl is not encoded.
// The OGC server will receive a call with this url: https://titiler.xyz/cog/tiles/WebMercatorQuad/16/34060/23336@1x?url=https://data.geo.admin.ch/ch.swisstopo.swissalti3d/swissalti3d_2019_2573-1085/swissalti3d_2019_2573-1085_0.5_2056_5728.tif&bidx=1&rescale=1600%2C2100&colormap_name=gist_earth
// from the OGC server POV the 'rescale' and 'colormap_name' keys could be parsed as sub query parameters for 'https://data.geo.admin.ch/'
QString project = "https://data.geo.admin.ch/ch.swisstopo.swissalti3d/swissalti3d_2019_2573-1085/swissalti3d_2019_2573-1085_0.5_2056_5728.tif";
QString projectEnc = QUrl::toPercentEncoding( project );
QString extraParam = "&bidx=1&rescale=1600%2C2100&colormap_name=gist_earth";
QString serverUrl = QString( "https://titiler.xyz/cog/tiles/WebMercatorQuad/16/34060/23336@1x?url=%1" );
uri.setEncodedUri( dataUri.arg( serverUrl.arg( projectEnc ) + extraParam ) );
QCOMPARE( uri.param( QStringLiteral( "username" ) ), QStringLiteral( "username" ) );
QCOMPARE( uri.username(), QStringLiteral( "username" ) );
QCOMPARE( uri.param( QStringLiteral( "password" ) ), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri.password(), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri.param( QStringLiteral( "SERVICE" ) ), QStringLiteral( "WMS" ) );
QCOMPARE( uri.param( QStringLiteral( "REQUEST" ) ), QStringLiteral( "GetCapabilities" ) );
// not enough encoded at the beginning ==> bad encoding at the end
QCOMPARE( uri.param( QStringLiteral( "url" ) ), serverUrl.arg( project ) );
QgsDataSourceUri uri2;
// here the project url is encoded and the whole serverUrl is also encoded.
// The OGC server will receive a call with this url: https://titiler.xyz/cog/tiles/WebMercatorQuad/16/34060/23336@1x?url=https%3A%2F%2Fdata.geo.admin.ch%2Fch.swisstopo.swissalti3d%2Fswissalti3d_2019_2573-1085%2Fswissalti3d_2019_2573-1085_0.5_2056_5728.tif&bidx=1&rescale=1600%2C2100&colormap_name=gist_earth
// and will be able to decode all parameters
QString serverUrlEnc = QUrl::toPercentEncoding( serverUrl.arg( projectEnc ) + extraParam );
uri2.setEncodedUri( dataUri.arg( serverUrlEnc ) );
QCOMPARE( uri2.param( QStringLiteral( "username" ) ), QStringLiteral( "username" ) );
QCOMPARE( uri2.username(), QStringLiteral( "username" ) );
QCOMPARE( uri2.param( QStringLiteral( "password" ) ), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri2.password(), QStringLiteral( "qgisèé" ) );
QCOMPARE( uri2.param( QStringLiteral( "SERVICE" ) ), QStringLiteral( "WMS" ) );
QCOMPARE( uri2.param( QStringLiteral( "REQUEST" ) ), QStringLiteral( "GetCapabilities" ) );
QCOMPARE( uri2.param( QStringLiteral( "url" ) ), serverUrl.arg( projectEnc ) + extraParam );
}
}
QGSTEST_MAIN( TestQgsDataSourceUri )
#include "testqgsdatasourceuri.moc"

View File

@ -59,7 +59,7 @@ void TestQgsGdalCloudConnection::encodeDecode()
data.rootPath = QStringLiteral( "some/path" );
data.credentialOptions = QVariantMap { { "pw", QStringLiteral( "xxxx" ) }, { "key", QStringLiteral( "yyy" ) } };
QCOMPARE( QgsGdalCloudProviderConnection::encodedUri( data ), QStringLiteral( "container=my_container&credentialOptions=key%3Dyyy%7Cpw%3Dxxxx&handler=vsis3&rootPath=some/path" ) );
QCOMPARE( QgsGdalCloudProviderConnection::encodedUri( data ), QStringLiteral( "container=my_container&credentialOptions=key%3Dyyy%7Cpw%3Dxxxx&handler=vsis3&rootPath=some%2Fpath" ) );
const QgsGdalCloudProviderConnection::Data data2 = QgsGdalCloudProviderConnection::decodedUri( QStringLiteral( "container=my_container&credentialOptions=key%3Dyyy%7Cpw%3Dxxxx&handler=vsis3&rootPath=some/path" ) );
QCOMPARE( data2.vsiHandler, QStringLiteral( "vsis3" ) );
@ -94,7 +94,7 @@ void TestQgsGdalCloudConnection::testConnections()
// retrieve stored connection
conn = QgsGdalCloudProviderConnection( QStringLiteral( "my connection" ) );
QCOMPARE( conn.uri(), QStringLiteral( "container=my_container&credentialOptions=key%3Dyyy%7Cpw%3Dxxxx&handler=vsis3&rootPath=some/path" ) );
QCOMPARE( conn.uri(), QStringLiteral( "container=my_container&credentialOptions=key%3Dyyy%7Cpw%3Dxxxx&handler=vsis3&rootPath=some%2Fpath" ) );
// add a second connection
QgsGdalCloudProviderConnection::Data data2;

View File

@ -187,11 +187,14 @@ void TestQgsHttpheaders::createQgsOwsConnection()
QgsOwsConnection ows( "service", "name" );
QCOMPARE( ows.connectionInfo(), ",authcfg=,referer=http://test.com" );
QCOMPARE( ows.uri().encodedUri(), "url&http-header:other_http_header=value&http-header:referer=http://test.com" );
if ( ows.uri().encodedUri().startsWith( "url=" ) )
QCOMPARE( ows.uri().encodedUri(), "url=&http-header:other_http_header=value&http-header:referer=http%3A%2F%2Ftest.com" );
else
QCOMPARE( ows.uri().encodedUri(), "url&http-header:other_http_header=value&http-header:referer=http%3A%2F%2Ftest.com" );
QgsDataSourceUri uri( QString( "https://www.ogc.org/?p1=v1" ) );
QgsDataSourceUri uri2 = ows.addWmsWcsConnectionSettings( uri, "service", "name" );
QCOMPARE( uri2.encodedUri(), "https://www.ogc.org/?p1=v1&http-header:other_http_header=value&http-header:referer=http://test.com" );
QCOMPARE( uri2.encodedUri(), "https://www.ogc.org/?p1=v1&http-header:other_http_header=value&http-header:referer=http%3A%2F%2Ftest.com" );
// check space separated string
QCOMPARE( uri2.uri(), " https://www.ogc.org/?p1='v1' http-header:other_http_header='value' http-header:referer='http://test.com' referer='http://test.com'" );
@ -199,7 +202,7 @@ void TestQgsHttpheaders::createQgsOwsConnection()
QgsDataSourceUri uri3( uri2.uri() );
QCOMPARE( uri3.httpHeader( QgsHttpHeaders::KEY_REFERER ), "http://test.com" );
QCOMPARE( uri3.httpHeader( "other_http_header" ), "value" );
QCOMPARE( uri3.encodedUri(), "https://www.ogc.org/?p1=v1&referer=http://test.com&http-header:other_http_header=value&http-header:referer=http://test.com" );
QCOMPARE( uri3.encodedUri(), "https://www.ogc.org/?p1=v1&referer=http%3A%2F%2Ftest.com&http-header:other_http_header=value&http-header:referer=http%3A%2F%2Ftest.com" );
}

View File

@ -60,7 +60,7 @@ void TestQgsSensorThingsConnection::encodeDecode()
data.password = QStringLiteral( "my_pw" );
data.httpHeaders.insert( QStringLiteral( "my_header" ), QStringLiteral( "value" ) );
QCOMPARE( QgsSensorThingsProviderConnection::encodedUri( data ), QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( QgsSensorThingsProviderConnection::encodedUri( data ), QStringLiteral( "url=http%3A%2F%2Ftesturl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( QgsSensorThingsProviderConnection::encodedLayerUri( data ), QStringLiteral( "user='my_user' password='my_pw' authcfg=my_auth url='http://testurl' http-header:my_header='value'" ) );
const QgsSensorThingsProviderConnection::Data data2 = QgsSensorThingsProviderConnection::decodedUri( QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
@ -93,7 +93,7 @@ void TestQgsSensorThingsConnection::testConnections()
// retrieve stored connection
conn = QgsSensorThingsProviderConnection( QStringLiteral( "my connection" ) );
QCOMPARE( conn.uri(), QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( conn.uri(), QStringLiteral( "url=http%3A%2F%2Ftesturl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
// add a second connection
QgsSensorThingsProviderConnection::Data data2;
@ -104,7 +104,7 @@ void TestQgsSensorThingsConnection::testConnections()
data2.httpHeaders.insert( QStringLiteral( "my_header" ), QStringLiteral( "value2" ) );
// construct connection using encoded uri
QgsSensorThingsProviderConnection conn2( QgsSensorThingsProviderConnection::encodedUri( data2 ), {} );
QCOMPARE( conn2.uri(), QStringLiteral( "url=http://testurl2&username=my_user2&password=my_pw2&authcfg=my_auth2&http-header:my_header=value2" ) );
QCOMPARE( conn2.uri(), QStringLiteral( "url=http%3A%2F%2Ftesturl2&username=my_user2&password=my_pw2&authcfg=my_auth2&http-header:my_header=value2" ) );
conn2.store( QStringLiteral( "second connection" ) );
// retrieve stored connections

View File

@ -61,8 +61,8 @@ void TestQgsTiledSceneConnection::encodeDecode()
data.password = QStringLiteral( "my_pw" );
data.httpHeaders.insert( QStringLiteral( "my_header" ), QStringLiteral( "value" ) );
QCOMPARE( QgsTiledSceneProviderConnection::encodedUri( data ), QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( QgsTiledSceneProviderConnection::encodedLayerUri( data ), QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( QgsTiledSceneProviderConnection::encodedUri( data ), QStringLiteral( "url=http%3A%2F%2Ftesturl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( QgsTiledSceneProviderConnection::encodedLayerUri( data ), QStringLiteral( "url=http%3A%2F%2Ftesturl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
const QgsTiledSceneProviderConnection::Data data2 = QgsTiledSceneProviderConnection::decodedUri( QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( data2.url, QStringLiteral( "http://testurl" ) );
@ -97,7 +97,7 @@ void TestQgsTiledSceneConnection::testConnections()
// retrieve stored connection
conn = QgsTiledSceneProviderConnection( QStringLiteral( "my connection" ) );
QCOMPARE( conn.uri(), QStringLiteral( "url=http://testurl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( conn.uri(), QStringLiteral( "url=http%3A%2F%2Ftesturl&username=my_user&password=my_pw&authcfg=my_auth&http-header:my_header=value" ) );
QCOMPARE( qgis::down_cast<QgsTiledSceneProviderConnection *>( &conn )->providerKey(), QStringLiteral( "test_provider" ) );
// add a second connection
@ -110,7 +110,7 @@ void TestQgsTiledSceneConnection::testConnections()
data2.httpHeaders.insert( QStringLiteral( "my_header" ), QStringLiteral( "value2" ) );
// construct connection using encoded uri
QgsTiledSceneProviderConnection conn2( QgsTiledSceneProviderConnection::encodedUri( data2 ), QStringLiteral( "test_provider2" ), {} );
QCOMPARE( conn2.uri(), QStringLiteral( "url=http://testurl2&username=my_user2&password=my_pw2&authcfg=my_auth2&http-header:my_header=value2" ) );
QCOMPARE( conn2.uri(), QStringLiteral( "url=http%3A%2F%2Ftesturl2&username=my_user2&password=my_pw2&authcfg=my_auth2&http-header:my_header=value2" ) );
QCOMPARE( qgis::down_cast<QgsTiledSceneProviderConnection *>( &conn2 )->providerKey(), QStringLiteral( "test_provider2" ) );
conn2.store( QStringLiteral( "second connection" ) );

View File

@ -62,13 +62,13 @@ void TestQgsVectorTileConnection::test_encodedUri()
conn.zMin = 0;
conn.zMax = 18;
QString uri = QgsVectorTileProviderConnection::encodedUri( conn );
QCOMPARE( uri, QStringLiteral( "type=xyz&url=https://api.maptiler.com/tiles/v3/%7Bz%7D/%7Bx%7D/%7By%7D.pbf?key%3Dabcdef12345&zmax=18&zmin=0" ) );
QCOMPARE( uri, QStringLiteral( "type=xyz&url=https%3A%2F%2Fapi.maptiler.com%2Ftiles%2Fv3%2F%7Bz%7D%2F%7Bx%7D%2F%7By%7D.pbf%3Fkey%3Dabcdef12345&zmax=18&zmin=0" ) );
conn.url = QStringLiteral( "file:///home/user/tiles.mbtiles" );
conn.zMin = 0;
conn.zMax = 18;
uri = QgsVectorTileProviderConnection::encodedUri( conn );
QCOMPARE( uri, QStringLiteral( "type=mbtiles&url=file:///home/user/tiles.mbtiles&zmax=18&zmin=0" ) );
QCOMPARE( uri, QStringLiteral( "type=mbtiles&url=file%3A%2F%2F%2Fhome%2Fuser%2Ftiles.mbtiles&zmax=18&zmin=0" ) );
}

View File

@ -257,11 +257,12 @@ void TestQgsVectorTileLayer::testMbtilesProviderMetadata()
QCOMPARE( vectorTileMetadata->validLayerTypesForUri( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) ), { Qgis::LayerType::VectorTile } );
// query sublayers
QString localMbtilesPath = QStringLiteral( "%1%2" ).arg( QUrl::toPercentEncoding( TEST_DATA_DIR ), QUrl::toPercentEncoding( QStringLiteral("/vector_tile/mbtiles_vt.mbtiles") ) );
QList<QgsProviderSublayerDetails> sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1" ).arg( localMbtilesPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( !sublayers.at( 0 ).skippedContainerScan() );
QVERIFY( !QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers ) );
@ -270,7 +271,7 @@ void TestQgsVectorTileLayer::testMbtilesProviderMetadata()
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1" ).arg( localMbtilesPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( !sublayers.at( 0 ).skippedContainerScan() );
@ -279,7 +280,7 @@ void TestQgsVectorTileLayer::testMbtilesProviderMetadata()
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1" ).arg( localMbtilesPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
QVERIFY( QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers ) );
@ -288,17 +289,19 @@ void TestQgsVectorTileLayer::testMbtilesProviderMetadata()
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1" ).arg( localMbtilesPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
// fast scan mode means that any mbtile file will be reported, including those with only raster tiles
// (we are skipping a potentially expensive db open and format check)
QString localIsleOfManPath = QStringLiteral( "%1%2" ).arg( QUrl::toPercentEncoding( TEST_DATA_DIR ), QUrl::toPercentEncoding( QStringLiteral("/isle_of_man.mbtiles") ) );
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "%1/isle_of_man.mbtiles" ).arg( TEST_DATA_DIR ), Qgis::SublayerQueryFlag::FastScan );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "isle_of_man" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/isle_of_man.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1" ).arg( localIsleOfManPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
@ -329,8 +332,9 @@ void TestQgsVectorTileLayer::test_relativePathsMbTiles()
QgsReadWriteContext contextRel;
contextRel.setPathResolver( QgsPathResolver( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/project.qgs" ) ) );
const QgsReadWriteContext contextAbs;
QString localMbtilesPath = QStringLiteral( "%1%2" ).arg( QUrl::toPercentEncoding( TEST_DATA_DIR ), QUrl::toPercentEncoding( QStringLiteral("/vector_tile/mbtiles_vt.mbtiles") ) );
const QString srcMbtiles = QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR );
const QString srcMbtiles = QStringLiteral( "type=mbtiles&url=%1" ).arg( localMbtilesPath );
auto layer = std::make_unique<QgsVectorTileLayer>( srcMbtiles );
QVERIFY( layer->isValid() );
@ -338,7 +342,7 @@ void TestQgsVectorTileLayer::test_relativePathsMbTiles()
// encode source: converting absolute paths to relative
const QString srcMbtilesRel = layer->encodedSource( srcMbtiles, contextRel );
QCOMPARE( srcMbtilesRel, QStringLiteral( "type=mbtiles&url=./vector_tile/mbtiles_vt.mbtiles" ) );
QCOMPARE( srcMbtilesRel, QStringLiteral( "type=mbtiles&url=.%2Fvector_tile%2Fmbtiles_vt.mbtiles" ) );
// encode source: keeping absolute paths
QCOMPARE( layer->encodedSource( srcMbtiles, contextAbs ), srcMbtiles );
@ -389,7 +393,7 @@ void TestQgsVectorTileLayer::test_relativePathsXyz()
contextRel.setPathResolver( QgsPathResolver( "/home/qgis/project.qgs" ) );
const QgsReadWriteContext contextAbs;
const QString srcXyzLocal = "type=xyz&url=file:///home/qgis/%7Bz%7D/%7Bx%7D/%7By%7D.pbf";
const QString srcXyzLocal = "type=xyz&url=file%3A%2F%2F%2Fhome%2Fqgis%2F%7Bz%7D%2F%7Bx%7D%2F%7By%7D.pbf";
const QString srcXyzRemote = "type=xyz&url=http://www.example.com/%7Bz%7D/%7Bx%7D/%7By%7D.pbf";
auto layer = std::make_unique<QgsVectorTileLayer>( srcXyzLocal );
@ -397,7 +401,7 @@ void TestQgsVectorTileLayer::test_relativePathsXyz()
// encode source: converting absolute paths to relative
const QString srcXyzLocalRel = layer->encodedSource( srcXyzLocal, contextRel );
QCOMPARE( srcXyzLocalRel, QStringLiteral( "type=xyz&url=file:./%7Bz%7D/%7Bx%7D/%7By%7D.pbf" ) );
QCOMPARE( srcXyzLocalRel, QStringLiteral( "type=xyz&url=file%3A.%2F%7Bz%7D%2F%7Bx%7D%2F%7By%7D.pbf" ) );
QCOMPARE( layer->encodedSource( srcXyzRemote, contextRel ), srcXyzRemote );
// encode source: keeping absolute paths
@ -433,7 +437,8 @@ void TestQgsVectorTileLayer::test_absoluteRelativeUriXyz()
QString absoluteUri = dsAbs.encodedUri();
QString relativeUri = dsRel.encodedUri();
QCOMPARE( vectorTileMetadata->absoluteToRelativeUri( absoluteUri, context ), relativeUri );
QString absToRelUri = vectorTileMetadata->absoluteToRelativeUri( absoluteUri, context );
QCOMPARE( absToRelUri, relativeUri );
QCOMPARE( vectorTileMetadata->relativeToAbsoluteUri( relativeUri, context ), absoluteUri );
}
@ -455,22 +460,24 @@ void TestQgsVectorTileLayer::testVtpkProviderMetadata()
QVERIFY( vectorTileMetadata->querySublayers( QStringLiteral( "type=vtpk&url=%1/points.shp" ).arg( TEST_DATA_DIR ) ).isEmpty() );
// vtpk uris
QString localVtpkPath = QStringLiteral( "%1%2" ).arg( QUrl::toPercentEncoding( TEST_DATA_DIR ), QUrl::toPercentEncoding( QStringLiteral("/testvtpk.vtpk") ) );
QCOMPARE( vectorTileMetadata->priorityForUri( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/testvtpk.vtpk" ) ), 100 );
QCOMPARE( vectorTileMetadata->validLayerTypesForUri( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/testvtpk.vtpk" ) ), { Qgis::LayerType::VectorTile } );
QList<QgsProviderSublayerDetails> sublayers = vectorTileMetadata->querySublayers( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/testvtpk.vtpk" ) );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "vtpkvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "testvtpk" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QCOMPARE( vectorTileMetadata->priorityForUri( QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR ) ), 100 );
QCOMPARE( vectorTileMetadata->validLayerTypesForUri( QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR ) ), { Qgis::LayerType::VectorTile } );
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR ) );
QCOMPARE( vectorTileMetadata->priorityForUri( QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath ) ), 100 );
QCOMPARE( vectorTileMetadata->validLayerTypesForUri( QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath ) ), {Qgis::LayerType::VectorTile} );
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath ) );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "vtpkvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "testvtpk" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
// test that vtpk provider is the preferred provider for vtpk files
@ -497,7 +504,9 @@ void TestQgsVectorTileLayer::test_relativePathsVtpk()
contextRel.setPathResolver( QgsPathResolver( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/project.qgs" ) ) );
const QgsReadWriteContext contextAbs;
const QString srcVtpk = QStringLiteral( "type=vtpk&url=%1/testvtpk.vtpk" ).arg( TEST_DATA_DIR );
QString localVtpkPath = QStringLiteral( "%1%2" ).arg( QUrl::toPercentEncoding( TEST_DATA_DIR ), QUrl::toPercentEncoding( QStringLiteral("/testvtpk.vtpk") ) );
const QString srcVtpk = QStringLiteral( "type=vtpk&url=%1" ).arg( localVtpkPath );
auto layer = std::make_unique<QgsVectorTileLayer>( srcVtpk );
QVERIFY( layer->isValid() );
@ -505,7 +514,7 @@ void TestQgsVectorTileLayer::test_relativePathsVtpk()
// encode source: converting absolute paths to relative
const QString srcVtpkRel = layer->encodedSource( srcVtpk, contextRel );
QCOMPARE( srcVtpkRel, QStringLiteral( "type=vtpk&url=./testvtpk.vtpk" ) );
QCOMPARE( srcVtpkRel, QStringLiteral( "type=vtpk&url=.%2Ftestvtpk.vtpk" ) );
// encode source: keeping absolute paths
QCOMPARE( layer->encodedSource( srcVtpk, contextAbs ), srcVtpk );

View File

@ -469,8 +469,8 @@ void TestQgsWmsProvider::absoluteRelativeUri()
QgsProviderMetadata *wmsMetadata = QgsProviderRegistry::instance()->providerMetadata( "wms" );
QVERIFY( wmsMetadata );
QString absoluteUri = "type=mbtiles&url=file://" + QStringLiteral( TEST_DATA_DIR ) + "/isle_of_man.mbtiles";
QString relativeUri = "type=mbtiles&url=file:./isle_of_man.mbtiles";
QString absoluteUri = "type=mbtiles&url=" + QString( QUrl::toPercentEncoding( "file://" + QStringLiteral( TEST_DATA_DIR ) + "/isle_of_man.mbtiles" ) );
QString relativeUri = "type=mbtiles&url=file%3A.%2Fisle_of_man.mbtiles";
QCOMPARE( wmsMetadata->absoluteToRelativeUri( absoluteUri, context ), relativeUri );
QCOMPARE( wmsMetadata->relativeToAbsoluteUri( relativeUri, context ), absoluteUri );
}

View File

@ -105,7 +105,7 @@ class TestVectorTile(QgisTestCase):
parts["path"] = "/my/new/file.mbtiles"
uri = md.encodeUri(parts)
self.assertEqual(uri, "type=mbtiles&url=/my/new/file.mbtiles")
self.assertEqual(uri, "type=mbtiles&url=%2Fmy%2Fnew%2Ffile.mbtiles")
uri = (
"type=xyz&url=https://fake.server/%7Bx%7D/%7By%7D/%7Bz%7D.png&zmin=0&zmax=2"
@ -125,7 +125,7 @@ class TestVectorTile(QgisTestCase):
uri = md.encodeUri(parts)
self.assertEqual(
uri,
"type=xyz&url=https://fake.new.server/%7Bx%7D/%7By%7D/%7Bz%7D.png&zmax=2&zmin=0",
"type=xyz&url=https%3A%2F%2Ffake.new.server%2F%7Bx%7D%2F%7By%7D%2F%7Bz%7D.png&zmax=2&zmin=0",
)
uri = "type=xyz&serviceType=arcgis&url=https://fake.server/%7Bx%7D/%7By%7D/%7Bz%7D.png&zmax=2&http-header:referer=https://qgis.org/&styleUrl=https://qgis.org/"
@ -147,7 +147,7 @@ class TestVectorTile(QgisTestCase):
uri = md.encodeUri(parts)
self.assertEqual(
uri,
"serviceType=arcgis&styleUrl=https://qgis.org/&type=xyz&url=https://fake.new.server/%7Bx%7D/%7By%7D/%7Bz%7D.png&zmax=2&http-header:referer=https://qgis.org/",
"serviceType=arcgis&styleUrl=https%3A%2F%2Fqgis.org%2F&type=xyz&url=https%3A%2F%2Ffake.new.server%2F%7Bx%7D%2F%7By%7D%2F%7Bz%7D.png&zmax=2&http-header:referer=https%3A%2F%2Fqgis.org%2F",
)
def testZoomRange(self):

View File

@ -64,14 +64,14 @@ void TestQgsServerWmsParameters::external_layers()
QgsWms::QgsWmsParametersLayer layer_params = layers_params[0];
QCOMPARE( layer_params.mNickname, QString( "external_layer_1" ) );
QCOMPARE( layer_params.mExternalUri, QString( "layers=layer_1_name&url=http://url_1" ) );
QCOMPARE( layer_params.mExternalUri, QString( "layers=layer_1_name&url=http%3A%2F%2Furl_1" ) );
layer_params = layers_params[1];
QCOMPARE( layer_params.mNickname, QString( "layer" ) );
layer_params = layers_params[2];
QCOMPARE( layer_params.mNickname, QString( "external_layer_2" ) );
QCOMPARE( layer_params.mExternalUri, QString( "layers=layer_2_name&opacities=100&url=http://url_2" ) );
QCOMPARE( layer_params.mExternalUri, QString( "layers=layer_2_name&opacities=100&url=http%3A%2F%2Furl_2" ) );
//test if opacities are also applied to external layers
QCOMPARE( layers_params[0].mOpacity, 255 );
@ -94,7 +94,7 @@ void TestQgsServerWmsParameters::external_layers()
QgsWms::QgsWmsParametersLayer layer_params2 = layers_params2[0];
QCOMPARE( layer_params2.mNickname, QString( "external_layer_1" ) );
QCOMPARE( layer_params2.mExternalUri, QString( "layers=layer_1_name&url=http://url_1" ) );
QCOMPARE( layer_params2.mExternalUri, QString( "layers=layer_1_name&url=http%3A%2F%2Furl_1" ) );
}
void TestQgsServerWmsParameters::percent_encoding()