diff --git a/.ci/travis/linux/before_script.sh b/.ci/travis/linux/before_script.sh index b4c0ef293fa..4cffff849e1 100755 --- a/.ci/travis/linux/before_script.sh +++ b/.ci/travis/linux/before_script.sh @@ -23,7 +23,11 @@ docker-compose --version docker-compose -f $DOCKER_COMPOSE config #docker pull ubuntu:16.04 docker pull "qgis/qgis3-build-deps:${DOCKER_TAG}" || true -docker build --cache-from "qgis/qgis3-build-deps:${DOCKER_TAG}" -t "qgis/qgis3-build-deps:${DOCKER_TAG}" . +if [[ $DOCKER_DEPS_IMAGE_REBUILD =~ true ]]; then + docker build --no-cache -t "qgis/qgis3-build-deps:${DOCKER_TAG}" . +else + docker build --cache-from "qgis/qgis3-build-deps:${DOCKER_TAG}" -t "qgis/qgis3-build-deps:${DOCKER_TAG}" . +fi echo "travis_fold:end:docker" # image should be pushed even if QGIS build fails # but push is achieved only on branches (not for PRs) diff --git a/.ci/travis/linux/blacklist.txt b/.ci/travis/linux/blacklist.txt index c6157cbd4c9..52e3831f557 100755 --- a/.ci/travis/linux/blacklist.txt +++ b/.ci/travis/linux/blacklist.txt @@ -30,7 +30,11 @@ PyQgsSpatialiteProvider # Flaky, see https://travis-ci.org/qgis/QGIS/jobs/297708174 PyQgsServerAccessControl +# Flaky, see https://dash.orfeo-toolbox.org/testDetails.php?test=61670866&build=297397 +PyQgsLocator + # Need a local postgres installation PyQgsAuthManagerPKIPostgresTest PyQgsAuthManagerPasswordPostgresTest PyQgsAuthManagerOgrPostgresTest + diff --git a/.ci/travis/linux/docker-build-test.sh b/.ci/travis/linux/docker-build-test.sh index 5e133e626ea..abdae71c8b3 100755 --- a/.ci/travis/linux/docker-build-test.sh +++ b/.ci/travis/linux/docker-build-test.sh @@ -58,10 +58,10 @@ echo "travis_fold:end:cmake" ####### # Build ####### -echo "travis_fold:start:ninja-build.1" +# echo "travis_fold:start:ninja-build.1" echo "${bold}Building QGIS...${endbold}" ${CTEST_BUILD_COMMAND} -echo "travis_fold:end:ninja-build.1" +# echo "travis_fold:end:ninja-build.1" # Temporarily uncomment to debug ccache issues # echo "travis_fold:start:ccache-debug" diff --git a/.docker/Dockerfile b/.docker/Dockerfile index b0a3fc879f3..9cb33731772 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -12,7 +12,7 @@ RUN apt-get update \ ca-certificates \ ccache \ clang \ - cmake \ + # cmake \ curl \ dh-python \ flex \ @@ -69,7 +69,6 @@ RUN apt-get update \ qt5keychain-dev \ qtbase5-dev \ qtpositioning5-dev \ - qtscript5-dev \ qttools5-dev \ qttools5-dev-tools \ spawn-fcgi \ @@ -89,7 +88,10 @@ RUN apt-get update \ future \ termcolor \ && apt-get autoremove -y python3-pip python2.7 \ - && apt-get clean + && apt-get clean \ + && curl -s -S -O https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.tar.gz \ + && tar --strip-components=1 -zx -f cmake-3.10.1-Linux-x86_64.tar.gz -C /usr/local \ + && rm cmake-3.10.1-Linux-x86_64.tar.gz RUN echo "alias python=python3" >> ~/.bash_aliases @@ -97,5 +99,6 @@ ENV CC=/usr/lib/ccache/clang ENV CXX=/usr/lib/ccache/clang++ ENV QT_SELECT=5 ENV LANG=C.UTF-8 +ENV PATH="/usr/local/bin:${PATH}" CMD /root/QGIS/.ci/travis/linux/docker-build-test.sh diff --git a/.gitignore b/.gitignore index f8d3ce9263c..34f7fed68eb 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ /CMakeLists.txt.user /CMakeLists.txt.user.* api_doc -*build* +/build* debian/*.debhelper debian/*.substvars desktop.ini diff --git a/.travis.yml b/.travis.yml index b65c7bb560b..0a9184e4ae9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ matrix: - DOCKER_COMPOSE=${TRAVIS_BUILD_DIR}/.docker/docker-compose.travis.yml - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && echo $TRAVIS_BRANCH | sed 's/master/latest/' || echo "latest" ) - DOCKER_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && [[ $TRAVIS_PULL_REQUEST =~ false ]] && echo "true" || echo "false" ) + - DOCKER_DEPS_IMAGE_REBUILD=$( [[ $TRAVIS_COMMIT_MESSAGE =~ '[docker] update dependencies' ]] && echo "true" || echo "false" ) - CCACHE_DIR=${HOME}/.ccache dist: trusty sudo: false diff --git a/CMakeLists.txt b/CMakeLists.txt index b76c4f701c4..964931dafe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,7 +233,7 @@ IF(WITH_CORE) FIND_PACKAGE(LibZip REQUIRED) IF (WITH_INTERNAL_QEXTSERIALPORT) - SET(QEXTSERIALPORT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/core/gps/qextserialport) + SET(QEXTSERIALPORT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/external/qextserialport) ELSE (WITH_INTERNAL_QEXTSERIALPORT) FIND_PACKAGE(Qextserialport REQUIRED) ENDIF(WITH_INTERNAL_QEXTSERIALPORT) @@ -291,7 +291,6 @@ IF(WITH_CORE) ENDIF(WITH_QTWEBKIT) FIND_PACKAGE(Qt5Test REQUIRED) FIND_PACKAGE(Qt5UiTools REQUIRED) - FIND_PACKAGE(Qt5Script REQUIRED) FIND_PACKAGE(Qt5Sql REQUIRED) IF (WITH_3D) FIND_PACKAGE(Qt53DCore REQUIRED) @@ -463,12 +462,14 @@ ENDIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)") IF (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) MESSAGE (STATUS "Debug output enabled") - ADD_DEFINITIONS(-DQGISDEBUG=1) + SET(QGISDEBUG TRUE) +ELSE (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) + SET(QGISDEBUG FALSE) ENDIF (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) IF(MSVC) - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQGISDEBUG=1") - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DQGISDEBUG=1") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") # disable macros that offend std::numeric_limits::min()/max() ADD_DEFINITIONS(-DNOMINMAX) ENDIF(MSVC) diff --git a/INSTALL b/INSTALL index 00e1533c8a1..bf208f9adc9 100644 --- a/INSTALL +++ b/INSTALL @@ -179,12 +179,12 @@ Now update your local sources database: =============================== || Distribution | install command for packages | - | stretch | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | - | xenial | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | - | yakkety | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | - | zesty | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | - | artful | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | - | sid | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | stretch | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | xenial | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | yakkety | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | zesty | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | artful | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | + | sid | ``apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb`` | (extracted from the control.in file in debian/) @@ -367,7 +367,7 @@ new subdirectory called `build` or `build-qt5` in it. 3.9.1. Install build dependencies ================================= - dnf install qt5-qtwebkit-devel qt5-qtlocation-devel qt5-qttools-static qt5-qtscript-devel qca-qt5-devel qca-qt5-ossl qt5-qt3d-devel python3-qt5-devel python3-qscintilla-qt5-devel qscintilla-qt5-devel python3-qscintilla-devel python3-qscintilla-qt5 clang flex bison geos-devel gdal-devel sqlite-devel libspatialite-devel qt5-qtsvg-devel qt5-qtxmlpatterns-devel spatialindex-devel expat-devel proj-devel qwt-qt5-devel gsl-devel postgresql-devel cmake python3-future gdal-python3 python3-psycopg2 python3-PyYAML python3-pygments python3-jinja2 python3-OWSLib qca-qt5-ossl qwt-qt5-devel qtkeychain-qt5-devel qwt-devel sip-devel libzip-devel + dnf install qt5-qtwebkit-devel qt5-qtlocation-devel qt5-qttools-static qca-qt5-devel qca-qt5-ossl qt5-qt3d-devel python3-qt5-devel python3-qscintilla-qt5-devel qscintilla-qt5-devel python3-qscintilla-devel python3-qscintilla-qt5 clang flex bison geos-devel gdal-devel sqlite-devel libspatialite-devel qt5-qtsvg-devel qt5-qtxmlpatterns-devel spatialindex-devel expat-devel proj-devel qwt-qt5-devel gsl-devel postgresql-devel cmake python3-future gdal-python3 python3-psycopg2 python3-PyYAML python3-pygments python3-jinja2 python3-OWSLib qca-qt5-ossl qwt-qt5-devel qtkeychain-qt5-devel qwt-devel sip-devel libzip-devel To build QGIS server additional dependencies are required: diff --git a/cmake/SIPMacros.cmake b/cmake/SIPMacros.cmake index 5e1bdfd117e..9814d52fb9f 100644 --- a/cmake/SIPMacros.cmake +++ b/cmake/SIPMacros.cmake @@ -42,7 +42,7 @@ SET(SIP_DISABLE_FEATURES) SET(SIP_EXTRA_OPTIONS) SET(SIP_EXTRA_OBJECTS) -MACRO(GENERATE_SIP_PYTHON_MODULE_CODE MODULE_NAME MODULE_SIP CPP_FILES) +MACRO(GENERATE_SIP_PYTHON_MODULE_CODE MODULE_NAME MODULE_SIP SIP_FILES CPP_FILES) STRING(REPLACE "." "/" _x ${MODULE_NAME}) GET_FILENAME_COMPONENT(_parent_module_path ${_x} PATH) GET_FILENAME_COMPONENT(_child_module_name ${_x} NAME) @@ -52,6 +52,18 @@ MACRO(GENERATE_SIP_PYTHON_MODULE_CODE MODULE_NAME MODULE_SIP CPP_FILES) FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_module_path}) # Output goes in this dir. + # If this is not need anymore (using input configuration file for SIP files) + # SIP could be run in the source rather than in binary directory + SET(_configured_module_sip ${CMAKE_CURRENT_BINARY_DIR}/${_module_path}/${_module_path}.sip) + FOREACH (_sip_file ${SIP_FILES}) + GET_FILENAME_COMPONENT(_sip_file_path ${_sip_file} PATH) + GET_FILENAME_COMPONENT(_sip_file_name_we ${_sip_file} NAME_WE) + FILE(RELATIVE_PATH _sip_file_relpath ${CMAKE_CURRENT_SOURCE_DIR} "${_sip_file_path}/${_sip_file_name_we}") + SET(_out_sip_file "${CMAKE_CURRENT_BINARY_DIR}/${_sip_file_relpath}.sip") + CONFIGURE_FILE(${_sip_file} ${_out_sip_file}) + ENDFOREACH (_sip_file) + + SET(_sip_includes) FOREACH (_inc ${SIP_INCLUDES}) GET_FILENAME_COMPONENT(_abs_inc ${_inc} ABSOLUTE) @@ -99,13 +111,14 @@ MACRO(GENERATE_SIP_PYTHON_MODULE_CODE MODULE_NAME MODULE_SIP CPP_FILES) ADD_DEFINITIONS( /bigobj ) ENDIF(MSVC) - SET(SIPCMD ${SIP_BINARY_PATH} ${_sip_tags} -w -e ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_BINARY_DIR}/${_module_path} ${_sip_includes} ${_abs_module_sip}) + SET(SIPCMD ${SIP_BINARY_PATH} ${_sip_tags} -w -e ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_BINARY_DIR}/${_module_path} -I ${CMAKE_CURRENT_BINARY_DIR}/${_module_path} ${_sip_includes} ${_configured_module_sip}) ADD_CUSTOM_COMMAND( OUTPUT ${_sip_output_files} COMMAND ${CMAKE_COMMAND} -E echo ${message} COMMAND ${CMAKE_COMMAND} -E touch ${_sip_output_files} COMMAND ${SIPCMD} - DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND} + MAIN_DEPENDENCY ${_configured_module_sip} + DEPENDS ${SIP_EXTRA_FILES_DEPEND} VERBATIM ) diff --git a/cmake_templates/qgsconfig.h.in b/cmake_templates/qgsconfig.h.in index 544babbcca7..67bbefe2af8 100644 --- a/cmake_templates/qgsconfig.h.in +++ b/cmake_templates/qgsconfig.h.in @@ -62,5 +62,7 @@ #cmakedefine USE_THREAD_LOCAL +#cmakedefine QGISDEBUG + #endif diff --git a/debian/control b/debian/control index ce9629fce9e..5d5800a2473 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,7 @@ Build-Depends: libspatialite-dev, libsqlite3-dev, libspatialindex-dev, - qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtscript5-dev, qtpositioning5-dev, + qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtpositioning5-dev, libqt5svg5-dev, libqt5xmlpatterns5-dev, libqt5webkit5-dev, libqt5opengl5-dev, libqt5sql5-sqlite, libqt5scintilla2-dev, libqwt-qt5-dev, libqca-qt5-2-dev, libqca-qt5-2-plugins, python3-dev, python3-all-dev, python3-sip, python3-sip-dev, @@ -203,7 +203,6 @@ Depends: libqt5webkit5-dev, libqca-qt5-2-dev, libqwt-qt5-dev, - qtscript5-dev, qtpositioning5-dev, pyqt5-dev-tools, python3-pyqt5, diff --git a/debian/control.in b/debian/control.in index 98ee47b98a9..41acaa4aa5d 100644 --- a/debian/control.in +++ b/debian/control.in @@ -13,7 +13,7 @@ Build-Depends: libexpat1-dev, libfcgi-dev, libgdal-dev (>= 1.11), -#sid stretch xenial yakkety zesty artful# libgsl-dev, +#sid stretch xenial yakkety zesty artful bionic# libgsl-dev, libgeos-dev (>= 3.0.0), libpq-dev, libproj-dev, @@ -21,7 +21,7 @@ Build-Depends: libsqlite3-dev, libspatialindex-dev, libzip-dev, - qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtscript5-dev, qtpositioning5-dev, qt5keychain-dev, + qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtpositioning5-dev, qt5keychain-dev, libqt5svg5-dev, libqt5xmlpatterns5-dev, libqt5webkit5-dev, libqt5opengl5-dev, libqt5sql5-sqlite, libqt5scintilla2-dev, libqwt-qt5-dev, libqca-qt5-2-dev, libqca-qt5-2-plugins, python3-dev, python3-all-dev, python3-sip, python3-sip-dev, @@ -38,12 +38,12 @@ Build-Depends: graphviz, xvfb, xauth, xfonts-base, xfonts-100dpi, xfonts-75dpi, xfonts-scalable, -#artful sid# libosgearth-dev, qt3d5-dev, qt3d-assimpsceneimport-plugin, qt3d-defaultgeometryloader-plugin, qt3d-gltfsceneio-plugin, qt3d-scene2d-plugin, +#sid artful bionic# libosgearth-dev, qt3d5-dev, qt3d-assimpsceneimport-plugin, qt3d-defaultgeometryloader-plugin, qt3d-gltfsceneio-plugin, qt3d-scene2d-plugin, #oracle# oracle-instantclient12.1-devel, oracle-instantclient12.1-basiclite, qtbase5-private-dev, locales, ca-certificates, ninja-build Build-Conflicts: libqgis-dev, qgis-dev -#sid stretch xenial yakkety zesty artful#Standards-Version: 3.9.7 -# xenial yakkety zesty artful#XS-Python-Version: current +#sid stretch xenial yakkety zesty artful bionic#Standards-Version: 3.9.7 +# xenial yakkety zesty artful bionic#XS-Python-Version: current Vcs-Browser: https://github.com/qgis/QGIS/ Vcs-Git: https://github.com/qgis/QGIS.git Homepage: http://qgis.org/ @@ -145,19 +145,19 @@ Description: QGIS - shared native gui library . This package contains the shared native gui library. -#artful sid#Package: libqgis-3d{QGIS_ABI} -#artful sid#Architecture: any -#artful sid#Section: libs -#artful sid#Depends: -#artful sid# qt3d-assimpsceneimport-plugin, qt3d-defaultgeometryloader-plugin, qt3d-gltfsceneio-plugin, qt3d-scene2d-plugin, -#artful sid# ${shlibs:Depends}, -#artful sid# ${misc:Depends} -#artful sid#Description: QGIS - shared 3d library -#artful sid# QGIS is a Geographic Information System (GIS) which manages, analyzes and -#artful sid# display databases of geographic information. -#artful sid# . -#artful sid# This package contains the shared 3d library. -#artful sid# +#artful bionic sid#Package: libqgis-3d{QGIS_ABI} +#artful bionic sid#Architecture: any +#artful bionic sid#Section: libs +#artful bionic sid#Depends: +#artful bionic sid# qt3d-assimpsceneimport-plugin, qt3d-defaultgeometryloader-plugin, qt3d-gltfsceneio-plugin, qt3d-scene2d-plugin, +#artful bionic sid# ${shlibs:Depends}, +#artful bionic sid# ${misc:Depends} +#artful bionic sid#Description: QGIS - shared 3d library +#artful bionic sid# QGIS is a Geographic Information System (GIS) which manages, analyzes and +#artful bionic sid# display databases of geographic information. +#artful bionic sid# . +#artful bionic sid# This package contains the shared 3d library. +#artful bionic sid# Package: libqgisgrass{GRASSVER}-{QGIS_ABI} Architecture: any Section: libs @@ -214,7 +214,7 @@ Depends: libexpat1-dev, libgdal-dev (>= 1.11), libgeos-dev (>= 3.0.0), -#sid stretch xenial yakkety zesty artful# libgsl-dev, +#sid stretch xenial yakkety zesty artful bionic# libgsl-dev, libpq-dev, libproj-dev, libqgis-app{QGIS_ABI} (= ${binary:Version}), @@ -232,7 +232,6 @@ Depends: libqt5webkit5-dev, libqca-qt5-2-dev, libqwt-qt5-dev, - qtscript5-dev, qtpositioning5-dev, pyqt5-dev-tools, python3-pyqt5, diff --git a/debian/copyright b/debian/copyright index 1cba2e66ea6..828b372dbad 100644 --- a/debian/copyright +++ b/debian/copyright @@ -166,9 +166,9 @@ Comment: Voronoi diagram calculator/ Delaunay triangulator Steve Fortune's homepage: http://netlib.bell-labs.com/cm/cs/who/sjf/index.html License: GPL-2+ -Files: src/app/gps/qwtpolar-0.1/* - src/app/gps/qwtpolar-1.0/* - src/app/gps/qwtpolar-1.1.1/* +Files: external//qwtpolar-0.1/* + external//qwtpolar-1.0/* + external//qwtpolar-1.1.1/* Copyright: 2008, Uwe Rathmann Comment: This library is free software; you can redistribute it and/or modify it under the terms of the Qwt License, Version 1.0 @@ -185,23 +185,23 @@ Comment: Artistic Style is maintained and updated by Jim Pattee. The original author was Tal Davidson, Israel. License: LGPL-2.1+ -Files: src/core/gps/config.h - src/core/gps/context.c - src/core/gps/context.h - src/core/gps/gmath.c - src/core/gps/gmath.h - src/core/gps/info.c - src/core/gps/info.h - src/core/gps/nmeatime.h - src/core/gps/parse.c - src/core/gps/parse.h - src/core/gps/parser.h - src/core/gps/sentence.c - src/core/gps/sentence.h - src/core/gps/time.c - src/core/gps/tok.c - src/core/gps/tok.h - src/core/gps/units.h +Files: external/nmea/config.h + external/nmea/context.c + external/nmea/context.h + external/nmea/gmath.c + external/nmea/gmath.h + external/nmea/info.c + external/nmea/info.h + external/nmea/nmeatime.h + external/nmea/parse.c + external/nmea/parse.h + external/nmea/parser.h + external/nmea/sentence.c + external/nmea/sentence.h + external/nmea/time.c + external/nmea/tok.c + external/nmea/tok.h + external/nmea/units.h Copyright: Tim License: LGPL-2+ diff --git a/debian/qgis-common.install b/debian/qgis-common.install index 9fa4fa8baf4..b3b0703594e 100644 --- a/debian/qgis-common.install +++ b/debian/qgis-common.install @@ -18,4 +18,7 @@ usr/share/qgis/i18n/* usr/share/qgis/images/* usr/share/qgis/resources/spatialite.db usr/share/qgis/resources/themes/* +usr/share/qgis/resources/data/* +usr/share/qgis/resources/metadata-ISO/* +usr/share/qgis/resources/2to3migration.txt usr/share/qgis/qgis_global_settings.ini diff --git a/debian/rules b/debian/rules index d460c7b297a..e7b46f27f52 100755 --- a/debian/rules +++ b/debian/rules @@ -36,7 +36,7 @@ endif QT_PLUGINS_DIR = usr/lib/$(DEB_BUILD_MULTIARCH)/qt5/plugins -ifneq ($(DISTRIBUTION),$(findstring $(DISTRIBUTION),"stretch xenial yakkety zesty artful")) +ifneq ($(DISTRIBUTION),$(findstring $(DISTRIBUTION),"stretch xenial yakkety zesty artful bionic")) DISTRIBUTION := sid endif @@ -102,7 +102,7 @@ ifneq (,$(findstring $(DISTRIBUTION),"sid stretch")) CMAKE_OPTS += -DPOSTGRES_LIBRARY=/usr/lib/$(DEB_BUILD_MULTIARCH)/libpq.so endif -ifneq (,$(findstring $(DISTRIBUTION),"artful sid")) +ifneq (,$(findstring $(DISTRIBUTION),"artful bionic sid")) CMAKE_OPTS += \ -DWITH_3D=TRUE \ -DGEOS_LIBRARY=/usr/lib/$(DEB_BUILD_MULTIARCH)/libgeos_c.so @@ -266,6 +266,9 @@ override_dh_auto_install: $(RM) $(CURDIR)/debian/tmp/usr/share/qgis/doc/LICENSE $(RM) $(CURDIR)/debian/tmp/usr/share/qgis/python/plugins/db_manager/LICENSE $(RM) $(CURDIR)/debian/tmp/usr/share/qgis/python/plugins/MetaSearch/LICENSE.txt + $(RM) $(CURDIR)/debian/tmp/usr/bin/qgis_wcstest + $(RM) $(CURDIR)/debian/tmp/usr/bin/qgis_bench + $(RM) $(CURDIR)/debian/tmp/usr/share/qgis/resources/wcs-servers.json # Man pages are installed by dh_installman $(RM) $(CURDIR)/debian/tmp/usr/man/man1/qgis.1 diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 26a3683260d..5d85e2444e6 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -56,7 +56,6 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/core/3d ${CMAKE_SOURCE_DIR}/src/core/annotations ${CMAKE_SOURCE_DIR}/src/core/auth - ${CMAKE_SOURCE_DIR}/src/core/composer ${CMAKE_SOURCE_DIR}/src/core/diagram ${CMAKE_SOURCE_DIR}/src/core/dxf ${CMAKE_SOURCE_DIR}/src/core/effects diff --git a/doc/INSTALL.html b/doc/INSTALL.html index 75bbd814789..850805d8ec8 100644 --- a/doc/INSTALL.html +++ b/doc/INSTALL.html @@ -310,27 +310,27 @@ sudo apt-get update stretch -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb xenial -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb yakkety -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb zesty -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb artful -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb sid -apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb +apt-get install bison ca-certificates ccache cmake cmake-curses-gui dh-python doxygen expect flex gdal-bin git graphviz grass-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libosgearth-dev libpq-dev libproj-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqt5opengl5-dev libqt5scintilla2-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libqwt-qt5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libzip-dev lighttpd locales ninja-build pkg-config poppler-utils pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-dateutil python3-dev python3-future python3-gdal python3-httplib2 python3-jinja2 python3-markupsafe python3-mock python3-nose2 python3-owslib python3-plotly python3-psycopg2 python3-pygments python3-pyproj python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-requests python3-sip python3-sip-dev python3-six python3-termcolor python3-tz python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qt5-default qt5keychain-dev qtbase5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools spawn-fcgi txt2tags xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb @@ -601,7 +601,7 @@ new subdirectory called `build` or `build-qt5` in it.

3.9.1. Install build dependencies

-dnf install qt5-qtwebkit-devel qt5-qtlocation-devel qt5-qttools-static qt5-qtscript-devel qca-qt5-devel qca-qt5-ossl qt5-qt3d-devel python3-qt5-devel python3-qscintilla-qt5-devel qscintilla-qt5-devel python3-qscintilla-devel python3-qscintilla-qt5 clang flex bison geos-devel gdal-devel sqlite-devel libspatialite-devel qt5-qtsvg-devel qt5-qtxmlpatterns-devel spatialindex-devel expat-devel proj-devel qwt-qt5-devel gsl-devel postgresql-devel cmake python3-future gdal-python3 python3-psycopg2 python3-PyYAML python3-pygments python3-jinja2 python3-OWSLib qca-qt5-ossl qwt-qt5-devel qtkeychain-qt5-devel qwt-devel sip-devel libzip-devel
+dnf install qt5-qtwebkit-devel qt5-qtlocation-devel qt5-qttools-static qca-qt5-devel qca-qt5-ossl qt5-qt3d-devel python3-qt5-devel python3-qscintilla-qt5-devel qscintilla-qt5-devel python3-qscintilla-devel python3-qscintilla-qt5 clang flex bison geos-devel gdal-devel sqlite-devel libspatialite-devel qt5-qtsvg-devel qt5-qtxmlpatterns-devel spatialindex-devel expat-devel proj-devel qwt-qt5-devel gsl-devel postgresql-devel cmake python3-future gdal-python3 python3-psycopg2 python3-PyYAML python3-pygments python3-jinja2 python3-OWSLib qca-qt5-ossl qwt-qt5-devel qtkeychain-qt5-devel qwt-devel sip-devel libzip-devel
 

diff --git a/doc/api_break.dox b/doc/api_break.dox index 803e7b0950a..a9aa82010b4 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -75,9 +75,6 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsCentroidFillSymbolLayerV2WidgetQgsCentroidFillSymbolLayerWidget QgsCircularStringV2QgsCircularString QgsColorButtonV2QgsColorButton -QgsComposerLegendStyleQgsLegendStyle -QgsComposerSymbolV2ItemQgsComposerSymbolItem -QgsComposerTableAvailableSortProxyModelV2QgsComposerTableAvailableSortProxyModel QgsCompoundCurveV2QgsCompoundCurve QgsCoordinateSequenceV2QgsCoordinateSequence QgsCptCityColorRampV2QgsCptCityColorRamp @@ -100,6 +97,10 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsFontMarkerSymbolLayerV2WidgetQgsFontMarkerSymbolLayerWidget QgsGeometryCollectionV2QgsGeometryCollection QgsGeometryGeneratorSymbolLayerV2QgsGeometryGeneratorSymbolLayer +QgsGPSConnectionQgsGpsConnection +QgsGPSConnectionRegistryQgsGpsConnectionRegistry +QgsGPSDetectorQgsGpsDetector +QgsGPSInformationQgsGpsInformation QgsGradientFillSymbolLayerV2QgsGradientFillSymbolLayer QgsGradientFillSymbolLayerV2WidgetQgsGradientFillSymbolLayerWidget QgsGraduatedSymbolRendererV2QgsGraduatedSymbolRenderer @@ -128,6 +129,7 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsMultiPolylineQgsPolylineXY QgsMultiPolylineV2QgsPolyline QgsMultiSurfaceV2QgsMultiSurface +QgsNMEAConnectionQgsNmeaConnection QgsNumericScaleBarStyleQgsNumericScaleBarRenderer QgsPointQgsPointXY QgsPointSequenceV2QgsPointSequence @@ -196,6 +198,7 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsSymbolV2SelectorWidgetQgsSymbolSelectorWidget QgsTicksScaleBarStyleQgsTicksScaleBarRenderer QgsTINInterpolatorQgsTinInterpolator +QgsUserInputDockWidgetQgsUserInputWidget QgsVectorColorBrewerColorRampV2QgsColorBrewerColorRamp QgsVectorColorBrewerColorRampV2DialogQgsColorBrewerColorRampDialog QgsVectorColorBrewerColorRampV2DialogBaseQgsColorBrewerColorRampDialogBase @@ -227,7 +230,6 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsAnnotationmapPositionFixedhasFixedMapPosition QgsApplicationdefaultStyleV2PathdefaultStylePath QgsApplicationuserStyleV2PathuserStylePath -QgsComposerShapesetUseSymbolV2setUseSymbol QgsIFeatureSelectionManagerselectedFeaturesIdsselectedFeatureIds QgsMapLayercapitaliseLayerNamecapitalizeLayerName QgsSymbolLayerUtilscreateSymbolLayerV2ListFromSldcreateSymbolLayerListFromSld @@ -238,7 +240,6 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes} QgsVectorLayerrendererV2renderer QgsVectorLayerselectedFeaturesIdsselectedFeatureIds QgsVectorLayerEditUtilsdeleteVertexV2deleteVertex -QgsComposerSymbolItemsymbolV2symbol QgsServerInterfacecapabiblitiesCachecapabilitiesCache QgsGraphEdgepropertycost QgsGraphEdgepropertiesstrategies @@ -267,18 +268,14 @@ Removed Classes {#qgis_api_break_3_0_removed_classes} - QgsColorDialog was removed, and QgsColorDialogV2 was renamed to QgsColorDialog. Hence, QgsColorButtonV2 does not exist anymore. All the functionality from the old QgsColorDialog has been moved to the new class. - QgsColorRampComboBox was removed, replaced by QgsColorRampButton -- QgsComposerAttributeTable and associated classes (eg QgsComposerAttributeTableCompare, -QgsComposerAttributeTableColumnModel, QgsComposerTableSortColumnsProxyModel) were removed. -Use QgsComposerAttributeTableV2 instead. -- QgsComposerLegendItem and subclasses were removed (QgsComposerSymbolItem, QgsComposerRasterSymbolItem, QgsComposerLayerItem, QgsComposerGroupItem, QgsComposerStyleItem). -- QgsComposerTable was removed. Use QgsComposerAttributeTableV2 instead. -- ComposerTextTable was removed. Use ComposerTextTableV2 instead. - QgsCRSCache was removed. QgsCoordinateReferenceSystem now internally uses a cache for CRS creation, so there is no longer a need for the separate cache class. Code which previously called QgsCRSCache::updateCRSCache() should now call QgsCoordinateReferenceSystem::invalidateCache() and QgsCoordinateTransformCache::instance()->invalidateCrs( authid ). +- QgsCoordinateTransformCache was removed. QgsCoordinateTransform now transparently caches transforms. - QgsDataDefined was removed. Use the QgsProperty framework instead. - QgsDataDefinedButton was removed. Use QgsPropertyOverrideButton instead. - QgsDataDefinedSymbolDialog was removed. Code using this dialog should be reworked to use QgsPropertyOverrideButton +- QgsDatumTransformStore. Use QgsCoordinateTransformContext instead. - QgsDefaultPluginLayerLegend was removed. Use QgsMapLayer::setLegend() to provide legend nodes for plugin layers. - DualEdgeTriangulation - QgsFileNameWidgetWrapper was removed. Use QgsExternalResourceWidgetWrapper instead. @@ -353,6 +350,14 @@ General changes {#qgis_api_break_3_0_global} - Network analysis library has been merged into analysis library +Composer {#qgis_api_break_3_0_Composer} +-------- + +All composer related methods have been removed from the public API and Python bindings. These classes +have been replaced with the new layouts engine, based on QgsLayout, QgsLayoutItem, and the other +related classes. + + Labeling {#qgis_api_break_3_0_Labeling} -------- @@ -425,14 +430,6 @@ QgisInterface {#qgis_api_break_3_0_QgisInterface} - fileMenu() has been removed, use projectMenu() instead. - actionRemoveLayer was removed as it no longer exists. - actionTouch was removed, as the corresponding action no longer exists (see notes on QgsMapToolTouch) -- activeComposers(), createNewComposer(), duplicateComposer(), deleteComposer(), composerAdded(), -composerWillBeRemoved(), composerRemoved() were all removed. Composer windows are now only created -on demand, and destroyed when the window is closed. The new interface methods openComposers(), -openComposer(), closeComposer(), composerOpened(), composerWillBeClosed() and composerClosed() -are similar, but only apply to composer windows when they exist. To access all compositions -from a project, the new QgsProject.instance().layoutManager() class should be used instead. -Additionally, the new interface methods work with QgsComposerInterface objects instead -of QgsComposerView objects. - interaction with the main window status bar should no longer use the native Qt statusBar() method. Instead iface.statusBarIface() should be used. @@ -507,22 +504,7 @@ QgsApplication {#qgis_api_break_3_0_QgsApplication} - srsDbFilePath() was renamed to srsDatabaseFilePath() - setAuthDbDirPath() was renamed to setAuthDatabaseDirPath() - createDB() was renamed to createDatabase() - - -QgsAtlasComposition {#qgis_api_break_3_0_QgsAtlasComposition} -------------------- - -- readXMLMapSettings() was removed. QGIS no longer supports upgrading pre 2.2 compositions and if this is a -requirement the projects should first be upgraded by opening and saving in 2.18. -- composerMap() and setComposerMap() were removed. Use QgsComposerMap::atlasDriven() and setAtlasDriven() -instead -- fixedScale() and setFixedScale() were removed. Use QgsComposerMap::atlasScalingMode() and setAtlasScalingMode() -instead -- margin() and setMargin() were removed. Use QgsComposerMap::atlasMargin() and setAtlasMargin() -instead -- setSortKeyAttributeIndex() and sortKeyAttributeIndex() were removed. Use sortKeyAttributeName() -and setSortKeyAttributeName() instead. -- currentFeature() was removed. Use feature() instead. +- composerTemplatePaths() was renamed to layoutTemplatePaths() QgsAttributeDialog {#qgis_api_break_3_0_QgsAttributeDialog} @@ -652,12 +634,14 @@ QgsColorButton {#qgis_api_break_3_0_QgsColorButton} - Behaviour enum and its corresponding setter/getter have been renamed to Behavior - setAllowAlpha() and allowAlpha() were removed. Use setAllowOpacity() and allowOpacity() instead. +- acceptLiveUpdates() and setAcceptLiveUpdates() were removed. This functionality is no longer supported. QgsColorDialog {#qgis_api_break_3_0_QgsColorDialog} -------------- - setAllowAlpha() was removed. Use setAllowOpacity() instead. +- getLiveColor() was removed. This functionality is no longer supported. QgsColorEffect {#qgis_api_break_3_0_QgsColorEffect} @@ -678,208 +662,6 @@ QgsColorSchemeRegistry {#qgis_api_break_3_0_QgsColorSchemeRegistry} - This class is no longer a singleton and instance() has been removed. Instead use QgsApplication::colorSchemeRegistry() to access an application-wide registry. -QgsComposerArrow {#qgis_api_break_3_0_QgsComposerArrow} ----------------- - -- setOutlineWidth(), outlineWidth(), arrowColor() and setArrowColor() were removed. -Use setArrowHeadOutlineWidth(), arrowHeadOutlineWidth(), arrowHeadOutlineColor(), -setArrowHeadOutlineColor(), arrowHeadFillColor(), setArrowHeadFillColor(), -setLineSymbol() or lineSymbol() instead. - - -QgsComposerAttributeTableV2 {#qgis_api_break_3_0_QgsComposerAttributeTableV2} ---------------------------- - -- setDisplayAttributes() was removed. Use setDisplayedFields() instead. - - -QgsComposerItem {#qgis_api_break_3_0_QgsComposerItem} ---------------- - -- zoomContent( int delta, double x, double y ) was removed. Use zoomContent( double, QPointF, ZoomMode ) -instead. -- drawText(), textWidthMillimeters(), fontHeightCharacterMM(), fontAscentMillimeters(), -fontDescentMillimeters(), fontHeightMillimeters(), pixelFontSize(), scaledFontPixelSize(), -drawArrowHead(), angle(), largestRotatedRectWithinBounds(), and rotate() were removed. -Use the corresponding methods in QgsComposerUtils instead. -- rotation() and setRotation() were removed. Use itemRotation() and setItemRotation() -instead. -- lockSymbolSize(), imageSizeConsideringRotation(), cornerPointOnRotatedAndScaledRect(), -sizeChangedByRotation() were removed. No replacement is offered for these methods. -- transparency() and setTransparency() were removed. Use itemOpacity() and setItemOpacity() instead. - -QgsComposerItemCommand {#qgis_api_break_3_0_QgsComposerItemCommand} ----------------------- - -- ItemTransparency was removed. Use ItemOpacity instead. - - -QgsComposerLabel {#qgis_api_break_3_0_QgsComposerLabel} ----------------- - -- setExpressionContext() has been removed. Setup the composition using an atlas and with -expression variables in the composer label item instead. -- setSubstitutions has been removed. Use expression context variables in the composer -label item instead. -- margin() was removed. Use marginX() and marginY() instead. - - -QgsComposerLegend {#qgis_api_break_3_0_QgsComposerLegend} ------------------ - -- model() now returns the new QgsLegendModel (previously QgsLegendModelV2, see \ref qgis_api_break_3_0_renamed_classes). -- modelV2() has been renamed to model(). - - -QgsComposerLegendItem {#qgis_api_break_3_0_QgsComposerLegendItem} ---------------------- - -- writeXMLChildren() has been renamed to writeXmlChildren() - - -QgsComposerMap {#qgis_api_break_3_0_QgsComposerMap} --------------- - -- layerSet() and setLayerSet() have been replaced by layers() and setLayers() which work with list of layers instead of layer IDs -- containsWMSLayer() has been renamed to containsWmsLayer() -- mapRenderer() has been removed. Use mapSettings() instead. -- All grid style and format enums were moved to QgsComposerMapGrid. -- All grid property getters and setters were moved to QgsComposerMapGrid, -and should be accessed using QgsComposerMap::grid() or QgsComposerMap::grids(). -- All overview property getters and setters were moved to QgsComposerMapOverview, -and should be accessed using QgsComposerMap::overview() or QgsComposerMap::overviews(). -- overviewExtentChanged() was moved to QgsComposerMapOverview. -- toggleAtlasPreview(), connectMapOverviewSignals() were no longer required and are removed. -- setRotation() and rotation() were removed. Use setMapRotation() and mapRotation() -instead. -- atlasFixedScale() and setAtlasFixedScale() were removed. Use atlasScalingMode() -and setAtlasScalingMode() instead. -- storeCurrentLayerSet() was removed. Use setLayers() instead. -- The layersChanged() slot was removed. -- setMapCanvas() was removed. This is no longer required to draw map annotations, which are instead retrieved from the composition's -associated project's annotationManager() -- setDrawCanvasItems() and drawCanvasItems() were renamed to setDrawAnnotations() and drawAnnotations() -- setCacheUpdated(), updateCachedImage(), renderModeUpdateCachedImage() and cache() were removed. Use invalidateCache() instead. -- The PreviewMode mode enums were removed. These are no longer used. -- previewMode() and setPreviewMode() were removed. These are no longer required. - - -QgsComposerMapGrid {#qgis_api_break_3_0_QgsComposerMapGrid} ------------------- - -- The annotation position Disabled was removed. QgsComposerMapGrid::HideAll -should be used instead. - - -QgsComposerMultiFrame {#qgis_api_break_3_0_QgsComposerMultiFrame} ---------------------- - -- render( QPainter* p, const QRectF& renderExtent ) was removed. Use -render( QPainter* painter, const QRectF& renderExtent, const int frameIndex ) -instead. -- render( QPainter* painter, const QRectF& renderExtent, const int frameIndex ) -was made pure virtual. - - -QgsComposerNodesItem {#qgis_api_break_3_0_QgsComposerNodesItem} --------------------- - -- _readXMLStyle() has been renamed to _readXmlStyle() -- _writeXMLStyle() has been renamed to _writeXMLStyle() -- unselectNode() has been renamed to deselectNode() - -QgsComposerObject {#qgis_api_break_3_0_QgsComposerObject} ------------------ - -- dataDefinedProperty() and setDataDefinedProperty() no longer use QgsDataDefined objects. -Instead these methods have been ported to the QgsProperty framework. -- dataDefinedEvaluate() was removed. Use the QgsProperty evaluation methods instead. - - -QgsComposerPicture {#qgis_api_break_3_0_QgsComposerPicture} ------------------- - -- setPictureFile() and pictureFile() were removed. Use setPicturePath() -and picturePath() instead. -- rotation() and setRotation() were removed. Use pictureRotation() -and setPictureRotation() instead. -- usePictureExpression() and pictureExpression() were removed. Use -QgsComposerObject::dataDefinedProperty instead. -- setUsePictureExpression() was removed. Use -QgsComposerObject::setDataDefinedProperty() instead. -- updatePictureExpression() was removed. - -QgsComposerScaleBar {#qgis_api_break_3_0_QgsComposerScaleBar} -------------------- - -- The Alignment and SegmentSizeMode enums were moved to QgsScaleBarSettings -- The ScaleBarUnits enum was removed. Use QgsUnitTypes::DistanceUnit instead. -- setBrush() was removed. Use setFillColor() instead. -- setBrush2() was removed. Use setFillColor2() instead. -- setPen() was removed. Use setLineColor() and setLineWidth() instead. -- segmentMillimeters() and firstLabelString() were removed. - - -QgsComposerTable {#qgis_api_break_3_0_QgsComposerTable} ----------------- - -- tableWriteXML() has been renamed to tableWriteXml() -- tableReadXML() has been renamed to tableReadXml() - - -QgsComposerTableV2 {#qgis_api_break_3_0_QgsComposerTableV2} ------------------- - -- rowsVisible(), rowRange(), drawHorizontalGridLines() and - drawVerticalGridLines() were removed. -- WrapBehaviour enum and its setter and getter methods has been renamed to WrapBehavior -- EmptyTableBehaviour enum and its setter and getter methods has been renamed to EmptyTableBehavior - - - QgsComposerView {#qgis_api_break_3_0_QgsComposerView} --------------------- - -- unselectNode() has been renamed to deselectNode() - - - QgsComposerUtils {#qgis_api_break_3_0_QgsComposerUtils} ------------------ - -- readDataDefinedPropertyMap() was renamed to readOldDataDefinedPropertyMap() and the signature has changed -to use a QgsPropertyCollection object. -- readDataDefinedProperty() was renamed to readOldDataDefinedProperty() and the signature has changed -to use the QgsProperty framework objects. -- writeDataDefinedPropertyMap() was removed. This is now handled by QgsPropertyCollection::writeXml() - - -QgsComposition {#qgis_api_break_3_0_QgsComposition} --------------- - -- The constructor no longer takes a reference to a QgsMapSettings object. This is no longer -used by compositions. To set the layers to show in composer maps, the QgsComposerMap::setLayers() -method should be used instead. -- constructor requires QgsProject instance -- addItemsFromXML() has been renamed to addItemsFromXml() -- Constructor with QgsMapRenderer parameter has been removed. Use the variant with QgsMapSettings parameter. -- mapRenderer() has been removed. Use mapSettings() instead. -- setSnapGridTolerance(), setAlignmentSnapTolerance(), alignmentSnapTolerance() and snapGridTolerance() -were removed. Use setSnapTolerance() and snapTolerance() instead. -- getComposerHtmlByItem() was removed. Use QgsComposerFrame::multiFrame() instead. -- pixelFontSize(), pointFontSize(), relativeResizeRect(), relativePosition() were removed. Use the corresponding methods in QgsComposerUtils instead. -- sortZList() was removed. Use refreshZList() instead. -- addComposerTable(), composerTableAdded() were removed. -- setAllUnselected() has been renamed to setAllDeselected. -- worldFileMap() and setWorldFileMap() have been renamed to referenceMap() and setReferenceMap() -- dataDefinedProperty() and setDataDefinedProperty() now use the QgsProperty framework instead -of QgsDataDefined objects. -- mapSettings() was removed. Use QgsComposerMap::mapSettings() instead. -- The composerArrowAdded, composerPolygonAdded, composerPolylineAdded, composerHtmlFrameAdded, composerItemGroupAdded, -composerLabelAdded, composerMapAdded, composerScaleBarAdded, composerLegendAdded, composerPictureAdded, -composerShapeAdded, and composerTableFrameAdded were removed. Use the general itemAdded signal instead to catch -all these item added events. -- addComposerMap no longer takes a setDefaultPreviewStyle argument. -- the mapsToRestore parameter has been removed from addItemsFromXml - QgsCompoundColorWidget {#qgis_api_break_3_0_QgsCompoundColorWidget} ---------------------- @@ -914,11 +696,12 @@ called if changes are made to the CRS database. QgsCoordinateTransform {#qgis_api_break_3_0_QgsCoordinateTransform} ---------------------- -- QgsCoordinateTransform is no longer a QObject. readXml, writeXml and initialize are all normal public members now, +- QgsCoordinateTransform is no longer a QObject. Initialize is a normal public members now, not slots. The invalidTransformInput() signal has been removed. - The extra QgsCoordinateTransform constructors (those not taking QgsCoordinateReferenceSystem arguments) have been removed. Now, QgsCoordinateTransform must be created using an already existing source and destination -QgsCoordinateReferenceSystem object. +QgsCoordinateReferenceSystem object, and either a QgsCoordinateTransformContext object or a reference to the +current project instance. - QgsCoordinateTransform::clone() has been removed. Just use direct copies instead. - sourceCrs() and destCrs() now return a copy instead of a reference to the CRS. This has no effect on PyQGIS code, but c++ plugins calling these methods will need to be updated. @@ -930,7 +713,15 @@ plugins calling these methods will need to be updated. - 'p' argument in transform() has been renamed to 'point', 'theRect' to 'rectangle', 'poly' to 'polygon' - setDestCRSID has been removed, use setDestinationCrs() instead - 'theNode', 'theDoc' parameters in readXML and writeXML have been renamed to 'node' and 'document' respectively -- readXML() and writeXML() have been renamed to readXml() and writeXml() for consistency +- readXML() and writeXML() have been removed. +- initialize() was removed, calls to this method can safely be omitted. +- datumTransformations() was moved to QgsDatumTransform, and now returns a list of QgsDatumTransform.TransformPair instead of a list of lists. +- datumTransformString() was moved to QgsDatumTransform and renamed to datumTransformToProj() +- datumTransformCrsInfo() was moved to QgsDatumTransform and renamed to datumTransformInfo(), and now returns a QgsDatumTransform.TransformInfo object. +- sourceDatumTransform() was renamed to sourceDatumTransformId() +- setSourceDatumTransform() was renamed to setSourceDatumTransformId() +- destinationDatumTransform() was renamed to destinationDatumTransformId() +- setDestinationDatumTransform() was renamed to setDestinationDatumTransformId() QgsCoordinateTransformCache {#qgis_api_break_3_0_QgsCoordinateTransformCache} @@ -1117,7 +908,8 @@ ellipsoid to 'NONE' to disable ellipsoidal calculations. - ellipsoidalEnabled() was removed. Ellipsoidal calculations are now enabled whenever a valid ellipsoid() is set. Check willUseEllipsoid() to determine whether ellipsoidal calculations will be performed. - sourceCrs() now returns a QgsCoordinateReferenceSystem instead of the crs ID. -- setSourceCrs() now requires a QgsCoordinateReferenceSystem instead of crs ID. +- setSourceCrs() now requires a QgsCoordinateReferenceSystem instead of crs ID, and requires a QgsCoordinateTransformContext object. PyQGIS code +can use QgsProject.instance().transformContext() for the QgsCoordinateTransformContext argument. - setSourceAuthId() was removed. Use setSourceCrs() instead. - geographic() was removed. Check sourceCrs().isGeographic() instead. - measure() has been removed. Use measureArea() or measureLength() instead. @@ -1164,6 +956,8 @@ QgsEditorWidgetWrapper {#qgis_api_break_3_0_QgsEditorWidgetWrapper} - constraintStatusChanged now reports a QgsEditorWidgetWrapper::ConstraintResult instead of the previous boolean value for the constraint status - The boolean constraintValid argument for updateConstraintWidgetStatus has been changed to a QgsEditorWidgetWrapper::ConstraintResult value +- All `valueChanged( value )` slots have been removed. Use `emit valueChanged( value )` instead. +- The `valueChanged()` slot has been renamed to `emitValueChanged()`. QgsExpression {#qgis_api_break_3_0_QgsExpression} @@ -1342,6 +1136,8 @@ maintains Z or M dimensions from the input points and is more efficient. - exportToWkt() was renamed to asWkt() - exportToGeoJSON() was renamed to asJson() - closestSegmentWithContext() now returns an extra value, indicating whether the point is to the left of the geometry +- equals() now performs a fast, strict equality check, where geometries are considered equal only if they have the +exact same type, vertices and order. The slower topological test can be performed by calling isGeosEqual instead. QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer} @@ -1453,6 +1249,10 @@ QgsGeometryUtils {#qgis_api_break_3_0_QgsGeometryUtils} - componentType enum has been renamed to ComponentType and its members were CamelCased too: VERTEX, RING and PART become Vertex, Ring and Part, respectively. - adjacentVertices was removed - use QgsAbstractGeometry.adjacentVertices instead. +- segmentIntersection takes an optional boolean parameter "acceptImproperIntersection" returning true even if the intersection is improper. +Takes another boolean argument "isIntersection" returning if there is an intersection or not. The "inter" parameter has been renamed "intersectionPoint". +- projPointOnSegment has been renamed to projectPointOnSegment +- getSelfIntersections has been renamed to selfIntersections QgsGPSConnectionRegistry {#qgis_api_break_3_0_QgsGPSConnectionRegistry} @@ -1636,6 +1436,7 @@ QgsMapCanvas {#qgis_api_break_3_0_QgsMapCanvas} - setCrsTransformEnabled(), hasCrsTransformEnabled(), hasCrsTransformEnabledChanged() were removed. CRS transformation is now always enabled. - setMapUnits() was removed. The map units are dictated by the units for the destination CRS. - The mapUnitsChanged() signal was removed. Listen for the destinationCrsChanged() signal instead, as the destination CRS dictates the map units. +- layerCrsChange() slot was removed. Datum transforms are now handled in QgisApp. QgsMapCanvasItem {#qgis_api_break_3_0_QgsMapCanvasItem} ---------------- @@ -1988,7 +1789,8 @@ QgsPointLocator {#qgis_api_break_3_0_QgsPointLocator} --------------- - The constructor now takes a reference rather than a pointer to a CRS. This has no effect on PyQGIS code, but c++ -plugins calling this method will need to be updated. +plugins calling this method will need to be updated. The constructor now requires a QgsCoordinateTransformContext argument +when a destination crs is specified. - The destCRS parameter in the constructor has been renamed to destinationCrs. - destCRS() has been renamed to destinationCrs() - destinationCrs() now returns a copy instead of a reference to the CRS. This has no effect on PyQGIS code, but c++ @@ -2075,6 +1877,9 @@ QgsRasterDataProvider {#qgis_api_break_3_0_QgsRasterDataProvider} - draw() has been removed from the interface as it was not used anywhere. - The progress and progressUpdate signals were removed. Methods which previously emitted these signals now accept a QgsRasterBlockFeedback argument for reporting progress updates. +- metadata() was renamed to htmlMetadata() +- makeTableCell() has been removed +- makeTableCells() has been removed, look for the new class QgsHtmlUtils QgsRasterFileWriter {#qgis_api_break_3_0_QgsRasterFileWriter} @@ -2116,6 +1921,12 @@ loadDefaultStyle argument. signals now accept a QgsRasterBlockFeedback argument for reporting progress updates. +QgsRasterLayerSaveAsDialog {#qgis_api_break_3_0_QgsRasterLayerSaveAsDialog} +-------------------------- + +- The currentExtent and currentCrs arguments have been dropped from the constructor. Use setMapCanvas() instead. + + QgsRasterProjector {#qgis_api_break_3_0_QgsRasterProjector} ------------------ @@ -2507,6 +2318,8 @@ QgsTracer {#qgis_api_break_3_0_QgsTracer} --------- - hasCrsTransformEnabled() and setCrsTransformEnabled() were removed. CRS transformation is now always enabled when required. +- setDestinationCrs() now requires a QgsCoordinateTransformContext argument. + QgsTransaction {#qgis_api_break_3_0_QgsTransaction} -------------- @@ -2630,6 +2443,10 @@ displayExpression instead. For the map tip use mapTipTemplate() instead. - addPart() now returns QgsGeometry::OperationResult enum, integer representation of returned values may have changed - splitParts() now returns QgsGeometry::OperationResult enum, integer representation of returned values may have changed - splitFeatured() now returns QgsGeometry::OperationResult enum, integer representation of returned values may have changed +- pendingFields() was dropped. Use fields() instead. +- pendingAllAttributesList() was dropped. Use allAttributes() instead. +- pendingPkAttributesList() and pkAttributeList() were dropped. Use primaryKeyAttributes() instead. +- pendingFeatureCount() was dropped. Use featureCount() instead. QgsVectorLayerEditBuffer {#qgis_api_break_3_0_QgsVectorLayerEditBuffer} diff --git a/doc/contributors.json b/doc/contributors.json index 0046b2f7bd1..eb0bf240dae 100644 --- a/doc/contributors.json +++ b/doc/contributors.json @@ -8,7 +8,7 @@ "Committer": "Yes", "First Commit Message": "Initial revision", "First Commit Date": "06-07-2002", - "GIT Nickname": "gsherman" + "GIT Nickname": "g-sherman" }, "geometry": { "type": "Point", @@ -141,7 +141,7 @@ "Committer": "Yes", "First Commit Message": "some dutch translations for git testing", "First Commit Date": "4-9-2011", - "GIT Nickname": "duiv" + "GIT Nickname": "rduivenvoorde" }, "geometry": { "type": "Point", @@ -277,7 +277,7 @@ "Committer": "Yes", "First Commit Message": "some bugfixes", "First Commit Date": "07-04-2004", - "GIT Nickname": "rabla,rblazek,blazek" + "GIT Nickname": "blazek" }, "geometry": { "type": "Point", @@ -334,7 +334,7 @@ "type": "Point", "coordinates": [ 104.9110, - 11.5582 + 11.5582 ] } }, @@ -581,8 +581,8 @@ "geometry": { "type": "Point", "coordinates": [ - 6.86121, - 46.47796 + -70.413406, + 19.745284 ] } }, @@ -644,7 +644,7 @@ "Committer": "Yes", "First Commit Message": "Patch to allow setting extents from cli on startup", "First Commit Date": "03-06-2005", - "GIT Nickname": "wonder" + "GIT Nickname": "wonder-sk" }, "geometry": { "type": "Point", diff --git a/doc/developersmap.html b/doc/developersmap.html index b9e0d1efe2f..d0f9e55c609 100644 --- a/doc/developersmap.html +++ b/doc/developersmap.html @@ -17,14 +17,23 @@ " ) ); - - mComposition->setAtlasMode( QgsComposition::ExportAtlas ); - QVERIFY( mComposition->atlasComposition().beginRender() ); - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); - - htmlItem->loadHtml(); - - QgsCompositionChecker checker( QStringLiteral( "composerhtml_setfeature" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_html" ) ); - bool result = checker.testComposition( mReport ); - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; - QVERIFY( result ); - - QgsProject::instance()->removeMapLayers( QList() << childLayer << parentLayer ); -} - - -QGSTEST_MAIN( TestQgsComposerHtml ) -#include "testqgscomposerhtml.moc" diff --git a/tests/src/core/testqgscomposerlabel.cpp b/tests/src/core/testqgscomposerlabel.cpp deleted file mode 100644 index 3df5ff692a5..00000000000 --- a/tests/src/core/testqgscomposerlabel.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*************************************************************************** - testqgscomposerlabel.cpp - ---------------------- - begin : Sept 2012 - copyright : (C) 2012 by Hugo Mercier - email : hugo dot mercier at oslandia dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgscomposerlabel.h" -#include "qgsvectorlayer.h" -#include "qgsvectordataprovider.h" -#include "qgsmultirenderchecker.h" -#include "qgsfontutils.h" -#include "qgsproject.h" - -#include -#include "qgstest.h" - -class TestQgsComposerLabel : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerLabel() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - - // test simple expression evaluation - void evaluation(); - // test expression evaluation when a feature is set - void feature_evaluation(); - // test page expressions - void page_evaluation(); - void marginMethods(); //tests getting/setting margins - void render(); - void renderAsHtml(); - void renderAsHtmlRelative(); - - private: - QgsComposition *mComposition = nullptr; - QgsComposerLabel *mComposerLabel = nullptr; - QgsVectorLayer *mVectorLayer = nullptr; - QString mReport; -}; - -void TestQgsComposerLabel::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create maplayers from testdata and add to layer registry - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + '/' + "france_parts.shp" ); - mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - - //create composition with composer map - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposition->atlasComposition().setCoverageLayer( mVectorLayer ); - - mComposerLabel = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( mComposerLabel ); - - qWarning() << "composer label font: " << mComposerLabel->font().toString() << " exactMatch:" << mComposerLabel->font().exactMatch(); -} - -void TestQgsComposerLabel::cleanupTestCase() -{ - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - delete mComposition; - delete mVectorLayer; - - QgsApplication::exitQgis(); -} - -void TestQgsComposerLabel::init() -{ - mComposerLabel = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( mComposerLabel ); -} - -void TestQgsComposerLabel::cleanup() -{ - mComposition->removeItem( mComposerLabel ); - delete mComposerLabel; - mComposerLabel = 0; -} - -void TestQgsComposerLabel::evaluation() -{ - { - // $CURRENT_DATE evaluation - QString expected = "__" + QDate::currentDate().toString() + "__"; - mComposerLabel->setText( QStringLiteral( "__$CURRENT_DATE__" ) ); - QString evaluated = mComposerLabel->displayText(); - QCOMPARE( evaluated, expected ); - } - { - // $CURRENT_DATE() evaluation - QDateTime now = QDateTime::currentDateTime(); - QString expected = "__" + now.toString( QStringLiteral( "dd" ) ) + "(ok)__"; - mComposerLabel->setText( QStringLiteral( "__$CURRENT_DATE(dd)(ok)__" ) ); - QString evaluated = mComposerLabel->displayText(); - QCOMPARE( evaluated, expected ); - } - { - // $CURRENT_DATE() evaluation (inside an expression) - QDate now = QDate::currentDate(); - int dd = now.day(); - - QString expected = "__" + QStringLiteral( "%1" ).arg( dd + 1 ) + "(ok)__"; - mComposerLabel->setText( QStringLiteral( "__[%$CURRENT_DATE(dd) + 1%](ok)__" ) ); - QString evaluated = mComposerLabel->displayText(); - QCOMPARE( evaluated, expected ); - } - { - // expression evaluation (without feature) - QString expected = QStringLiteral( "__[NAME_1]42__" ); - mComposerLabel->setText( QStringLiteral( "__[%\"NAME_1\"%][%21*2%]__" ) ); - QString evaluated = mComposerLabel->displayText(); - QCOMPARE( evaluated, expected ); - } -} - -void TestQgsComposerLabel::feature_evaluation() -{ - mComposition->atlasComposition().setEnabled( true ); - mComposition->setAtlasMode( QgsComposition::ExportAtlas ); - mComposition->atlasComposition().updateFeatures(); - mComposition->atlasComposition().prepareForFeature( 0 ); - { - // evaluation with a feature - mComposerLabel->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); - QString evaluated = mComposerLabel->displayText(); - QString expected = QStringLiteral( "Basse-Normandie_ok" ); - QCOMPARE( evaluated, expected ); - } - mComposition->atlasComposition().prepareForFeature( 1 ); - { - // evaluation with a feature - mComposerLabel->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); - QString evaluated = mComposerLabel->displayText(); - QString expected = QStringLiteral( "Bretagne_ok" ); - QCOMPARE( evaluated, expected ); - } - mComposition->atlasComposition().setEnabled( false ); -} - -void TestQgsComposerLabel::page_evaluation() -{ - mComposition->setNumPages( 2 ); - { - mComposerLabel->setText( QStringLiteral( "[%@layout_page||'/'||@layout_numpages%]" ) ); - QString evaluated = mComposerLabel->displayText(); - QString expected = QStringLiteral( "1/2" ); - QCOMPARE( evaluated, expected ); - - // move to the second page and re-evaluate - mComposerLabel->setItemPosition( 0, 320 ); - QCOMPARE( mComposerLabel->displayText(), QString( "2/2" ) ); - } -} - -void TestQgsComposerLabel::marginMethods() -{ - QgsComposerLabel label( mComposition ); - //test setting margins separately - label.setMarginX( 3.0 ); - label.setMarginY( 4.0 ); - QCOMPARE( label.marginX(), 3.0 ); - QCOMPARE( label.marginY(), 4.0 ); - //test setting margins together - label.setMargin( 5.0 ); - QCOMPARE( label.marginX(), 5.0 ); - QCOMPARE( label.marginY(), 5.0 ); - - //test reading label margins from pre 2.7 projects - QDomDocument labelDoc; - QString labelXml; - labelXml = QStringLiteral( "=2.7 projects - labelXml = QStringLiteral( "setText( QStringLiteral( "test label" ) ); - mComposerLabel->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) ); - mComposerLabel->setPos( 70, 70 ); - mComposerLabel->adjustSizeToText(); - - QgsCompositionChecker checker( QStringLiteral( "composerlabel_render" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_label" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerLabel::renderAsHtml() -{ - mComposerLabel->setFontColor( QColor( 200, 40, 60 ) ); - mComposerLabel->setText( QStringLiteral( "test html" ) ); - mComposerLabel->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) ); - mComposerLabel->setPos( 70, 70 ); - mComposerLabel->adjustSizeToText(); - mComposerLabel->setHtmlState( 1 ); - mComposerLabel->update(); - - QgsCompositionChecker checker( QStringLiteral( "composerlabel_renderhtml" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_label" ) ); - QVERIFY( checker.testComposition( mReport, 0, 10 ) ); -} - -void TestQgsComposerLabel::renderAsHtmlRelative() -{ - QgsProject::instance()->setFileName( QStringLiteral( TEST_DATA_DIR ) + QDir::separator() + "test.qgs" ); - mComposerLabel->setFontColor( QColor( 200, 40, 60 ) ); - mComposerLabel->setText( QStringLiteral( "test " ) ); - mComposerLabel->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 48 ) ); - mComposerLabel->setPos( 70, 70 ); - mComposerLabel->adjustSizeToText(); - mComposerLabel->setHtmlState( 1 ); - mComposerLabel->update(); - - QgsCompositionChecker checker( QStringLiteral( "composerlabel_renderhtmlrelative" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_label" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -QGSTEST_MAIN( TestQgsComposerLabel ) -#include "testqgscomposerlabel.moc" diff --git a/tests/src/core/testqgscomposermap.cpp b/tests/src/core/testqgscomposermap.cpp deleted file mode 100644 index 30fb688399a..00000000000 --- a/tests/src/core/testqgscomposermap.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/*************************************************************************** - testqgscomposermap.cpp - ---------------------- - begin : Juli 2012 - copyright : (C) 2012 by Marco Hugentobler - email : marco at sourcepole dot ch - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposermap.h" -#include "qgsmultibandcolorrenderer.h" -#include "qgsrasterlayer.h" -#include "qgsrasterdataprovider.h" -#include "qgsvectorlayer.h" -#include "qgsvectordataprovider.h" -#include "qgsproject.h" -#include "qgsmapthemecollection.h" -#include "qgsproperty.h" -#include -#include "qgstest.h" - -class TestQgsComposerMap : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerMap() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void render(); //test if rendering of the composition with composr map is correct - void uniqueId(); //test if map id is adapted when doing copy paste - void worldFileGeneration(); // test world file generation - void mapPolygonVertices(); // test mapPolygon function with no map rotation - void dataDefinedLayers(); //test data defined layer string - void dataDefinedStyles(); //test data defined styles - - private: - QgsComposition *mComposition = nullptr; - QgsComposerMap *mComposerMap = nullptr; - QgsRasterLayer *mRasterLayer = nullptr; - QgsVectorLayer *mPointsLayer = nullptr; - QgsVectorLayer *mPolysLayer = nullptr; - QgsVectorLayer *mLinesLayer = nullptr; - QString mReport; -}; - -void TestQgsComposerMap::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create maplayers from testdata and add to layer registry - QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif" ); - mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 2, 3, 4 ); - mRasterLayer->setRenderer( rasterRenderer ); - - QFileInfo pointFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - mPointsLayer = new QgsVectorLayer( pointFileInfo.filePath(), - pointFileInfo.completeBaseName(), QStringLiteral( "ogr" ) ); - - QFileInfo polyFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ); - mPolysLayer = new QgsVectorLayer( polyFileInfo.filePath(), - polyFileInfo.completeBaseName(), QStringLiteral( "ogr" ) ); - - QFileInfo lineFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/lines.shp" ); - mLinesLayer = new QgsVectorLayer( lineFileInfo.filePath(), - lineFileInfo.completeBaseName(), QStringLiteral( "ogr" ) ); - - // some layers need to be in project for data-defined layers functionality - QgsProject::instance()->addMapLayers( QList() << mRasterLayer << mPointsLayer << mPolysLayer << mLinesLayer ); -} - -void TestQgsComposerMap::cleanupTestCase() -{ - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerMap::init() -{ - //create composition with composer map - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - mComposerMap->setFrameEnabled( true ); - mComposerMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( mComposerMap ); - - mReport = QStringLiteral( "

Composer Map Tests

\n" ); -} - -void TestQgsComposerMap::cleanup() -{ - delete mComposition; -} - -void TestQgsComposerMap::render() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - QgsCompositionChecker checker( QStringLiteral( "composermap_render" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_map" ) ); - - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerMap::uniqueId() -{ - QDomDocument doc; - QDomElement documentElement = doc.createElement( QStringLiteral( "ComposerItemClipboard" ) ); - mComposerMap->writeXml( documentElement, doc ); - mComposition->addItemsFromXml( documentElement, doc, false ); - - //test if both composer maps have different ids - const QgsComposerMap *newMap = 0; - QList mapList = mComposition->composerMapItems(); - QList::const_iterator mapIt = mapList.constBegin(); - for ( ; mapIt != mapList.constEnd(); ++mapIt ) - { - if ( *mapIt != mComposerMap ) - { - newMap = *mapIt; - break; - } - } - - QVERIFY( newMap ); - - int oldId = mComposerMap->id(); - int newId = newMap->id(); - - mComposition->removeComposerItem( const_cast( newMap ) ); - - QVERIFY( oldId != newId ); -} - -void TestQgsComposerMap::worldFileGeneration() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->setMapRotation( 30.0 ); - - mComposition->setGenerateWorldFile( true ); - mComposition->setReferenceMap( mComposerMap ); - - double a, b, c, d, e, f; - mComposition->computeWorldFileParameters( a, b, c, d, e, f ); - - QGSCOMPARENEAR( a, 4.18048, 0.001 ); - QGSCOMPARENEAR( b, 2.41331, 0.001 ); - QGSCOMPARENEAR( c, 779444, 1 ); - QGSCOMPARENEAR( d, 2.4136, 0.001 ); - QGSCOMPARENEAR( e, -4.17997, 0.001 ); - QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); - - //test with map on second page. Parameters should be the same - mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); - mComposition->computeWorldFileParameters( a, b, c, d, e, f ); - - QGSCOMPARENEAR( a, 4.18048, 0.001 ); - QGSCOMPARENEAR( b, 2.41331, 0.001 ); - QGSCOMPARENEAR( c, 779444, 1 ); - QGSCOMPARENEAR( d, 2.4136, 0.001 ); - QGSCOMPARENEAR( e, -4.17997, 0.001 ); - QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); - - //test computing parameters for specific region - mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); - mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f ); - - QGSCOMPARENEAR( a, 4.18061, 0.001 ); - QGSCOMPARENEAR( b, 2.41321, 0.001 ); - QGSCOMPARENEAR( c, 773810, 1 ); - QGSCOMPARENEAR( d, 2.4137, 0.001 ); - QGSCOMPARENEAR( e, -4.1798, 0.001 ); - QGSCOMPARENEAR( f, 3.35331e+06, 1e+03 ); - - mComposition->setGenerateWorldFile( false ); - mComposerMap->setMapRotation( 0.0 ); - -} - -void TestQgsComposerMap::mapPolygonVertices() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - QPolygonF visibleExtent = mComposerMap->visibleExtentPolygon(); - - //vertices should be returned in clockwise order starting at the top-left point - QVERIFY( std::fabs( visibleExtent[0].x() - 781662.375 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[0].y() - 3345223.125 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[1].x() - 793062.375 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[1].y() - 3345223.125 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[2].x() - 793062.375 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[2].y() - 3339523.125 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[3].x() - 781662.375 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[3].y() - 3339523.125 ) < 0.001 ); - - //polygon should be closed - QVERIFY( visibleExtent.isClosed() ); - - //now test with rotated map - mComposerMap->setMapRotation( 10 ); - visibleExtent = mComposerMap->visibleExtentPolygon(); - - //vertices should be returned in clockwise order starting at the top-left point - QVERIFY( std::fabs( visibleExtent[0].x() - 781254.0735015 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[0].y() - 3344190.0324834 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[1].x() - 792480.881886 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[1].y() - 3346169.62171 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[2].x() - 793470.676499 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[2].y() - 3340556.21752 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[3].x() - 782243.868114 ) < 0.001 ); - QVERIFY( std::fabs( visibleExtent[3].y() - 3338576.62829 ) < 0.001 ); - - //polygon should be closed - QVERIFY( visibleExtent.isClosed() ); - - mComposerMap->setMapRotation( 0 ); - -} - -void TestQgsComposerMap::dataDefinedLayers() -{ - delete mComposition; - QgsMapSettings ms; - ms.setLayers( QList() << mRasterLayer << mPolysLayer << mPointsLayer << mLinesLayer ); - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - mComposerMap->setFrameEnabled( true ); - mComposition->addComposerMap( mComposerMap ); - - //test malformed layer set string - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( QStringLiteral( "'x'" ) ) ); - QList result = mComposerMap->layersToRender(); - QVERIFY( result.isEmpty() ); - - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( QStringLiteral( "'x|'" ) ) ); - result = mComposerMap->layersToRender(); - QVERIFY( result.isEmpty() ); - - //test subset of valid layers - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( - QStringLiteral( "'%1|%2'" ).arg( mPolysLayer->name(), mRasterLayer->name() ) ) ); - result = mComposerMap->layersToRender(); - QCOMPARE( result.count(), 2 ); - QVERIFY( result.contains( mPolysLayer ) ); - QVERIFY( result.contains( mRasterLayer ) ); - - //test non-existent layer - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( - QStringLiteral( "'x|%1|%2'" ).arg( mLinesLayer->name(), mPointsLayer->name() ) ) ); - result = mComposerMap->layersToRender(); - QCOMPARE( result.count(), 2 ); - QVERIFY( result.contains( mLinesLayer ) ); - QVERIFY( result.contains( mPointsLayer ) ); - - //test no layers - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( - QStringLiteral( "''" ) ) ); - result = mComposerMap->layersToRender(); - QVERIFY( result.isEmpty() ); - - - //test with atlas feature evaluation - QgsVectorLayer *atlasLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string" ), QStringLiteral( "atlas" ), QStringLiteral( "memory" ) ); - QVERIFY( atlasLayer->isValid() ); - QgsFeature f1( atlasLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), mLinesLayer->name() ); - QgsFeature f2( atlasLayer->dataProvider()->fields(), 1 ); - f2.setAttribute( QStringLiteral( "col1" ), mPointsLayer->name() ); - atlasLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 ); - mComposition->atlasComposition().setCoverageLayer( atlasLayer ); - mComposition->atlasComposition().setEnabled( true ); - mComposition->setAtlasMode( QgsComposition::ExportAtlas ); - mComposition->atlasComposition().beginRender(); - mComposition->atlasComposition().prepareForFeature( 0 ); - - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromField( QStringLiteral( "col1" ) ) ); - result = mComposerMap->layersToRender(); - QCOMPARE( result.count(), 1 ); - QCOMPARE( result.at( 0 ), mLinesLayer ); - mComposition->atlasComposition().prepareForFeature( 1 ); - result = mComposerMap->layersToRender(); - QCOMPARE( result.count(), 1 ); - QCOMPARE( result.at( 0 ), mPointsLayer ); - mComposition->atlasComposition().setEnabled( false ); - delete atlasLayer; - - //render test - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( - QStringLiteral( "'%1|%2'" ).arg( mPolysLayer->name(), mPointsLayer->name() ) ) ); - mComposerMap->setNewExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_ddlayers" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_map" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerMap::dataDefinedStyles() -{ - delete mComposition; - - QList layers = QList() << mRasterLayer << mPolysLayer << mPointsLayer << mLinesLayer; - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - mComposerMap->setFrameEnabled( true ); - mComposerMap->setLayers( layers ); - mComposition->addComposerMap( mComposerMap ); - - QgsMapThemeCollection::MapThemeRecord rec; - rec.setLayerRecords( QList() - << QgsMapThemeCollection::MapThemeLayerRecord( mPointsLayer ) - << QgsMapThemeCollection::MapThemeLayerRecord( mLinesLayer ) - ); - - QgsProject::instance()->mapThemeCollection()->insert( QStringLiteral( "test preset" ), rec ); - - // test following of preset - mComposerMap->setFollowVisibilityPreset( true ); - mComposerMap->setFollowVisibilityPresetName( QStringLiteral( "test preset" ) ); - QSet result = mComposerMap->layersToRender().toSet(); - QCOMPARE( result.count(), 2 ); - mComposerMap->setFollowVisibilityPresetName( QString() ); - - //test malformed style string - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "5" ) ) ); - result = mComposerMap->layersToRender().toSet(); - QCOMPARE( result, layers.toSet() ); - - //test valid preset - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) ); - result = mComposerMap->layersToRender().toSet(); - QCOMPARE( result.count(), 2 ); - QVERIFY( result.contains( mLinesLayer ) ); - QVERIFY( result.contains( mPointsLayer ) ); - - //test non-existent preset - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'bad preset'" ) ) ); - result = mComposerMap->layersToRender().toSet(); - QCOMPARE( result, layers.toSet() ); - - //test that dd layer set overrides style layers - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) ); - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty::fromExpression( - QStringLiteral( "'%1'" ).arg( mPolysLayer->name() ) ) ); - result = mComposerMap->layersToRender().toSet(); - QCOMPARE( result.count(), 1 ); - QVERIFY( result.contains( mPolysLayer ) ); - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapLayers, QgsProperty() ); - - //render test - mComposerMap->dataDefinedProperties().setProperty( QgsComposerObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) ); - mComposerMap->setNewExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_ddstyles" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_map" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -QGSTEST_MAIN( TestQgsComposerMap ) -#include "testqgscomposermap.moc" diff --git a/tests/src/core/testqgscomposermapgrid.cpp b/tests/src/core/testqgscomposermapgrid.cpp deleted file mode 100644 index 3d50337eb08..00000000000 --- a/tests/src/core/testqgscomposermapgrid.cpp +++ /dev/null @@ -1,704 +0,0 @@ -/*************************************************************************** - testqgscomposermapgrid.cpp - ---------------------- - begin : August 2014 - copyright : (C) 2014 by Nyall Dawosn, Marco Hugentobler - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposermap.h" -#include "qgscomposermapgrid.h" -#include "qgsfontutils.h" -#include "qgsproject.h" -#include -#include "qgstest.h" - -class TestQgsComposerMapGrid : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerMapGrid(); - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void grid(); //test if grid and grid annotation works - void reprojected(); //test if reprojected grid works - void crossGrid(); //test if grid "cross" mode works - void markerGrid(); //test if grid "marker" mode works - void frameOnly(); //test if grid "frame/annotation" mode works - void zebraStyle(); //test zebra map border style - void zebraStyleSides(); //test zebra border on certain sides - void frameDivisions(); //test filtering frame divisions - void annotationFilter(); //test filtering annotations - void interiorTicks(); //test interior tick mode - void interiorTicksAnnotated(); //test interior tick mode with annotations - void exteriorTicks(); //test exterior tick mode - void exteriorTicksAnnotated(); //test exterior tick mode with annotations - void interiorExteriorTicks(); //test interior & exterior tick mode - void interiorExteriorTicksAnnotated(); //test interior & exterior tick mode with annotations - void lineBorder(); //test line border frame mode - void lineBorderAnnotated(); //test line border frame with annotations - void annotationFormats(); //various tests for annotation formats - void descendingAnnotations(); //test descending annotation direction - - private: - QgsComposition *mComposition = nullptr; - QgsComposerMap *mComposerMap = nullptr; - QgsMapSettings *mMapSettings = nullptr; - QString mReport; -}; - -TestQgsComposerMapGrid::TestQgsComposerMapGrid() = default; - -void TestQgsComposerMapGrid::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - mMapSettings = new QgsMapSettings(); - - mReport = QStringLiteral( "

Composer Map Grid Tests

\n" ); -} - -void TestQgsComposerMapGrid::cleanupTestCase() -{ - delete mMapSettings; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerMapGrid::init() -{ - QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem( 32633 ); - mMapSettings->setDestinationCrs( crs ); - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - mComposerMap->setFrameEnabled( true ); - mComposerMap->setBackgroundColor( QColor( 150, 100, 100 ) ); - mComposerMap->grid()->setAnnotationFont( QgsFontUtils::getStandardTestFont() ); - mComposerMap->grid()->setAnnotationPrecision( 0 ); - mComposerMap->grid()->setIntervalX( 2000 ); - mComposerMap->grid()->setIntervalY( 2000 ); - mComposerMap->grid()->setGridLineWidth( 0.5 ); - mComposerMap->grid()->setGridLineColor( QColor( 0, 0, 0 ) ); - mComposerMap->updateBoundingRect(); - mComposition->addComposerMap( mComposerMap ); -} - -void TestQgsComposerMapGrid::cleanup() -{ - delete mComposition; -} - -void TestQgsComposerMapGrid::grid() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->grid()->setEnabled( true ); - - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setGridLineColor( QColor( 0, 255, 0 ) ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::HideAll, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::HideAll, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::Horizontal, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::Horizontal, QgsComposerMapGrid::Bottom ); - mComposerMap->grid()->setAnnotationFontColor( QColor( 255, 0, 0, 150 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_Overlay ); - mComposerMap->updateBoundingRect(); - qWarning() << "grid annotation font: " << mComposerMap->grid()->annotationFont().toString() << " exactMatch:" << mComposerMap->grid()->annotationFont().exactMatch(); - QgsCompositionChecker checker( QStringLiteral( "composermap_grid" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposerMap->grid()->setEnabled( false ); - mComposerMap->grid()->setAnnotationEnabled( false ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::reprojected() -{ - mComposerMap->setNewExtent( QgsRectangle( -243577.565, 2939084.773, 1215622.435, 3668684.773 ) ); - QgsCoordinateReferenceSystem geographic = QgsCoordinateReferenceSystem( 4326 ); - mComposerMap->grid()->setCrs( geographic ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setIntervalX( 1 ); - mComposerMap->grid()->setIntervalY( 1 ); - mComposerMap->grid()->setAnnotationEnabled( false ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::ExteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->setFrameEnabled( false ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker( QStringLiteral( "composermap_gridreprojected" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposerMap->grid()->setEnabled( false ); - mComposerMap->grid()->setCrs( mMapSettings->destinationCrs() ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->setFrameEnabled( true ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::crossGrid() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::Cross ); - mComposerMap->grid()->setCrossLength( 2.0 ); - mComposerMap->grid()->setAnnotationEnabled( false ); - mComposerMap->grid()->setGridLineColor( QColor( 0, 255, 0 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker( QStringLiteral( "composermap_crossgrid" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::Solid ); - mComposerMap->grid()->setEnabled( false ); - mComposerMap->grid()->setAnnotationEnabled( false ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::markerGrid() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::Markers ); - mComposerMap->grid()->setAnnotationEnabled( false ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker( QStringLiteral( "composermap_markergrid" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::Solid ); - mComposerMap->grid()->setEnabled( false ); - mComposerMap->grid()->setAnnotationEnabled( false ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::frameOnly() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( false ); - //set a frame for testing - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::Zebra ); - mComposerMap->grid()->setFrameWidth( 2.0 ); - mComposerMap->grid()->setFramePenSize( 0.5 ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker( QStringLiteral( "composermap_gridframeonly" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::Solid ); - mComposerMap->grid()->setEnabled( false ); - mComposerMap->grid()->setAnnotationEnabled( false ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::zebraStyle() -{ - mComposerMap->setNewExtent( QgsRectangle( 785462.375, 3341423.125, 789262.375, 3343323.125 ) ); //zoom in - mComposerMap->grid()->setAnnotationFontColor( QColor( 0, 0, 0, 0 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::Zebra ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( QColor( 255, 100, 0, 200 ) ); - mComposerMap->grid()->setFrameFillColor1( QColor( 50, 90, 50, 100 ) ); - mComposerMap->grid()->setFrameFillColor2( QColor( 200, 220, 100, 60 ) ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_zebrastyle" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapGrid::zebraStyleSides() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->grid()->setAnnotationFontColor( QColor( 0, 0, 0, 0 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::Zebra ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setFrameFillColor1( Qt::black ); - mComposerMap->grid()->setFrameFillColor2( Qt::white ); - mComposerMap->grid()->setEnabled( true ); - - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameLeft, true ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameRight, false ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameTop, false ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameBottom, false ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_zebrastyle_left" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameTop, true ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker2( QStringLiteral( "composermap_zebrastyle_lefttop" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameRight, true ); - mComposerMap->updateBoundingRect(); - QgsCompositionChecker checker3( QStringLiteral( "composermap_zebrastyle_lefttopright" ), mComposition ); - checker3.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult3 = checker3.testComposition( mReport, 0, 0 ); - QVERIFY( testResult3 ); - - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameBottom, true ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - -} - -void TestQgsComposerMapGrid::frameDivisions() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - //rotate map, so we mix latitude and longitude coordinates on every map side - mComposerMap->setMapRotation( 45.0 ); - - //setup defaults - mComposerMap->grid()->setAnnotationFontColor( QColor( 0, 0, 0, 0 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::Zebra ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setFrameFillColor1( Qt::black ); - mComposerMap->grid()->setFrameFillColor2( Qt::white ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameLeft, true ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameRight, true ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameTop, true ); - mComposerMap->grid()->setFrameSideFlag( QgsComposerMapGrid::FrameBottom, true ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_rotatedframe" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::LatitudeOnly, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::LongitudeOnly, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::LatitudeOnly, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::LongitudeOnly, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_framedivisions" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - testResult = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setFrameDivisions( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Bottom ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->setMapRotation( 0.0 ); -} - -void TestQgsComposerMapGrid::annotationFilter() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - //rotate map, so we mix latitude and longitude coordinates on every map side - mComposerMap->setMapRotation( 45.0 ); - - //setup defaults - mComposerMap->grid()->setAnnotationFontColor( QColor( 0, 0, 0, 0 ) ); - mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver ); - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_rotatedannotations" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::HideAll, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::LongitudeOnly, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::LatitudeOnly, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::LongitudeOnly, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_filteredannotations" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - testResult = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationEnabled( false ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationDisplay( QgsComposerMapGrid::ShowAll, QgsComposerMapGrid::Bottom ); - mComposerMap->setMapRotation( 0.0 ); -} - -void TestQgsComposerMapGrid::interiorTicks() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::InteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_interiorticks" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); -} - -void TestQgsComposerMapGrid::interiorTicksAnnotated() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::InteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_interiorticks_annotated" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_interiorticks_annotated2" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setAnnotationEnabled( false ); -} - -void TestQgsComposerMapGrid::exteriorTicks() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::ExteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_exteriorticks" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); -} - -void TestQgsComposerMapGrid::exteriorTicksAnnotated() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::ExteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_exteriorticks_annotated" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_exteriorticks_annotated2" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setAnnotationEnabled( false ); -} - -void TestQgsComposerMapGrid::interiorExteriorTicks() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::InteriorExteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_interiorexteriorticks" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); -} - -void TestQgsComposerMapGrid::interiorExteriorTicksAnnotated() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::InteriorExteriorTicks ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_interiorexteriorticks_annotated" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_interiorexteriorticks_annotated2" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setAnnotationEnabled( false ); -} - -void TestQgsComposerMapGrid::lineBorder() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::LineBorder ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_lineborder" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); -} - -void TestQgsComposerMapGrid::lineBorderAnnotated() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::LineBorder ); - mComposerMap->grid()->setFrameWidth( 10 ); - mComposerMap->grid()->setFramePenSize( 1 ); - mComposerMap->grid()->setFramePenColor( Qt::black ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_lineborder_annotated" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_lineborder_annotated2" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setAnnotationEnabled( false ); -} - -void TestQgsComposerMapGrid::annotationFormats() -{ - //create grids in geographic and projected coordinates - QgsCoordinateReferenceSystem projectedCrs; - projectedCrs.createFromSrid( 3994 ); - QgsCoordinateReferenceSystem geographicCrs; - geographicCrs.createFromSrid( 4326 ); - - QgsComposerMapGrid gridGeographic( QStringLiteral( "geographic grid" ), mComposerMap ); - gridGeographic.setCrs( geographicCrs ); - QgsComposerMapGrid gridProjected( QStringLiteral( "projected grid" ), mComposerMap ); - gridProjected.setCrs( projectedCrs ); - - //decimal degrees format - gridGeographic.setAnnotationFormat( QgsComposerMapGrid::DecimalWithSuffix ); - gridGeographic.setAnnotationPrecision( 1 ); - gridProjected.setAnnotationFormat( QgsComposerMapGrid::DecimalWithSuffix ); - gridProjected.setAnnotationPrecision( 1 ); - - QgsExpressionContext expressionContext = gridGeographic.createExpressionContext(); - - //normal e/w - QCOMPARE( gridGeographic.gridAnnotationString( 90, QgsComposerMapGrid::Longitude, expressionContext ), QString( "90.0" ) + QChar( 176 ) + QString( "E" ) ); - QCOMPARE( gridProjected.gridAnnotationString( 90, QgsComposerMapGrid::Longitude, expressionContext ), QString( "90.0E" ) ); - - //0 degrees - QCOMPARE( gridGeographic.gridAnnotationString( 0, QgsComposerMapGrid::Longitude, expressionContext ), QString( "0.0" ) + QChar( 176 ) ); - QCOMPARE( gridProjected.gridAnnotationString( 0, QgsComposerMapGrid::Longitude, expressionContext ), QString( "0.0E" ) ); - - //180 degrees - QCOMPARE( gridGeographic.gridAnnotationString( 180, QgsComposerMapGrid::Longitude, expressionContext ), QString( "180.0" ) + QChar( 176 ) ); - QCOMPARE( gridProjected.gridAnnotationString( 180, QgsComposerMapGrid::Longitude, expressionContext ), QString( "180.0E" ) ); - - //normal n/s - QCOMPARE( gridGeographic.gridAnnotationString( 45, QgsComposerMapGrid::Latitude, expressionContext ), QString( "45.0" ) + QChar( 176 ) + QString( "N" ) ); - QCOMPARE( gridProjected.gridAnnotationString( 45, QgsComposerMapGrid::Latitude, expressionContext ), QString( "45.0N" ) ); - - //0 north/south - QCOMPARE( gridGeographic.gridAnnotationString( 0, QgsComposerMapGrid::Latitude, expressionContext ), QString( "0.0" ) + QChar( 176 ) ); - QCOMPARE( gridProjected.gridAnnotationString( 0, QgsComposerMapGrid::Latitude, expressionContext ), QString( "0.0N" ) ); - - //Custom format annotations - gridProjected.setAnnotationFormat( QgsComposerMapGrid::CustomFormat ); - gridProjected.setAnnotationExpression( QStringLiteral( "(@grid_number/10) || case when @grid_axis ='x' then 'a' else 'b' end" ) ); - QCOMPARE( gridProjected.gridAnnotationString( 45, QgsComposerMapGrid::Latitude, expressionContext ), QString( "4.5b" ) ); - QCOMPARE( gridProjected.gridAnnotationString( 33, QgsComposerMapGrid::Longitude, expressionContext ), QString( "3.3a" ) ); -} - -void TestQgsComposerMapGrid::descendingAnnotations() -{ - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - - mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame ); - mComposerMap->grid()->setEnabled( true ); - mComposerMap->grid()->setStyle( QgsComposerMapGrid::FrameAnnotationsOnly ); - mComposerMap->grid()->setAnnotationEnabled( true ); - mComposerMap->grid()->setAnnotationFontColor( Qt::black ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::InsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::VerticalDescending, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::VerticalDescending, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::VerticalDescending, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationDirection( QgsComposerMapGrid::VerticalDescending, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker( QStringLiteral( "composermap_verticaldescending_inside" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult = checker.testComposition( mReport, 0, 0 ); - QVERIFY( testResult ); - - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Left ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Right ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Top ); - mComposerMap->grid()->setAnnotationPosition( QgsComposerMapGrid::OutsideMapFrame, QgsComposerMapGrid::Bottom ); - mComposerMap->updateBoundingRect(); - - QgsCompositionChecker checker2( QStringLiteral( "composermap_verticaldescending_outside" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_mapgrid" ) ); - bool testResult2 = checker2.testComposition( mReport, 0, 0 ); - QVERIFY( testResult2 ); - - mComposerMap->grid()->setAnnotationEnabled( false ); -} - -QGSTEST_MAIN( TestQgsComposerMapGrid ) -#include "testqgscomposermapgrid.moc" diff --git a/tests/src/core/testqgscomposermapoverview.cpp b/tests/src/core/testqgscomposermapoverview.cpp deleted file mode 100644 index 75e70153b8e..00000000000 --- a/tests/src/core/testqgscomposermapoverview.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/*************************************************************************** - testqgscomposermapoverview.cpp - ---------------------- - begin : September 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposermap.h" -#include "qgscomposermapoverview.h" -#include "qgsproject.h" -#include "qgsmultibandcolorrenderer.h" -#include "qgsrasterlayer.h" -#include "qgsrasterdataprovider.h" -#include "qgsfontutils.h" -#include -#include "qgstest.h" - -class TestQgsComposerMapOverview : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerMapOverview() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void overviewMap(); //test if overview map frame works - void overviewMapRotated(); //test if overview map frame works with rotated overview - void overviewMapRotated2(); //test if overview map frame works with rotated map - void overviewMapBlending(); //test if blend modes with overview map frame works - void overviewMapInvert(); //test if invert of overview map frame works - void overviewMapCenter(); //test if centering of overview map frame works - void overviewReprojected(); //test that overview frame is reprojected - - private: - QgsComposition *mComposition = nullptr; - QgsComposerMap *mComposerMap = nullptr; - QgsRasterLayer *mRasterLayer = nullptr; - QString mReport; -}; - -void TestQgsComposerMapOverview::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create maplayers from testdata and add to layer registry - QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/rgb256x256.png" ); - mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 1, 2, 3 ); - mRasterLayer->setRenderer( rasterRenderer ); - - //create composition with composer map - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - mComposerMap->setFrameEnabled( true ); - mComposerMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( mComposerMap ); - - mReport = QStringLiteral( "

Composer Map Overview Tests

\n" ); -} - -void TestQgsComposerMapOverview::cleanupTestCase() -{ - delete mComposition; - delete mRasterLayer; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposerMapOverview::init() -{ -} - -void TestQgsComposerMapOverview::cleanup() -{ - -} - -void TestQgsComposerMapOverview::overviewMap() -{ - QgsComposerMap *overviewMap = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMap->setFrameEnabled( true ); - overviewMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMap ); - mComposerMap->setNewExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in - overviewMap->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( mComposerMap->id() ); - QgsCompositionChecker checker( QStringLiteral( "composermap_overview" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposition->removeComposerItem( overviewMap ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewMapRotated() -{ - QgsComposerMap *overviewMap = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMap->setFrameEnabled( true ); - overviewMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMap ); - mComposerMap->setNewExtent( QgsRectangle( 96, -144, 160, -112 ) ); //zoom in - mComposerMap->setMapRotation( 30 ); - overviewMap->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( mComposerMap->id() ); - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_rotated" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 600 ); - mComposition->removeComposerItem( overviewMap ); - mComposerMap->setMapRotation( 0 ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewMapRotated2() -{ - QgsComposerMap *overviewMap = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMap->setFrameEnabled( true ); - overviewMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMap ); - mComposerMap->setNewExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in - overviewMap->setMapRotation( 30 ); - overviewMap->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( mComposerMap->id() ); - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_rotated2" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 600 ); - mComposition->removeComposerItem( overviewMap ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewMapBlending() -{ - QgsComposerMap *overviewMapBlend = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMapBlend->setFrameEnabled( true ); - overviewMapBlend->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMapBlend ); - mComposerMap->setNewExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in - overviewMapBlend->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapBlend->overview()->setFrameMap( mComposerMap->id() ); - overviewMapBlend->overview()->setBlendMode( QPainter::CompositionMode_Multiply ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_blending" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposition->removeComposerItem( overviewMapBlend ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewMapInvert() -{ - QgsComposerMap *overviewMapInvert = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMapInvert->setFrameEnabled( true ); - overviewMapInvert->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMapInvert ); - mComposerMap->setNewExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in - overviewMapInvert->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapInvert->overview()->setFrameMap( mComposerMap->id() ); - overviewMapInvert->overview()->setInverted( true ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_invert" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposition->removeComposerItem( overviewMapInvert ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewMapCenter() -{ - QgsComposerMap *overviewMapCenter = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMapCenter->setFrameEnabled( true ); - overviewMapCenter->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMapCenter ); - mComposerMap->setNewExtent( QgsRectangle( 192, -288, 320, -224 ) ); - overviewMapCenter->setNewExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapCenter->overview()->setFrameMap( mComposerMap->id() ); - overviewMapCenter->overview()->setCentered( true ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_center" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposition->removeComposerItem( overviewMapCenter ); - QVERIFY( testResult ); -} - -void TestQgsComposerMapOverview::overviewReprojected() -{ - QgsComposerMap *overviewMap = new QgsComposerMap( mComposition, 20, 130, 70, 70 ); - overviewMap->setFrameEnabled( true ); - //overviewMap->setLayers( QList() << mRasterLayer ); - mComposition->addComposerMap( overviewMap ); - - mComposerMap->setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); - overviewMap->setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 54030 ) ); - - mComposerMap->setNewExtent( QgsRectangle( 93, -64.245, 120.6, -45 ) ); - overviewMap->setNewExtent( QgsRectangle( 4712502, -7620278, 10872777, -2531356 ) ); - overviewMap->overview()->setFrameMap( mComposerMap->id() ); - - QgsCompositionChecker checker( QStringLiteral( "composermap_overview_reprojected" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); - - bool testResult = checker.testComposition( mReport, 0, 0 ); - mComposition->removeComposerItem( overviewMap ); - QVERIFY( testResult ); -} - -QGSTEST_MAIN( TestQgsComposerMapOverview ) -#include "testqgscomposermapoverview.moc" diff --git a/tests/src/core/testqgscomposermodel.cpp b/tests/src/core/testqgscomposermodel.cpp deleted file mode 100644 index 68e02724f33..00000000000 --- a/tests/src/core/testqgscomposermodel.cpp +++ /dev/null @@ -1,630 +0,0 @@ -/*************************************************************************** - testqgscomposermodel.cpp - ----------------------- - begin : July 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgscomposition.h" -#include "qgscomposermodel.h" -#include "qgscomposerlabel.h" -#include "qgsapplication.h" -#include "qgsmapsettings.h" -#include "qgsproject.h" - -#include -#include "qgstest.h" -#include - -class TestQgsComposerModel : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerModel() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void creation(); //check that model has been created - void addItems(); //add some items to the composition and test model - void zList(); //check model z order list - void clear(); //check clearing the model - void addItemDirectly(); //add an item directly to the model - void rebuildZList(); //test rebuilding the z list from the current composer stacking - void removeItem(); //test removing an item from the model - void reorderUp(); //test reordering an item up - void reorderDown(); //test reordering an item down - void reorderTop(); //test reordering an item to top - void reorderBottom(); //test reordering an item to bottom - void getComposerItemAbove(); //test getting composer item above - void getComposerItemBelow(); //test getting composer item below - void setItemRemoved(); //test setting an item as removed - void rebuildZListWithRemoved(); //test rebuilding z list with removed items - void reorderUpWithRemoved(); //test reordering up with removed items - void reorderDownWithRemoved(); //test reordering down with removed items - void reorderToTopWithRemoved(); //test reordering to top with removed items - void reorderToBottomWithRemoved(); //test reordering to bottom with removed items - - void proxyCrash(); - - private: - QgsComposition *mComposition = nullptr; - QgsComposerLabel *mItem1 = nullptr; - QgsComposerLabel *mItem2 = nullptr; - QgsComposerLabel *mItem3 = nullptr; -}; - -void TestQgsComposerModel::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - mComposition = new QgsComposition( QgsProject::instance() ); - - mComposition->setPaperSize( 297, 210 ); //A4 landscape -} - -void TestQgsComposerModel::cleanupTestCase() -{ - delete mItem1; - delete mItem2; - delete mItem3; - delete mComposition; - QgsApplication::exitQgis(); -} - -void TestQgsComposerModel::init() -{ - -} - -void TestQgsComposerModel::cleanup() -{ - -} - -void TestQgsComposerModel::creation() -{ - QVERIFY( mComposition->itemsModel() ); - //check some basic things - QCOMPARE( mComposition->itemsModel()->columnCount(), 3 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 0 ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 0 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 0 ); -} - -void TestQgsComposerModel::addItems() -{ - //add some items to the composition - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 0 ); - mItem1 = new QgsComposerLabel( mComposition ); - mComposition->addItem( mItem1 ); - mItem2 = new QgsComposerLabel( mComposition ); - mComposition->addItem( mItem2 ); - mItem3 = new QgsComposerLabel( mComposition ); - mComposition->addItem( mItem3 ); - //check that these items have been added to the model - QCOMPARE( mComposition->itemsModel()->rowCount(), 3 ); - //and the scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); -} - -void TestQgsComposerModel::zList() -{ - //check z list for items added by TestQgsComposerModel::addItems() - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem1 ); -} - -void TestQgsComposerModel::clear() -{ - mComposition->itemsModel()->clear(); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 0 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 0 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 0 ); -} - -void TestQgsComposerModel::addItemDirectly() -{ - //create some items not attached to the composition - QgsComposerLabel *bottomItem = new QgsComposerLabel( 0 ); - QgsComposerLabel *topItem = new QgsComposerLabel( 0 ); - - mComposition->itemsModel()->clear(); - - mComposition->itemsModel()->addItemAtTop( bottomItem ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 1 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 1 ); - - mComposition->itemsModel()->addItemAtTop( topItem ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 2 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 2 ); - - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), topItem ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), bottomItem ); - - //also check scene list (these items are treated by the model as belonging to the scene, - //as they will have isRemoved() as false - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), topItem ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), bottomItem ); - - mComposition->itemsModel()->clear(); - delete bottomItem; - delete topItem; -} - -void TestQgsComposerModel::rebuildZList() -{ - //start with an empty model - mComposition->itemsModel()->clear(); - - //some items are in composition, added by TestQgsComposerModel::addItems - mComposition->itemsModel()->rebuildZList(); - - //check that these items have been added to the model - QCOMPARE( mComposition->itemsModel()->rowCount(), 3 ); - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem1 ); -} - -void TestQgsComposerModel::removeItem() -{ - //start with an empty model - mComposition->itemsModel()->clear(); - - QgsComposerLabel *item1 = new QgsComposerLabel( 0 ); - QgsComposerLabel *item2 = new QgsComposerLabel( 0 ); - - //add one item to the model - mComposition->itemsModel()->addItemAtTop( item1 ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 1 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 1 ); - - //also check scene list (this item is treated by the model as belonging to the scene, - //as it has isRemoved() as false - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), item1 ); - - //try removing a missing item - mComposition->itemsModel()->removeItem( 0 ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 1 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 1 ); - - //try removing an item not in the model - mComposition->itemsModel()->removeItem( item2 ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 1 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 1 ); - - //remove the item which is in the model - mComposition->itemsModel()->removeItem( item1 ); - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 0 ); - QCOMPARE( mComposition->itemsModel()->rowCount(), 0 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 0 ); - - delete item1; - delete item2; -} - -void TestQgsComposerModel::reorderUp() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try reordering some bad items - QVERIFY( ! mComposition->itemsModel()->reorderItemUp( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->reorderItemUp( label ) ); - - //trying to reorder up the topmost item should fail - QVERIFY( ! mComposition->itemsModel()->reorderItemUp( mItem3 ) ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemUp( mItem2 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem1 ); - - delete label; -} - -void TestQgsComposerModel::reorderDown() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try reordering some bad items - QVERIFY( ! mComposition->itemsModel()->reorderItemDown( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->reorderItemDown( label ) ); - - //trying to reorder down the bottommost item should fail - QVERIFY( ! mComposition->itemsModel()->reorderItemDown( mItem1 ) ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemDown( mItem2 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem2 ); - delete label; -} - -void TestQgsComposerModel::reorderTop() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try reordering some bad items - QVERIFY( ! mComposition->itemsModel()->reorderItemToTop( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->reorderItemToTop( label ) ); - - //trying to reorder up the topmost item should fail - QVERIFY( ! mComposition->itemsModel()->reorderItemToTop( mItem3 ) ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemToTop( mItem1 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem2 ); - delete label; -} - -void TestQgsComposerModel::reorderBottom() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try reordering some bad items - QVERIFY( ! mComposition->itemsModel()->reorderItemToBottom( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->reorderItemToBottom( label ) ); - - //trying to reorder down the bottommost item should fail - QVERIFY( ! mComposition->itemsModel()->reorderItemToBottom( mItem1 ) ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemToBottom( mItem3 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem3 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 2 ), mItem3 ); - delete label; -} - -void TestQgsComposerModel::getComposerItemAbove() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try getting item above some bad items - QVERIFY( ! mComposition->itemsModel()->getComposerItemAbove( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->getComposerItemAbove( label ) ); - - //trying to get item above topmost item should fail - QVERIFY( ! mComposition->itemsModel()->getComposerItemAbove( mItem3 ) ); - - //try using a good item - QCOMPARE( mComposition->itemsModel()->getComposerItemAbove( mItem2 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->getComposerItemAbove( mItem1 ), mItem2 ); - - delete label; -} - -void TestQgsComposerModel::getComposerItemBelow() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //try getting item below some bad items - QVERIFY( ! mComposition->itemsModel()->getComposerItemBelow( 0 ) ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - QVERIFY( ! mComposition->itemsModel()->getComposerItemBelow( label ) ); - - //trying to get item below bottom most item should fail - QVERIFY( ! mComposition->itemsModel()->getComposerItemBelow( mItem1 ) ); - - //try using a good item - QCOMPARE( mComposition->itemsModel()->getComposerItemBelow( mItem3 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->getComposerItemBelow( mItem2 ), mItem1 ); - - delete label; -} - -void TestQgsComposerModel::setItemRemoved() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 3 ); - - //try marking some bad items as removed - mComposition->itemsModel()->setItemRemoved( 0 ); - QgsComposerLabel *label = new QgsComposerLabel( 0 ); - mComposition->itemsModel()->setItemRemoved( label ); - QVERIFY( !label->isRemoved() ); - - //try using a good item - mComposition->itemsModel()->setItemRemoved( mItem3 ); - QVERIFY( mItem3->isRemoved() ); - - //item should still be in z-list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //but not in scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - delete label; -} - -void TestQgsComposerModel::rebuildZListWithRemoved() -{ - QVERIFY( mItem3->isRemoved() ); - - //start with an empty model - mComposition->itemsModel()->clear(); - - //rebuild z list - mComposition->itemsModel()->rebuildZList(); - - //check that only items in the scene are shown by the model - QCOMPARE( mComposition->itemsModel()->rowCount(), 2 ); - - //check z list contains ALL items - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - //check that scene list is missing removed item - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); -} - -void TestQgsComposerModel::reorderUpWithRemoved() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemUp( mItem1 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); -} - -void TestQgsComposerModel::reorderDownWithRemoved() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemDown( mItem2 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); -} - -void TestQgsComposerModel::reorderToTopWithRemoved() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemToTop( mItem1 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); -} - -void TestQgsComposerModel::reorderToBottomWithRemoved() -{ - mComposition->itemsModel()->clear(); - mComposition->itemsModel()->rebuildZList(); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem1 ); - - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem1 ); - - //try reorder a good item, should succeed - QVERIFY( mComposition->itemsModel()->reorderItemToBottom( mItem2 ) ); - - //check z list - QCOMPARE( mComposition->itemsModel()->zOrderListSize(), 3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 0 ), mItem3 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 1 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->zOrderList()->at( 2 ), mItem2 ); - //also check scene list - QCOMPARE( mComposition->itemsModel()->mItemsInScene.size(), 2 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 0 ), mItem1 ); - QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 ); -} - -void TestQgsComposerModel::proxyCrash() -{ - // test for a possible crash when using QgsComposerProxyModel and reordering items - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - // create a proxy - it's not used, but will be watching... - QgsComposerProxyModel *proxy = new QgsComposerProxyModel( composition ); - Q_UNUSED( proxy ); - - // add some items to composition - QgsComposerLabel *item1 = new QgsComposerLabel( composition ); - composition->addItem( item1 ); - QgsComposerLabel *item2 = new QgsComposerLabel( composition ); - composition->addItem( item2 ); - QgsComposerLabel *item3 = new QgsComposerLabel( composition ); - composition->addItem( item3 ); - - // reorder items - expect no crash! - composition->itemsModel()->reorderItemUp( item1 ); -} - -QGSTEST_MAIN( TestQgsComposerModel ) -#include "testqgscomposermodel.moc" diff --git a/tests/src/core/testqgscomposermultiframe.cpp b/tests/src/core/testqgscomposermultiframe.cpp deleted file mode 100644 index 2a6e762b83c..00000000000 --- a/tests/src/core/testqgscomposermultiframe.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/*************************************************************************** - testqgscomposermultiframe.cpp - ----------------------- - begin : September 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgscomposerhtml.h" -#include "qgscomposerframe.h" -#include "qgscomposerlabel.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgsapplication.h" -#include "qgsproject.h" - -#include -#include "qgstest.h" - -class TestQgsComposerMultiFrame : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerMultiFrame(); - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void addFrame(); //test creating new frame inherits all properties of existing frame - void frameIsEmpty(); //test if frame is empty works - void addRemovePage(); //test if page is added and removed for RepeatUntilFinished mode - void undoRedo(); //test that combinations of frame/multiframe undo/redo don't crash - void undoRedoRemovedFrame(); //test that undo doesn't crash with removed frames - - private: - QgsComposition *mComposition = nullptr; - QString mReport; -}; - -TestQgsComposerMultiFrame::TestQgsComposerMultiFrame() = default; - -void TestQgsComposerMultiFrame::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mReport = QStringLiteral( "

Composer MultiFrame Tests

\n" ); -} - -void TestQgsComposerMultiFrame::cleanupTestCase() -{ - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerMultiFrame::init() -{ - -} - -void TestQgsComposerMultiFrame::cleanup() -{ - -} - -void TestQgsComposerMultiFrame::addFrame() -{ - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - htmlItem->addFrame( frame1 ); - - //should not be inherited - frame1->setHidePageIfEmpty( true ); - - //should be inherited - frame1->setHideBackgroundIfEmpty( true ); - frame1->setFrameStrokeWidth( 5.0 ); - frame1->setFrameJoinStyle( Qt::RoundJoin ); - frame1->setFrameEnabled( true ); - frame1->setFrameStrokeColor( QColor( Qt::red ) ); - frame1->setBackgroundEnabled( true ); - frame1->setBackgroundColor( QColor( Qt::green ) ); - frame1->setBlendMode( QPainter::CompositionMode_ColorBurn ); - frame1->setItemOpacity( 0.5 ); - - QgsComposerFrame *frame2 = htmlItem->createNewFrame( frame1, QPointF( 50, 55 ), QSizeF( 70, 120 ) ); - - //check frame created in correct place - QCOMPARE( frame2->rect().height(), 120.0 ); - QCOMPARE( frame2->rect().width(), 70.0 ); - QCOMPARE( frame2->scenePos().x(), 50.0 ); - QCOMPARE( frame2->scenePos().y(), 55.0 ); - - //check frame properties - QCOMPARE( frame2->frameStrokeWidth(), frame1->frameStrokeWidth() ); - QCOMPARE( frame2->frameStrokeColor(), frame1->frameStrokeColor() ); - QCOMPARE( frame2->frameJoinStyle(), frame1->frameJoinStyle() ); - QCOMPARE( frame2->hasBackground(), frame1->hasBackground() ); - QCOMPARE( frame2->backgroundColor(), frame1->backgroundColor() ); - QCOMPARE( frame2->blendMode(), frame1->blendMode() ); - QCOMPARE( frame2->itemOpacity(), frame1->itemOpacity() ); - - //check non-inherited properties - QVERIFY( !frame2->hidePageIfEmpty() ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - -void TestQgsComposerMultiFrame::frameIsEmpty() -{ - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - QgsComposerFrame *frame2 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - htmlItem->addFrame( frame1 ); - htmlItem->addFrame( frame2 ); - htmlItem->setContentMode( QgsComposerHtml::ManualHtml ); - //short content, so frame 2 should be empty - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( frame1->isEmpty(), false ); - QCOMPARE( frame2->isEmpty(), true ); - - //long content, so frame 2 should not be empty - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( frame1->isEmpty(), false ); - QCOMPARE( frame2->isEmpty(), false ); - - //..and back again.. - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( frame1->isEmpty(), false ); - QCOMPARE( frame2->isEmpty(), true ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - -void TestQgsComposerMultiFrame::addRemovePage() -{ - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - htmlItem->addFrame( frame1 ); - htmlItem->setContentMode( QgsComposerHtml::ManualHtml ); - htmlItem->setResizeMode( QgsComposerMultiFrame::RepeatUntilFinished ); - - //short content, so should fit in one frame - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - //should be one page - QCOMPARE( htmlItem->frameCount(), 1 ); - QCOMPARE( mComposition->numPages(), 1 ); - - //long content, so we require 3 frames - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( htmlItem->frameCount(), 3 ); - QCOMPARE( mComposition->numPages(), 3 ); - - //..and back again.. - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( htmlItem->frameCount(), 1 ); - QCOMPARE( mComposition->numPages(), 1 ); - - - //get a bit more complicated - add another item to page 3 - QgsComposerLabel *label1 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label1 ); - label1->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 3 ); - - //long content, so we require 4 pages - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( htmlItem->frameCount(), 4 ); - QCOMPARE( mComposition->numPages(), 4 ); - - //..and back again. Since there's an item on page 3, only page 4 should be removed - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( htmlItem->frameCount(), 1 ); - QCOMPARE( mComposition->numPages(), 3 ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - -void TestQgsComposerMultiFrame::undoRedo() -{ - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - htmlItem->addFrame( frame1 ); - htmlItem->setContentMode( QgsComposerHtml::ManualHtml ); - htmlItem->setResizeMode( QgsComposerMultiFrame::RepeatUntilFinished ); - - //short content, so should fit in one frame - htmlItem->setHtml( QStringLiteral( "

Test content

" ) ); - htmlItem->loadHtml(); - - //do some combinations of undo/redo commands for both the frame and multiframe - //to try to trigger a crash - frame1->beginCommand( QStringLiteral( "move" ) ); - frame1->setSceneRect( QRectF( 10, 10, 20, 20 ) ); - frame1->endCommand(); - frame1->beginCommand( QStringLiteral( "stroke" ), QgsComposerMergeCommand::ItemStrokeWidth ); - frame1->setFrameStrokeWidth( 4.0 ); - frame1->endCommand(); - frame1->beginCommand( QStringLiteral( "stroke" ), QgsComposerMergeCommand::ItemStrokeWidth ); - frame1->setFrameStrokeWidth( 7.0 ); - frame1->endCommand(); - - //multiframe commands - mComposition->beginMultiFrameCommand( htmlItem, QStringLiteral( "maxbreak" ) ); - htmlItem->setMaxBreakDistance( 100 ); - mComposition->endMultiFrameCommand(); - - //another frame command - frame1->beginCommand( QStringLiteral( "bgcolor" ), QgsComposerMergeCommand::ItemOpacity ); - frame1->setBackgroundColor( QColor( 255, 255, 0 ) ); - frame1->endCommand(); - frame1->beginCommand( QStringLiteral( "bgcolor" ), QgsComposerMergeCommand::ItemOpacity ); - frame1->setBackgroundColor( QColor( 255, 0, 0 ) ); - frame1->endCommand(); - - //undo changes - - //frame bg - mComposition->undoStack()->undo(); - //multiframe max break - mComposition->undoStack()->undo(); - //frame stroke width - mComposition->undoStack()->undo(); - //frame move - mComposition->undoStack()->undo(); - - //check result - QCOMPARE( htmlItem->maxBreakDistance(), 10.0 ); - QCOMPARE( htmlItem->frame( 0 )->frameStrokeWidth(), 0.3 ); - QCOMPARE( htmlItem->frame( 0 )->pos(), QPointF( 0, 0 ) ); - QCOMPARE( htmlItem->frame( 0 )->backgroundColor(), QColor( 255, 255, 255 ) ); - - //now redo - - //frame move - mComposition->undoStack()->redo(); - //frame stroke width - mComposition->undoStack()->redo(); - //multiframe max break - mComposition->undoStack()->redo(); - //frame bg color - mComposition->undoStack()->redo(); - - //check result - QCOMPARE( htmlItem->maxBreakDistance(), 100.0 ); - QCOMPARE( htmlItem->frame( 0 )->frameStrokeWidth(), 7.0 ); - QCOMPARE( htmlItem->frame( 0 )->pos(), QPointF( 10, 10 ) ); - QCOMPARE( htmlItem->frame( 0 )->backgroundColor(), QColor( 255, 0, 0 ) ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - - -void TestQgsComposerMultiFrame::undoRedoRemovedFrame() -{ - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 200 ); - htmlItem->addFrame( frame1 ); - htmlItem->setContentMode( QgsComposerHtml::ManualHtml ); - htmlItem->setResizeMode( QgsComposerMultiFrame::RepeatUntilFinished ); - - //long content, so should require multiple frames - htmlItem->setHtml( QStringLiteral( "

Test content

" ) ); - htmlItem->loadHtml(); - - QVERIFY( htmlItem->frameCount() > 1 ); - - //do a command on the first frame - htmlItem->frame( 0 )->beginCommand( QStringLiteral( "stroke" ), QgsComposerMergeCommand::ItemStrokeWidth ); - htmlItem->frame( 0 )->setFrameStrokeWidth( 4.0 ); - htmlItem->frame( 0 )->endCommand(); - //do a command on the second frame - htmlItem->frame( 1 )->beginCommand( QStringLiteral( "stroke" ), QgsComposerMergeCommand::ItemStrokeWidth ); - htmlItem->frame( 1 )->setFrameStrokeWidth( 8.0 ); - htmlItem->frame( 1 )->endCommand(); - - //do a multiframe command which removes extra frames - mComposition->beginMultiFrameCommand( htmlItem, QStringLiteral( "source" ) ); - htmlItem->setHtml( QStringLiteral( "

Test content

" ) ); - mComposition->endMultiFrameCommand(); - - //wipes the second frame - htmlItem->loadHtml(); - - QCOMPARE( htmlItem->frameCount(), 1 ); - - //undo changes - - //multiframe command - mComposition->undoStack()->undo(); - //frame 2 command - mComposition->undoStack()->undo(); - //frame 1 command - mComposition->undoStack()->undo(); - - //check result - QVERIFY( htmlItem->frameCount() > 1 ); - QCOMPARE( htmlItem->frame( 0 )->frameStrokeWidth(), 0.3 ); - QCOMPARE( htmlItem->frame( 1 )->frameStrokeWidth(), 0.3 ); - - //now redo - - //frame 1 command - mComposition->undoStack()->redo(); - //frame 2 command - mComposition->undoStack()->redo(); - - //check result - QVERIFY( htmlItem->frameCount() > 1 ); - QCOMPARE( htmlItem->frame( 0 )->frameStrokeWidth(), 4.0 ); - QCOMPARE( htmlItem->frame( 1 )->frameStrokeWidth(), 8.0 ); - - //multiframe command - mComposition->undoStack()->redo(); - QCOMPARE( htmlItem->frameCount(), 1 ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - -QGSTEST_MAIN( TestQgsComposerMultiFrame ) -#include "testqgscomposermultiframe.moc" diff --git a/tests/src/core/testqgscomposerobject.cpp b/tests/src/core/testqgscomposerobject.cpp deleted file mode 100644 index f5f32f7cf3c..00000000000 --- a/tests/src/core/testqgscomposerobject.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/*************************************************************************** - testqgscomposerobject.cpp - ----------------------- - begin : July 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgscomposerobject.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgsproperty.h" -#include "qgsexpression.h" -#include "qgsapplication.h" -#include "qgsproject.h" - -#include -#include "qgstest.h" - -class TestQgsComposerObject : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerObject() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void creation(); //test creation of QgsComposerObject - void composition(); //test fetching composition from QgsComposerObject - void writeReadXml(); //test writing object to xml and reading back from it - void writeRetrieveDDProperty(); //test writing and retrieving dd properties from xml - void customProperties(); //test setting/getting custom properties - void writeRetrieveCustomProperties(); //test writing/retreiving custom properties from xml - - private: - bool renderCheck( const QString &testName, QImage &image, int mismatchCount = 0 ); - QgsComposition *mComposition = nullptr; - QString mReport; - -}; - -void TestQgsComposerObject::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mReport = QStringLiteral( "

Composer Object Tests

\n" ); -} - -void TestQgsComposerObject::cleanupTestCase() -{ - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerObject::init() -{ -} - -void TestQgsComposerObject::cleanup() -{ -} - -void TestQgsComposerObject::creation() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - QVERIFY( object ); - delete object; -} - -void TestQgsComposerObject::composition() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - QCOMPARE( object->composition(), mComposition ); - delete object; -} - -void TestQgsComposerObject::writeReadXml() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - - //test writing with no node - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QDomElement noNode; - QCOMPARE( object->writeXml( noNode, doc ), false ); - - //test writing with node - QDomElement composerObjectElem = doc.createElement( QStringLiteral( "ComposerObject" ) ); - rootNode.appendChild( composerObjectElem ); - QVERIFY( object->writeXml( composerObjectElem, doc ) ); - - //check if object node was written - QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "ComposerObject" ) ); - QCOMPARE( evalNodeList.count(), 1 ); - - //test reading node - QgsComposerObject *readObject = new QgsComposerObject( mComposition ); - - //test reading with no node - QCOMPARE( readObject->readXml( noNode, doc ), false ); - - //test reading node - QVERIFY( readObject->readXml( composerObjectElem, doc ) ); - - delete object; - delete readObject; -} - -void TestQgsComposerObject::writeRetrieveDDProperty() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - object->dataDefinedProperties().setProperty( QgsComposerObject::TestProperty, QgsProperty::fromExpression( QStringLiteral( "10 + 40" ) ) ); - object->prepareProperties(); - - //test writing object with dd settings - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QDomElement composerObjectElem = doc.createElement( QStringLiteral( "ComposerObject" ) ); - rootNode.appendChild( composerObjectElem ); - QVERIFY( object->writeXml( composerObjectElem, doc ) ); - - //check if object node was written - QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "ComposerObject" ) ); - QCOMPARE( evalNodeList.count(), 1 ); - - //test reading node containing dd settings - QgsComposerObject *readObject = new QgsComposerObject( mComposition ); - QVERIFY( readObject->readXml( composerObjectElem, doc ) ); - - //test getting not set dd from restored object - QgsProperty dd = readObject->dataDefinedProperties().property( QgsComposerObject::BlendMode ); - QVERIFY( !dd ); - - //test getting good property - dd = readObject->dataDefinedProperties().property( QgsComposerObject::TestProperty ); - QVERIFY( dd ); - QVERIFY( dd.isActive() ); - QCOMPARE( dd.propertyType(), QgsProperty::ExpressionBasedProperty ); - - delete object; - delete readObject; -} - -void TestQgsComposerObject::customProperties() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - - QCOMPARE( object->customProperty( "noprop", "defaultval" ).toString(), QString( "defaultval" ) ); - QVERIFY( object->customProperties().isEmpty() ); - object->setCustomProperty( QStringLiteral( "testprop" ), "testval" ); - QCOMPARE( object->customProperty( "testprop", "defaultval" ).toString(), QString( "testval" ) ); - QCOMPARE( object->customProperties().length(), 1 ); - QCOMPARE( object->customProperties().at( 0 ), QString( "testprop" ) ); - - //test no crash - object->removeCustomProperty( QStringLiteral( "badprop" ) ); - - object->removeCustomProperty( QStringLiteral( "testprop" ) ); - QVERIFY( object->customProperties().isEmpty() ); - QCOMPARE( object->customProperty( "noprop", "defaultval" ).toString(), QString( "defaultval" ) ); - - object->setCustomProperty( QStringLiteral( "testprop1" ), "testval1" ); - object->setCustomProperty( QStringLiteral( "testprop2" ), "testval2" ); - QStringList keys = object->customProperties(); - QCOMPARE( keys.length(), 2 ); - QVERIFY( keys.contains( "testprop1" ) ); - QVERIFY( keys.contains( "testprop2" ) ); - - delete object; -} - -void TestQgsComposerObject::writeRetrieveCustomProperties() -{ - QgsComposerObject *object = new QgsComposerObject( mComposition ); - object->setCustomProperty( QStringLiteral( "testprop" ), "testval" ); - object->setCustomProperty( QStringLiteral( "testprop2" ), 5 ); - - //test writing object with custom properties - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QDomElement composerObjectElem = doc.createElement( QStringLiteral( "ComposerObject" ) ); - rootNode.appendChild( composerObjectElem ); - QVERIFY( object->writeXml( composerObjectElem, doc ) ); - - //check if object node was written - QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "ComposerObject" ) ); - QCOMPARE( evalNodeList.count(), 1 ); - - //test reading node containing custom properties - QgsComposerObject *readObject = new QgsComposerObject( mComposition ); - QVERIFY( readObject->readXml( composerObjectElem, doc ) ); - - //test retrieved custom properties - QCOMPARE( readObject->customProperties().length(), 2 ); - QVERIFY( readObject->customProperties().contains( QString( "testprop" ) ) ); - QVERIFY( readObject->customProperties().contains( QString( "testprop2" ) ) ); - QCOMPARE( readObject->customProperty( "testprop" ).toString(), QString( "testval" ) ); - QCOMPARE( readObject->customProperty( "testprop2" ).toInt(), 5 ); - - delete object; - delete readObject; -} - -bool TestQgsComposerObject::renderCheck( const QString &testName, QImage &image, int mismatchCount ) -{ - mReport += "

" + testName + "

\n"; - QString myTmpDir = QDir::tempPath() + '/'; - QString myFileName = myTmpDir + testName + ".png"; - image.save( myFileName, "PNG" ); - QgsRenderChecker myChecker; - myChecker.setControlName( "expected_" + testName ); - myChecker.setRenderedImage( myFileName ); - bool myResultFlag = myChecker.compareImages( testName, mismatchCount ); - mReport += myChecker.report(); - return myResultFlag; -} - -QGSTEST_MAIN( TestQgsComposerObject ) -#include "testqgscomposerobject.moc" diff --git a/tests/src/core/testqgscomposerpaper.cpp b/tests/src/core/testqgscomposerpaper.cpp deleted file mode 100644 index 1f0cb11170e..00000000000 --- a/tests/src/core/testqgscomposerpaper.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/*************************************************************************** - testqgscomposerpaper.cpp - ---------------------- - begin : January 2014 - copyright : (C) 2014 by Marco Hugentobler, Nyall Dawson - email : nyall dot dawson at gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposershape.h" -#include "qgsproject.h" -#include "qgssymbol.h" -#include "qgssinglesymbolrenderer.h" -#include "qgsfillsymbollayer.h" -#include "qgslinesymbollayer.h" - -#include -#include "qgstest.h" -#include -#include - -class TestQgsComposerPaper : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerPaper() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void defaultPaper(); //test default paper style - void transparentPaper(); //test totally transparent paper style - void borderedPaper(); //test page with border - void markerLinePaper(); //test page with marker line border - void hiddenPages(); //test hidden page boundaries - - private: - QgsComposition *mComposition = nullptr; - QString mReport; - // QgsSingleSymbolRenderer* mSymbolRenderer; - -}; - -void TestQgsComposerPaper::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create empty composition - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mReport = QStringLiteral( "

Composer Paper Tests

\n" ); -} - -void TestQgsComposerPaper::cleanupTestCase() -{ - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposerPaper::init() -{ - -} - -void TestQgsComposerPaper::cleanup() -{ - -} - -void TestQgsComposerPaper::defaultPaper() -{ - QgsCompositionChecker checker( QStringLiteral( "composerpaper_default" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerPaper::transparentPaper() -{ - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::transparent ); - simpleFill->setStrokeColor( Qt::transparent ); - mComposition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - QgsCompositionChecker checker( QStringLiteral( "composerpaper_transparent" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerPaper::borderedPaper() -{ - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::white ); - simpleFill->setStrokeColor( Qt::black ); - simpleFill->setStrokeWidth( 6 ); - mComposition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - QgsCompositionChecker checker( QStringLiteral( "composerpaper_bordered" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerPaper::markerLinePaper() -{ - QgsMarkerLineSymbolLayer *markerLine = new QgsMarkerLineSymbolLayer(); - QgsFillSymbol *markerLineSymbol = new QgsFillSymbol(); - markerLineSymbol->changeSymbolLayer( 0, markerLine ); - mComposition->setPageStyleSymbol( markerLineSymbol ); - delete markerLineSymbol; - - QgsCompositionChecker checker( QStringLiteral( "composerpaper_markerborder" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerPaper::hiddenPages() -{ - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::blue ); - simpleFill->setStrokeColor( Qt::transparent ); - mComposition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - mComposition->setPagesVisible( false ); - QgsCompositionChecker checker( QStringLiteral( "composerpaper_hidden" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); - bool result = checker.testComposition( mReport ); - mComposition->setPagesVisible( true ); - QVERIFY( result ); -} - -QGSTEST_MAIN( TestQgsComposerPaper ) -#include "testqgscomposerpaper.moc" diff --git a/tests/src/core/testqgscomposerpicture.cpp b/tests/src/core/testqgscomposerpicture.cpp deleted file mode 100644 index 7be2c1a5027..00000000000 --- a/tests/src/core/testqgscomposerpicture.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/*************************************************************************** - testqgscomposerpicture.cpp - ---------------------- - begin : April 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposerpicture.h" -#include "qgsproject.h" -#include "qgsproperty.h" -#include -#include "qgstest.h" -#include -#include - -class TestQgsComposerPicture : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerPicture(); - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - - void pictureRotation(); //test if picture pictureRotation is functioning - void pictureItemRotation(); //test if composer picture item rotation is functioning - - void pictureResizeZoom(); - void pictureResizeStretch(); - void pictureResizeClip(); - void pictureResizeZoomAndResize(); - void pictureResizeFrameToImage(); - - void pictureClipAnchor(); - void pictureClipAnchorOversize(); - void pictureZoomAnchor(); - - void pictureSvgZoom(); - void pictureSvgStretch(); - void pictureSvgZoomAndResize(); - void pictureSvgFrameToImage(); - - void svgParameters(); - void issue_14644(); - - void pictureExpression(); - void pictureInvalidExpression(); - - - private: - QgsComposition *mComposition = nullptr; - QgsComposerPicture *mComposerPicture = nullptr; - QString mReport; - QString mPngImage; - QString mSvgImage; - QString mSvgParamsImage; -}; - -TestQgsComposerPicture::TestQgsComposerPicture() = default; - -void TestQgsComposerPicture::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - mPngImage = QStringLiteral( TEST_DATA_DIR ) + "/sample_image.png"; - mSvgImage = QStringLiteral( TEST_DATA_DIR ) + "/sample_svg.svg"; - mSvgParamsImage = QStringLiteral( TEST_DATA_DIR ) + "/svg_params.svg"; - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mComposerPicture = new QgsComposerPicture( mComposition ); - mComposerPicture->setPicturePath( mPngImage ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); - mComposerPicture->setFrameEnabled( true ); - - mReport = QStringLiteral( "

Composer Picture Tests

\n" ); -} - -void TestQgsComposerPicture::cleanupTestCase() -{ - delete mComposerPicture; - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposerPicture::init() -{ - -} - -void TestQgsComposerPicture::cleanup() -{ - -} - -void TestQgsComposerPicture::pictureRotation() -{ - //test picture rotation - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setPictureRotation( 45 ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_rotation" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setPictureRotation( 0 ); -} - -void TestQgsComposerPicture::pictureItemRotation() -{ - //test picture item rotation - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setItemRotation( 45, true ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_itemrotation" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setItemRotation( 0, true ); -} - -void TestQgsComposerPicture::pictureResizeZoom() -{ - //test picture resize Zoom mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_resize_zoom" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); -} - -void TestQgsComposerPicture::pictureResizeStretch() -{ - //test picture resize Stretch mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Stretch ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_resize_stretch" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); -} - -void TestQgsComposerPicture::pictureResizeClip() -{ - //test picture resize Clip mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Clip ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 30, 50 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_resize_clip" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureResizeZoomAndResize() -{ - //test picture resize ZoomResizeFrame mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::ZoomResizeFrame ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 50, 300 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_resize_zoomresize" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureResizeFrameToImage() -{ - //test picture resize FrameToImageSize mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::FrameToImageSize ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 50, 300 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_resize_frametoimage" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureClipAnchor() -{ - //test picture anchor in Clip mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Clip ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 30, 50 ) ); - mComposerPicture->setPictureAnchor( QgsComposerItem::LowerRight ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_clip_anchor" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPictureAnchor( QgsComposerItem::UpperLeft ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureClipAnchorOversize() -{ - //test picture anchor in Clip mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Clip ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 150, 120 ) ); - mComposerPicture->setPictureAnchor( QgsComposerItem::LowerMiddle ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_clip_anchoroversize" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPictureAnchor( QgsComposerItem::UpperLeft ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureZoomAnchor() -{ - //test picture anchor in Zoom mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 10, 30, 100 ) ); - mComposerPicture->setPictureAnchor( QgsComposerItem::LowerMiddle ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_zoom_anchor" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setPictureAnchor( QgsComposerItem::UpperLeft ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureSvgZoom() -{ - //test picture resize Zoom mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPicturePath( mSvgImage ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_svg_zoom" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setPicturePath( mPngImage ); -} - -void TestQgsComposerPicture::pictureSvgStretch() -{ - //test picture resize Stretch mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Stretch ); - mComposerPicture->setPicturePath( mSvgImage ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 20, 100 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_svg_stretch" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPicturePath( mPngImage ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); -} - -void TestQgsComposerPicture::pictureSvgZoomAndResize() -{ - //test picture resize ZoomResizeFrame mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::ZoomResizeFrame ); - mComposerPicture->setPicturePath( mSvgImage ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 50, 300 ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_svg_zoomresize" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); - mComposerPicture->setPicturePath( mPngImage ); -} - -void TestQgsComposerPicture::pictureSvgFrameToImage() -{ - //test picture resize FrameToImageSize mode - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::FrameToImageSize ); - mComposerPicture->setPicturePath( mSvgImage ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_svg_frametoimage" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); - mComposerPicture->setPicturePath( mPngImage ); -} - -void TestQgsComposerPicture::svgParameters() -{ - //test rendering an SVG file with parameters - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPicturePath( mSvgParamsImage ); - mComposerPicture->setSvgFillColor( QColor( 30, 90, 200, 100 ) ); - mComposerPicture->setSvgStrokeColor( QColor( 255, 45, 20, 200 ) ); - mComposerPicture->setSvgStrokeWidth( 2.2 ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_svg_params" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); - mComposerPicture->setPicturePath( mPngImage ); -} - -void TestQgsComposerPicture::issue_14644() -{ - //test rendering SVG file with text - mComposition->addComposerPicture( mComposerPicture ); - mComposerPicture->setResizeMode( QgsComposerPicture::Zoom ); - mComposerPicture->setPicturePath( QStringLiteral( TEST_DATA_DIR ) + "/svg/issue_14644.svg" ); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_issue_14644" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) ); - mComposerPicture->setPicturePath( mPngImage ); -} - -void TestQgsComposerPicture::pictureExpression() -{ - //test picture source via expression - mComposition->addComposerPicture( mComposerPicture ); - - QString expr = QStringLiteral( "'%1' || '/sample_svg.svg'" ).arg( TEST_DATA_DIR ); - mComposerPicture->dataDefinedProperties().setProperty( QgsComposerObject::PictureSource, QgsProperty::fromExpression( expr ) ); - mComposerPicture->refreshPicture(); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_expression" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->dataDefinedProperties().setProperty( QgsComposerObject::PictureSource, QgsProperty() ); -} - -void TestQgsComposerPicture::pictureInvalidExpression() -{ - //test picture source via bad expression - mComposition->addComposerPicture( mComposerPicture ); - - QString expr = QStringLiteral( "bad expression" ); - mComposerPicture->dataDefinedProperties().setProperty( QgsComposerObject::PictureSource, QgsProperty::fromExpression( expr ) ); - mComposerPicture->refreshPicture(); - - QgsCompositionChecker checker( QStringLiteral( "composerpicture_badexpression" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_picture" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); - - mComposition->removeItem( mComposerPicture ); - mComposerPicture->dataDefinedProperties().setProperty( QgsComposerObject::PictureSource, QgsProperty() ); -} - -QGSTEST_MAIN( TestQgsComposerPicture ) -#include "testqgscomposerpicture.moc" diff --git a/tests/src/core/testqgscomposerrotation.cpp b/tests/src/core/testqgscomposerrotation.cpp deleted file mode 100644 index 4ecd66b4b75..00000000000 --- a/tests/src/core/testqgscomposerrotation.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/*************************************************************************** - testqgscomposerrotation.cpp - ---------------------- - begin : April 2013 - copyright : (C) 2013 by Marco Hugentobler, Nyall Dawson - email : nyall dot dawson at gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposershape.h" -#include "qgscomposermap.h" -#include "qgscomposerlabel.h" -#include "qgsmultibandcolorrenderer.h" -#include "qgsrasterlayer.h" -#include "qgsproject.h" -#include "qgsfontutils.h" -#include "qgsrasterdataprovider.h" -#include -#include "qgstest.h" -#include -#include - -class TestQgsComposerRotation : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerRotation() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - - void shapeRotation(); //test if composer shape rotation is functioning - void labelRotation(); //test if composer label rotation is functioning - void mapRotation(); //test if composer map mapRotation is functioning - void mapItemRotation(); //test if composer map item rotation is functioning - - private: - QgsComposition *mComposition = nullptr; - QgsComposerShape *mComposerRect = nullptr; - QgsComposerLabel *mComposerLabel = nullptr; - QgsComposerMap *mComposerMap = nullptr; - QgsRasterLayer *mRasterLayer = nullptr; - QString mReport; -}; - -void TestQgsComposerRotation::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create maplayers from testdata and add to layer registry - QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/rgb256x256.png" ); - mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 1, 2, 3 ); - mRasterLayer->setRenderer( rasterRenderer ); - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mComposerRect = new QgsComposerShape( 70, 70, 150, 100, mComposition ); - mComposerRect->setShapeType( QgsComposerShape::Rectangle ); - mComposerRect->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) ); - - mComposerLabel = new QgsComposerLabel( mComposition ); - mComposerLabel->setText( QStringLiteral( "test label" ) ); - mComposerLabel->setFont( QgsFontUtils::getStandardTestFont() ); - mComposerLabel->setPos( 70, 70 ); - mComposerLabel->adjustSizeToText(); - mComposerLabel->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) ); - mComposerLabel->setBackgroundEnabled( true ); - - mReport = QStringLiteral( "

Composer Rotation Tests

\n" ); -} - -void TestQgsComposerRotation::cleanupTestCase() -{ - if ( mComposerMap ) - { - mComposition->removeItem( mComposerMap ); - delete mComposerMap; - } - - delete mComposerLabel; - delete mComposerRect; - delete mComposition; - delete mRasterLayer; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerRotation::init() -{ - -} - -void TestQgsComposerRotation::cleanup() -{ - -} - -void TestQgsComposerRotation::shapeRotation() -{ - mComposition->addComposerShape( mComposerRect ); - - mComposerRect->setItemRotation( 45, true ); - - QgsCompositionChecker checker( QStringLiteral( "composerrotation_shape" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); - QVERIFY( checker.testComposition( mReport ) ); - - mComposition->removeItem( mComposerRect ); - mComposerRect->setItemRotation( 0, true ); -} - -void TestQgsComposerRotation::labelRotation() -{ - mComposition->addComposerLabel( mComposerLabel ); - mComposerLabel->setItemRotation( 135, true ); - - QgsCompositionChecker checker( QStringLiteral( "composerrotation_label" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerRotation::mapRotation() -{ - // cleanup after labelRotation() - mComposition->removeItem( mComposerLabel ); - mComposerLabel->setItemRotation( 0, true ); - - //test map rotation - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 100, 50 ); - mComposerMap->setFrameEnabled( true ); - mComposition->addItem( mComposerMap ); - mComposerMap->setNewExtent( QgsRectangle( 0, -192, 256, -64 ) ); - mComposerMap->setMapRotation( 90 ); - mComposerMap->setLayers( QList() << mRasterLayer ); - - QgsCompositionChecker checker( QStringLiteral( "composerrotation_maprotation" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); - QVERIFY( checker.testComposition( mReport, 0, 200 ) ); -} - -void TestQgsComposerRotation::mapItemRotation() -{ - // cleanup after mapRotation() - mComposition->removeItem( mComposerMap ); - delete mComposerMap; - - //test map item rotation - mComposerMap = new QgsComposerMap( mComposition, 20, 50, 100, 50 ); - mComposerMap->setFrameEnabled( true ); - mComposition->addItem( mComposerMap ); - mComposerMap->setNewExtent( QgsRectangle( 0, -192, 256, -64 ) ); - mComposerMap->setItemRotation( 90, true ); - mComposerMap->setLayers( QList() << mRasterLayer ); - - QgsCompositionChecker checker( QStringLiteral( "composerrotation_mapitemrotation" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -QGSTEST_MAIN( TestQgsComposerRotation ) -#include "testqgscomposerrotation.moc" diff --git a/tests/src/core/testqgscomposerscalebar.cpp b/tests/src/core/testqgscomposerscalebar.cpp deleted file mode 100644 index 571ddfe7df8..00000000000 --- a/tests/src/core/testqgscomposerscalebar.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/*************************************************************************** - testqgscomposerscalebar.cpp - --------------------------- - begin : August 2012 - copyright : (C) 2012 by Marco Hugentobler - email : marco at sourcepole dot ch - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposermap.h" -#include "qgscomposerscalebar.h" -#include "qgsmultibandcolorrenderer.h" -#include "qgsrasterlayer.h" -#include "qgsrasterdataprovider.h" -#include "qgsfontutils.h" -#include "qgsproperty.h" -#include "qgsproject.h" - -#include -#include -#include "qgstest.h" - -class TestQgsComposerScaleBar : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerScaleBar() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void singleBox(); - void singleBoxAlpha(); - void doubleBox(); - void numeric(); - void tick(); - void dataDefined(); - - private: - QgsComposition *mComposition = nullptr; - QgsComposerMap *mComposerMap = nullptr; - QgsComposerScaleBar *mComposerScaleBar = nullptr; - QgsRasterLayer *mRasterLayer = nullptr; - QString mReport; -}; - -void TestQgsComposerScaleBar::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - // the scale denominator is formatted in a locale aware manner - // so 10000 is rendered as "10,000" in C (or en_US) locale, however - // other locales may render the number differently (e.g. "10 000" in cs_CZ) - // so we enforce C locale to make sure we get expected result - QLocale::setDefault( QLocale::c() ); - - //reproject to WGS84 - QgsCoordinateReferenceSystem destCRS; - destCRS.createFromId( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ); - QgsProject::instance()->setCrs( destCRS ); - QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) ); - - //create maplayers from testdata and add to layer registry - QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif" ); - mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 2, 3, 4 ); - mRasterLayer->setRenderer( rasterRenderer ); - - //create composition with composer map - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerMap = new QgsComposerMap( mComposition, 20, 20, 150, 150 ); - mComposerMap->setFrameEnabled( true ); - mComposition->addComposerMap( mComposerMap ); - mComposerMap->setNewExtent( QgsRectangle( 17.923, 30.160, 18.023, 30.260 ) ); - mComposerMap->setLayers( QList() << mRasterLayer ); - - mComposerScaleBar = new QgsComposerScaleBar( mComposition ); - mComposerScaleBar->setSceneRect( QRectF( 20, 180, 50, 20 ) ); - mComposition->addComposerScaleBar( mComposerScaleBar ); - mComposerScaleBar->setComposerMap( mComposerMap ); - mComposerScaleBar->setFont( QgsFontUtils::getStandardTestFont() ); - mComposerScaleBar->setUnits( QgsUnitTypes::DistanceMeters ); - mComposerScaleBar->setNumUnitsPerSegment( 2000 ); - mComposerScaleBar->setNumSegmentsLeft( 0 ); - mComposerScaleBar->setNumSegments( 2 ); - mComposerScaleBar->setHeight( 5 ); - mComposerScaleBar->setLineWidth( 1.0 ); - - qWarning() << "scalebar font: " << mComposerScaleBar->font().toString() << " exactMatch:" << mComposerScaleBar->font().exactMatch(); - - mReport = QStringLiteral( "

Composer Scalebar Tests

\n" ); -} - -void TestQgsComposerScaleBar::cleanupTestCase() -{ - delete mComposition; - delete mRasterLayer; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - - QgsApplication::exitQgis(); -} - -void TestQgsComposerScaleBar::init() -{ - -} - -void TestQgsComposerScaleBar::cleanup() -{ - -} - -void TestQgsComposerScaleBar::singleBox() -{ - mComposerScaleBar->setStyle( QStringLiteral( "Single Box" ) ); - QgsCompositionChecker checker( QStringLiteral( "composerscalebar_singlebox" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_scalebar" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerScaleBar::singleBoxAlpha() -{ - mComposerScaleBar->setStyle( QStringLiteral( "Single Box" ) ); - mComposerScaleBar->setFillColor( QColor( 255, 0, 0, 100 ) ); - mComposerScaleBar->setFillColor2( QColor( 0, 255, 0, 50 ) ); - mComposerScaleBar->setLineColor( QColor( 0, 0, 255, 150 ) ); - mComposerScaleBar->setFontColor( QColor( 255, 0, 255, 100 ) ); - mComposerScaleBar->setLineWidth( 1.0 ); - QgsCompositionChecker checker( QStringLiteral( "composerscalebar_singlebox_alpha" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_scalebar" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerScaleBar::doubleBox() -{ - // cleanup singleBoxAlpha - mComposerScaleBar->setFillColor( Qt::black ); - mComposerScaleBar->setFillColor2( Qt::white ); - mComposerScaleBar->setLineColor( Qt::black ); - mComposerScaleBar->setLineWidth( 1.0 ); - mComposerScaleBar->setFontColor( Qt::black ); - mComposerScaleBar->setStyle( QStringLiteral( "Double Box" ) ); - - QgsCompositionChecker checker( QStringLiteral( "composerscalebar_doublebox" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_scalebar" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerScaleBar::numeric() -{ - QFont f = mComposerScaleBar->font(); - - QFont newFont = QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ); - newFont.setPointSizeF( 12 ); - mComposerScaleBar->setFont( newFont ); - - mComposerScaleBar->setStyle( QStringLiteral( "Numeric" ) ); - mComposerScaleBar->setSceneRect( QRectF( 20, 180, 50, 20 ) ); - QgsCompositionChecker checker( QStringLiteral( "composerscalebar_numeric" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_scalebar" ) ); - bool result = checker.testComposition( mReport, 0, 0 ); - mComposerScaleBar->setFont( f ); - QVERIFY( result ); -} - -void TestQgsComposerScaleBar::tick() -{ - mComposerScaleBar->setStyle( QStringLiteral( "Line Ticks Up" ) ); - mComposerScaleBar->setLineWidth( 1.0 ); - QgsCompositionChecker checker( QStringLiteral( "composerscalebar_tick" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_scalebar" ) ); - QVERIFY( checker.testComposition( mReport, 0, 0 ) ); -} - -void TestQgsComposerScaleBar::dataDefined() -{ - mComposerScaleBar->dataDefinedProperties().setProperty( QgsComposerObject::ScalebarFillColor, QgsProperty::fromExpression( QStringLiteral( "'red'" ) ) ); - mComposerScaleBar->dataDefinedProperties().setProperty( QgsComposerObject::ScalebarFillColor2, QgsProperty::fromExpression( QStringLiteral( "'blue'" ) ) ); - mComposerScaleBar->dataDefinedProperties().setProperty( QgsComposerObject::ScalebarLineColor, QgsProperty::fromExpression( QStringLiteral( "'yellow'" ) ) ); - mComposerScaleBar->dataDefinedProperties().setProperty( QgsComposerObject::ScalebarLineWidth, QgsProperty::fromExpression( QStringLiteral( "1.2" ) ) ); - mComposerScaleBar->refreshDataDefinedProperty(); - QCOMPARE( mComposerScaleBar->brush().color().name(), QColor( 255, 0, 0 ).name() ); - QCOMPARE( mComposerScaleBar->brush2().color().name(), QColor( 0, 0, 255 ).name() ); - QCOMPARE( mComposerScaleBar->pen().color().name(), QColor( 255, 255, 0 ).name() ); - QCOMPARE( mComposerScaleBar->pen().widthF(), 1.2 ); - mComposerScaleBar->setDataDefinedProperties( QgsPropertyCollection() ); - mComposerScaleBar->setLineWidth( 1.0 ); -} - -QGSTEST_MAIN( TestQgsComposerScaleBar ) -#include "testqgscomposerscalebar.moc" diff --git a/tests/src/core/testqgscomposershapes.cpp b/tests/src/core/testqgscomposershapes.cpp deleted file mode 100644 index 64396678439..00000000000 --- a/tests/src/core/testqgscomposershapes.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** - testqgscomposershapes.cpp - ---------------------- - begin : April 2013 - copyright : (C) 2013 by Marco Hugentobler, Nyall Dawson - email : nyall dot dawson at gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgsmultirenderchecker.h" -#include "qgscomposershape.h" -#include "qgsmapsettings.h" -#include "qgsproject.h" -#include "qgssymbol.h" -#include "qgssinglesymbolrenderer.h" -#include "qgsfillsymbollayer.h" -#include -#include "qgstest.h" -#include -#include - -class TestQgsComposerShapes : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerShapes() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void rectangle(); //test if rectangle shape is functioning - void triangle(); //test if triange shape is functioning - void ellipse(); //test if ellipse shape is functioning - void roundedRectangle(); //test if rounded rectangle shape is functioning - void symbol(); //test is styling shapes via symbol is working - - private: - QgsComposition *mComposition = nullptr; - QgsComposerShape *mComposerShape = nullptr; - QString mReport; -}; - -void TestQgsComposerShapes::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create composition with two rectangles - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposerShape = new QgsComposerShape( 20, 20, 150, 100, mComposition ); - mComposerShape->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) ); - mComposition->addComposerShape( mComposerShape ); - - mReport = QStringLiteral( "

Composer Shape Tests

\n" ); -} - -void TestQgsComposerShapes::cleanupTestCase() -{ - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposerShapes::init() -{ - -} - -void TestQgsComposerShapes::cleanup() -{ - -} - -void TestQgsComposerShapes::rectangle() -{ - mComposerShape->setShapeType( QgsComposerShape::Rectangle ); - - QgsCompositionChecker checker( QStringLiteral( "composershapes_rectangle" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerShapes::triangle() -{ - mComposerShape->setShapeType( QgsComposerShape::Triangle ); - - QgsCompositionChecker checker( QStringLiteral( "composershapes_triangle" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerShapes::ellipse() -{ - mComposerShape->setShapeType( QgsComposerShape::Ellipse ); - - QgsCompositionChecker checker( QStringLiteral( "composershapes_ellipse" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -void TestQgsComposerShapes::roundedRectangle() -{ - mComposerShape->setShapeType( QgsComposerShape::Rectangle ); - mComposerShape->setCornerRadius( 30 ); - - QgsCompositionChecker checker( QStringLiteral( "composershapes_roundedrect" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) ); - QVERIFY( checker.testComposition( mReport ) ); - mComposerShape->setCornerRadius( 0 ); -} - -void TestQgsComposerShapes::symbol() -{ - mComposerShape->setShapeType( QgsComposerShape::Rectangle ); - - //setup simple fill - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::green ); - simpleFill->setStrokeColor( Qt::yellow ); - simpleFill->setStrokeWidth( 6 ); - - mComposerShape->setShapeStyleSymbol( fillSymbol ); - mComposerShape->setUseSymbol( true ); - delete fillSymbol; - - QgsCompositionChecker checker( QStringLiteral( "composershapes_symbol" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) ); - QVERIFY( checker.testComposition( mReport ) ); -} - -QGSTEST_MAIN( TestQgsComposerShapes ) -#include "testqgscomposershapes.moc" diff --git a/tests/src/core/testqgscomposertablev2.cpp b/tests/src/core/testqgscomposertablev2.cpp deleted file mode 100644 index 544b305a076..00000000000 --- a/tests/src/core/testqgscomposertablev2.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -/*************************************************************************** - testqgscomposertablev2.cpp - ---------------------- - begin : September 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgscomposermap.h" -#include "qgscomposerattributetablev2.h" -#include "qgscomposertablecolumn.h" -#include "qgscomposerframe.h" -#include "qgsmapsettings.h" -#include "qgsvectorlayer.h" -#include "qgsvectordataprovider.h" -#include "qgsfeature.h" -#include "qgsmultirenderchecker.h" -#include "qgsfontutils.h" -#include "qgsproject.h" -#include "qgsrelationmanager.h" - -#include -#include "qgstest.h" - -class TestQgsComposerTableV2 : public QObject -{ - Q_OBJECT - - public: - TestQgsComposerTableV2() = default; - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - - void attributeTableHeadings(); //test retrieving attribute table headers - void attributeTableRows(); //test retrieving attribute table rows - void attributeTableFilterFeatures(); //test filtering attribute table rows - void attributeTableSetAttributes(); //test subset of attributes in table - void attributeTableVisibleOnly(); //test displaying only visible attributes - void attributeTableRender(); //test rendering attribute table - void manualColumnWidth(); //test setting manual column widths - void attributeTableEmpty(); //test empty modes for attribute table - void showEmptyRows(); //test showing empty rows - void attributeTableExtend(); - void attributeTableRepeat(); - void attributeTableAtlasSource(); //test attribute table in atlas feature mode - void attributeTableRelationSource(); //test attribute table in relation mode - void contentsContainsRow(); //test the contentsContainsRow function - void removeDuplicates(); //test removing duplicate rows - void multiLineText(); //test rendering a table with multiline text - void horizontalGrid(); //test rendering a table with horizontal-only grid - void verticalGrid(); //test rendering a table with vertical-only grid - void align(); //test alignment of table cells - void wrapChar(); //test setting wrap character - void autoWrap(); //test auto word wrap - void cellStyles(); //test cell styles - void cellStylesRender(); //test rendering cell styles - - private: - QgsComposition *mComposition = nullptr; - QgsVectorLayer *mVectorLayer = nullptr; - QgsComposerAttributeTableV2 *mComposerAttributeTable = nullptr; - QgsComposerFrame *mFrame1 = nullptr; - QgsComposerFrame *mFrame2 = nullptr; - QString mReport; - - //compares rows in mComposerAttributeTable to expected rows - void compareTable( QList &expectedRows ); -}; - -void TestQgsComposerTableV2::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create maplayers from testdata and add to layer registry - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject::instance()->addMapLayer( mVectorLayer ); - - mReport = QStringLiteral( "

Composer TableV2 Tests

\n" ); -} - -void TestQgsComposerTableV2::cleanupTestCase() -{ - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposerTableV2::init() -{ - //create composition with composer map - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 portrait - - mComposerAttributeTable = new QgsComposerAttributeTableV2( mComposition, false ); - mComposition->addMultiFrame( mComposerAttributeTable ); - - mFrame1 = new QgsComposerFrame( mComposition, mComposerAttributeTable, 5, 5, 100, 30 ); - mFrame2 = new QgsComposerFrame( mComposition, mComposerAttributeTable, 5, 40, 100, 30 ); - - mFrame1->setFrameEnabled( true ); - mFrame2->setFrameEnabled( true ); - mComposerAttributeTable->addFrame( mFrame1 ); - mComposerAttributeTable->addFrame( mFrame2 ); - - mComposition->addComposerTableFrame( mComposerAttributeTable, mFrame1 ); - mComposition->addComposerTableFrame( mComposerAttributeTable, mFrame2 ); - mComposerAttributeTable->setVectorLayer( mVectorLayer ); - mComposerAttributeTable->setDisplayOnlyVisibleFeatures( false ); - mComposerAttributeTable->setMaximumNumberOfFeatures( 10 ); - mComposerAttributeTable->setContentFont( QgsFontUtils::getStandardTestFont() ); - mComposerAttributeTable->setHeaderFont( QgsFontUtils::getStandardTestFont() ); - mComposerAttributeTable->setBackgroundColor( Qt::yellow ); -} - -void TestQgsComposerTableV2::cleanup() -{ - delete mComposition; -} - -void TestQgsComposerTableV2::attributeTableHeadings() -{ - //test retrieving attribute table headers - QStringList expectedHeaders; - expectedHeaders << QStringLiteral( "Class" ) << QStringLiteral( "Heading" ) << QStringLiteral( "Importance" ) << QStringLiteral( "Pilots" ) << QStringLiteral( "Cabin Crew" ) << QStringLiteral( "Staff" ); - - //get header labels and compare - QMap headerMap = mComposerAttributeTable->headerLabels(); - QMap::const_iterator headerIt = headerMap.constBegin(); - QString expected; - QString evaluated; - for ( ; headerIt != headerMap.constEnd(); ++headerIt ) - { - evaluated = headerIt.value(); - expected = expectedHeaders.at( headerIt.key() ); - QCOMPARE( evaluated, expected ); - } -} - -void TestQgsComposerTableV2::compareTable( QList &expectedRows ) -{ - //retrieve rows and check - QgsComposerTableContents tableContents; - bool result = mComposerAttributeTable->getTableContents( tableContents ); - QCOMPARE( result, true ); - - QgsComposerTableContents::const_iterator resultIt = tableContents.constBegin(); - int rowNumber = 0; - int colNumber = 0; - - //check that number of rows matches expected - QCOMPARE( tableContents.count(), expectedRows.count() ); - - for ( ; resultIt != tableContents.constEnd(); ++resultIt ) - { - colNumber = 0; - QgsComposerTableRow::const_iterator cellIt = ( *resultIt ).constBegin(); - for ( ; cellIt != ( *resultIt ).constEnd(); ++cellIt ) - { - QCOMPARE( ( *cellIt ).toString(), expectedRows.at( rowNumber ).at( colNumber ) ); - colNumber++; - } - //also check that number of columns matches expected - QCOMPARE( ( *resultIt ).count(), expectedRows.at( rowNumber ).count() ); - - rowNumber++; - } -} - -void TestQgsComposerTableV2::attributeTableRows() -{ - //test retrieving attribute table rows - - QList expectedRows; - QStringList row; - row << QStringLiteral( "Jet" ) << QStringLiteral( "90" ) << QStringLiteral( "3" ) << QStringLiteral( "2" ) << QStringLiteral( "0" ) << QStringLiteral( "2" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Biplane" ) << QStringLiteral( "0" ) << QStringLiteral( "1" ) << QStringLiteral( "3" ) << QStringLiteral( "3" ) << QStringLiteral( "6" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Jet" ) << QStringLiteral( "85" ) << QStringLiteral( "3" ) << QStringLiteral( "1" ) << QStringLiteral( "1" ) << QStringLiteral( "2" ); - expectedRows.append( row ); - - //retrieve rows and check - mComposerAttributeTable->setMaximumNumberOfFeatures( 3 ); - compareTable( expectedRows ); -} - -void TestQgsComposerTableV2::attributeTableFilterFeatures() -{ - //test filtering attribute table rows - mComposerAttributeTable->setMaximumNumberOfFeatures( 10 ); - mComposerAttributeTable->setFeatureFilter( QStringLiteral( "\"Class\"='B52'" ) ); - mComposerAttributeTable->setFilterFeatures( true ); - - QList expectedRows; - QStringList row; - row << QStringLiteral( "B52" ) << QStringLiteral( "0" ) << QStringLiteral( "10" ) << QStringLiteral( "2" ) << QStringLiteral( "1" ) << QStringLiteral( "3" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "B52" ) << QStringLiteral( "12" ) << QStringLiteral( "10" ) << QStringLiteral( "1" ) << QStringLiteral( "1" ) << QStringLiteral( "2" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "B52" ) << QStringLiteral( "34" ) << QStringLiteral( "10" ) << QStringLiteral( "2" ) << QStringLiteral( "1" ) << QStringLiteral( "3" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "B52" ) << QStringLiteral( "80" ) << QStringLiteral( "10" ) << QStringLiteral( "2" ) << QStringLiteral( "1" ) << QStringLiteral( "3" ); - expectedRows.append( row ); - - //retrieve rows and check - compareTable( expectedRows ); - - mComposerAttributeTable->setFilterFeatures( false ); -} - -void TestQgsComposerTableV2::attributeTableSetAttributes() -{ - //test subset of attributes in table - QStringList attributes; - attributes << QStringLiteral( "Class" ) << QStringLiteral( "Pilots" ) << QStringLiteral( "Cabin Crew" ); - mComposerAttributeTable->setDisplayedFields( attributes ); - mComposerAttributeTable->setMaximumNumberOfFeatures( 3 ); - - //check headers - QStringList expectedHeaders; - expectedHeaders << QStringLiteral( "Class" ) << QStringLiteral( "Pilots" ) << QStringLiteral( "Cabin Crew" ); - - //get header labels and compare - QMap headerMap = mComposerAttributeTable->headerLabels(); - QMap::const_iterator headerIt = headerMap.constBegin(); - QString expected; - QString evaluated; - for ( ; headerIt != headerMap.constEnd(); ++headerIt ) - { - evaluated = headerIt.value(); - expected = expectedHeaders.at( headerIt.key() ); - QCOMPARE( evaluated, expected ); - } - - QList expectedRows; - QStringList row; - row << QStringLiteral( "Jet" ) << QStringLiteral( "2" ) << QStringLiteral( "0" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Biplane" ) << QStringLiteral( "3" ) << QStringLiteral( "3" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Jet" ) << QStringLiteral( "1" ) << QStringLiteral( "1" ); - expectedRows.append( row ); - - //retrieve rows and check - compareTable( expectedRows ); - - attributes.clear(); - mComposerAttributeTable->setDisplayedFields( attributes ); -} - -void TestQgsComposerTableV2::attributeTableVisibleOnly() -{ - //test displaying only visible attributes - - QgsComposerMap *composerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 ); - composerMap->setFrameEnabled( true ); - composerMap->setNewExtent( QgsRectangle( -131.767, 30.558, -110.743, 41.070 ) ); - - mComposerAttributeTable->setComposerMap( composerMap ); - mComposerAttributeTable->setDisplayOnlyVisibleFeatures( true ); - - QList expectedRows; - QStringList row; - row << QStringLiteral( "Jet" ) << QStringLiteral( "90" ) << QStringLiteral( "3" ) << QStringLiteral( "2" ) << QStringLiteral( "0" ) << QStringLiteral( "2" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Biplane" ) << QStringLiteral( "240" ) << QStringLiteral( "1" ) << QStringLiteral( "3" ) << QStringLiteral( "2" ) << QStringLiteral( "5" ); - expectedRows.append( row ); - row.clear(); - row << QStringLiteral( "Jet" ) << QStringLiteral( "180" ) << QStringLiteral( "3" ) << QStringLiteral( "1" ) << QStringLiteral( "0" ) << QStringLiteral( "1" ); - expectedRows.append( row ); - - //retrieve rows and check - compareTable( expectedRows ); - - mComposerAttributeTable->setDisplayOnlyVisibleFeatures( false ); - mComposerAttributeTable->setComposerMap( 0 ); - delete composerMap; -} - -void TestQgsComposerTableV2::attributeTableRender() -{ - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_render" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport ); - QVERIFY( result ); -} - -void TestQgsComposerTableV2::manualColumnWidth() -{ - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->columns()->at( 0 )->setWidth( 5 ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_columnwidth" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport, 0 ); - mComposerAttributeTable->columns()->at( 0 )->setWidth( 0 ); - QVERIFY( result ); -} - -void TestQgsComposerTableV2::attributeTableEmpty() -{ - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - //hide all features from table - mComposerAttributeTable->setFeatureFilter( QStringLiteral( "1=2" ) ); - mComposerAttributeTable->setFilterFeatures( true ); - - mComposerAttributeTable->setEmptyTableBehavior( QgsComposerTableV2::HeadersOnly ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_headersonly" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - QVERIFY( checker.testComposition( mReport, 0 ) ); - - mComposerAttributeTable->setEmptyTableBehavior( QgsComposerTableV2::HideTable ); - QgsCompositionChecker checker2( QStringLiteral( "composerattributetable_hidetable" ), mComposition ); - checker2.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - QVERIFY( checker2.testComposition( mReport, 0 ) ); - - mComposerAttributeTable->setEmptyTableBehavior( QgsComposerTableV2::ShowMessage ); - mComposerAttributeTable->setEmptyTableMessage( QStringLiteral( "no rows" ) ); - QgsCompositionChecker checker3( QStringLiteral( "composerattributetable_showmessage" ), mComposition ); - checker3.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - QVERIFY( checker3.testComposition( mReport, 0 ) ); - - mComposerAttributeTable->setFilterFeatures( false ); -} - -void TestQgsComposerTableV2::showEmptyRows() -{ - mComposerAttributeTable->setMaximumNumberOfFeatures( 3 ); - mComposerAttributeTable->setShowEmptyRows( true ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_drawempty" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - QVERIFY( checker.testComposition( mReport, 0 ) ); - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setShowEmptyRows( false ); -} - -void TestQgsComposerTableV2::attributeTableExtend() -{ - //test that adding and removing frames automatically does not result in a crash - mComposerAttributeTable->removeFrame( 1 ); - - //force auto creation of some new frames - mComposerAttributeTable->setResizeMode( QgsComposerMultiFrame::ExtendToNextPage ); - - mComposition->setSelectedItem( mComposerAttributeTable->frame( 1 ) ); - - //now auto remove extra created frames - mComposerAttributeTable->setMaximumNumberOfFeatures( 1 ); -} - -void TestQgsComposerTableV2::attributeTableRepeat() -{ - //test that creating and removing new frames in repeat mode does not crash - - mComposerAttributeTable->setResizeMode( QgsComposerMultiFrame::UseExistingFrames ); - //remove extra frames - for ( int idx = mComposerAttributeTable->frameCount(); idx > 0; --idx ) - { - mComposerAttributeTable->removeFrame( idx - 1 ); - } - - mComposerAttributeTable->setMaximumNumberOfFeatures( 1 ); - - //force auto creation of some new frames - mComposerAttributeTable->setResizeMode( QgsComposerMultiFrame::RepeatUntilFinished ); - - for ( int features = 0; features < 50; ++features ) - { - mComposerAttributeTable->setMaximumNumberOfFeatures( features ); - } - - //and then the reverse.... - for ( int features = 50; features > 1; --features ) - { - mComposerAttributeTable->setMaximumNumberOfFeatures( features ); - } -} - -void TestQgsComposerTableV2::attributeTableAtlasSource() -{ - QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( mComposition, false ); - - - table->setSource( QgsComposerAttributeTableV2::AtlasFeature ); - - //setup atlas - QgsVectorLayer *vectorLayer = nullptr; - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - vectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject::instance()->addMapLayer( vectorLayer ); - mComposition->atlasComposition().setCoverageLayer( vectorLayer ); - mComposition->atlasComposition().setEnabled( true ); - QVERIFY( mComposition->atlasComposition().beginRender() ); - - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); - QCOMPARE( table->contents()->length(), 1 ); - QgsComposerTableRow row = table->contents()->at( 0 ); - - //check a couple of results - QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); - QCOMPARE( row.at( 1 ), QVariant( 90 ) ); - QCOMPARE( row.at( 2 ), QVariant( 3 ) ); - QCOMPARE( row.at( 3 ), QVariant( 2 ) ); - QCOMPARE( row.at( 4 ), QVariant( 0 ) ); - QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - - //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 1 ) ); - QCOMPARE( table->contents()->length(), 1 ); - row = table->contents()->at( 0 ); - QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); - QCOMPARE( row.at( 1 ), QVariant( 0 ) ); - QCOMPARE( row.at( 2 ), QVariant( 1 ) ); - QCOMPARE( row.at( 3 ), QVariant( 3 ) ); - QCOMPARE( row.at( 4 ), QVariant( 3 ) ); - QCOMPARE( row.at( 5 ), QVariant( 6 ) ); - - //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 2 ) ); - QCOMPARE( table->contents()->length(), 1 ); - row = table->contents()->at( 0 ); - QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); - QCOMPARE( row.at( 1 ), QVariant( 85 ) ); - QCOMPARE( row.at( 2 ), QVariant( 3 ) ); - QCOMPARE( row.at( 3 ), QVariant( 1 ) ); - QCOMPARE( row.at( 4 ), QVariant( 1 ) ); - QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - - mComposition->atlasComposition().endRender(); - - //try for a crash when removing current atlas layer - QgsProject::instance()->removeMapLayer( vectorLayer->id() ); - table->refreshAttributes(); - - mComposition->removeMultiFrame( table ); - delete table; -} - - -void TestQgsComposerTableV2::attributeTableRelationSource() -{ - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points_relations.shp" ); - QgsVectorLayer *atlasLayer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - - QgsProject::instance()->addMapLayer( atlasLayer ); - - //setup atlas - mComposition->atlasComposition().setCoverageLayer( atlasLayer ); - mComposition->atlasComposition().setEnabled( true ); - - //create a relation - QgsRelation relation; - relation.setId( QStringLiteral( "testrelation" ) ); - relation.setReferencedLayer( atlasLayer->id() ); - relation.setReferencingLayer( mVectorLayer->id() ); - relation.addFieldPair( QStringLiteral( "Class" ), QStringLiteral( "Class" ) ); - QgsProject::instance()->relationManager()->addRelation( relation ); - - QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( mComposition, false ); - table->setMaximumNumberOfFeatures( 50 ); - table->setSource( QgsComposerAttributeTableV2::RelationChildren ); - table->setRelationId( relation.id() ); - - QVERIFY( mComposition->atlasComposition().beginRender() ); - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); - - QCOMPARE( mComposition->atlasComposition().feature().attribute( "Class" ).toString(), QString( "Jet" ) ); - QCOMPARE( table->contents()->length(), 8 ); - - QgsComposerTableRow row = table->contents()->at( 0 ); - - //check a couple of results - QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); - QCOMPARE( row.at( 1 ), QVariant( 90 ) ); - QCOMPARE( row.at( 2 ), QVariant( 3 ) ); - QCOMPARE( row.at( 3 ), QVariant( 2 ) ); - QCOMPARE( row.at( 4 ), QVariant( 0 ) ); - QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - row = table->contents()->at( 1 ); - QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); - QCOMPARE( row.at( 1 ), QVariant( 85 ) ); - QCOMPARE( row.at( 2 ), QVariant( 3 ) ); - QCOMPARE( row.at( 3 ), QVariant( 1 ) ); - QCOMPARE( row.at( 4 ), QVariant( 1 ) ); - QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - row = table->contents()->at( 2 ); - QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); - QCOMPARE( row.at( 1 ), QVariant( 95 ) ); - QCOMPARE( row.at( 2 ), QVariant( 3 ) ); - QCOMPARE( row.at( 3 ), QVariant( 1 ) ); - QCOMPARE( row.at( 4 ), QVariant( 1 ) ); - QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - - //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 1 ) ); - QCOMPARE( mComposition->atlasComposition().feature().attribute( "Class" ).toString(), QString( "Biplane" ) ); - QCOMPARE( table->contents()->length(), 5 ); - row = table->contents()->at( 0 ); - QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); - QCOMPARE( row.at( 1 ), QVariant( 0 ) ); - QCOMPARE( row.at( 2 ), QVariant( 1 ) ); - QCOMPARE( row.at( 3 ), QVariant( 3 ) ); - QCOMPARE( row.at( 4 ), QVariant( 3 ) ); - QCOMPARE( row.at( 5 ), QVariant( 6 ) ); - row = table->contents()->at( 1 ); - QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); - QCOMPARE( row.at( 1 ), QVariant( 340 ) ); - QCOMPARE( row.at( 2 ), QVariant( 1 ) ); - QCOMPARE( row.at( 3 ), QVariant( 3 ) ); - QCOMPARE( row.at( 4 ), QVariant( 3 ) ); - QCOMPARE( row.at( 5 ), QVariant( 6 ) ); - - mComposition->atlasComposition().endRender(); - - //try for a crash when removing current atlas layer - QgsProject::instance()->removeMapLayer( atlasLayer->id() ); - - table->refreshAttributes(); - - mComposition->removeMultiFrame( table ); - delete table; -} - -void TestQgsComposerTableV2::contentsContainsRow() -{ - QgsComposerTableContents testContents; - QgsComposerTableRow row1; - row1 << QVariant( QStringLiteral( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QStringLiteral( "string 2" ) ); - QgsComposerTableRow row2; - row2 << QVariant( QStringLiteral( "string 2" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QStringLiteral( "string 2" ) ); - //same as row1 - QgsComposerTableRow row3; - row3 << QVariant( QStringLiteral( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QStringLiteral( "string 2" ) ); - QgsComposerTableRow row4; - row4 << QVariant( QStringLiteral( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.7 ) << QVariant( QStringLiteral( "string 2" ) ); - - testContents << row1; - testContents << row2; - - QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row1 ) ); - QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row2 ) ); - QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row3 ) ); - QVERIFY( !mComposerAttributeTable->contentsContainsRow( testContents, row4 ) ); -} - -void TestQgsComposerTableV2::removeDuplicates() -{ - QgsVectorLayer *dupesLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:integer&field=col3:integer" ), QStringLiteral( "dupes" ), QStringLiteral( "memory" ) ); - QVERIFY( dupesLayer->isValid() ); - QgsFeature f1( dupesLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), 1 ); - f1.setAttribute( QStringLiteral( "col2" ), 1 ); - f1.setAttribute( QStringLiteral( "col3" ), 1 ); - QgsFeature f2( dupesLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), 1 ); - f2.setAttribute( QStringLiteral( "col2" ), 2 ); - f2.setAttribute( QStringLiteral( "col3" ), 2 ); - QgsFeature f3( dupesLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), 1 ); - f3.setAttribute( QStringLiteral( "col2" ), 2 ); - f3.setAttribute( QStringLiteral( "col3" ), 3 ); - QgsFeature f4( dupesLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), 1 ); - f4.setAttribute( QStringLiteral( "col2" ), 1 ); - f4.setAttribute( QStringLiteral( "col3" ), 1 ); - dupesLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( mComposition, false ); - table->setSource( QgsComposerAttributeTableV2::LayerAttributes ); - table->setVectorLayer( dupesLayer ); - table->setMaximumNumberOfFeatures( 50 ); - QCOMPARE( table->contents()->length(), 4 ); - - table->setUniqueRowsOnly( true ); - QCOMPARE( table->contents()->length(), 3 ); - - //check if removing attributes in unique mode works correctly (should result in duplicate rows, - //which will be stripped out) - delete table->columns()->takeLast(); - table->refreshAttributes(); - QCOMPARE( table->contents()->length(), 2 ); - delete table->columns()->takeLast(); - table->refreshAttributes(); - QCOMPARE( table->contents()->length(), 1 ); - table->setUniqueRowsOnly( false ); - QCOMPARE( table->contents()->length(), 4 ); - - mComposition->removeMultiFrame( table ); - delete table; - delete dupesLayer; -} - -void TestQgsComposerTableV2::multiLineText() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f2( multiLineLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), "singleline string" ); - f2.setAttribute( QStringLiteral( "col2" ), "multiline\nstring" ); - f2.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f3( multiLineLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col2" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col3" ), "multiline\nstring" ); - QgsFeature f4( multiLineLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), "long triple\nline\nstring" ); - f4.setAttribute( QStringLiteral( "col2" ), "double\nlinestring" ); - f4.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - mFrame2->setSceneRect( QRectF( 5, 40, 100, 90 ) ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_multiline" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport ); - QVERIFY( result ); - - delete multiLineLayer; -} - -void TestQgsComposerTableV2::horizontalGrid() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f2( multiLineLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), "singleline string" ); - f2.setAttribute( QStringLiteral( "col2" ), "multiline\nstring" ); - f2.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f3( multiLineLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col2" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col3" ), "multiline\nstring" ); - QgsFeature f4( multiLineLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), "long triple\nline\nstring" ); - f4.setAttribute( QStringLiteral( "col2" ), "double\nlinestring" ); - f4.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - mFrame1->setFrameEnabled( false ); - mFrame2->setFrameEnabled( false ); - mFrame2->setSceneRect( QRectF( 5, 40, 100, 90 ) ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setShowGrid( true ); - mComposerAttributeTable->setHorizontalGrid( true ); - mComposerAttributeTable->setVerticalGrid( false ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_horizontalgrid" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport ); - QVERIFY( result ); - - delete multiLineLayer; -} - -void TestQgsComposerTableV2::verticalGrid() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f2( multiLineLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), "singleline string" ); - f2.setAttribute( QStringLiteral( "col2" ), "multiline\nstring" ); - f2.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f3( multiLineLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col2" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col3" ), "multiline\nstring" ); - QgsFeature f4( multiLineLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), "long triple\nline\nstring" ); - f4.setAttribute( QStringLiteral( "col2" ), "double\nlinestring" ); - f4.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - mFrame1->setFrameEnabled( false ); - mFrame2->setFrameEnabled( false ); - mFrame2->setSceneRect( QRectF( 5, 40, 100, 90 ) ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setShowGrid( true ); - mComposerAttributeTable->setHorizontalGrid( false ); - mComposerAttributeTable->setVerticalGrid( true ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_verticalgrid" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport ); - QVERIFY( result ); - - delete multiLineLayer; -} - -void TestQgsComposerTableV2::align() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f2( multiLineLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), "singleline string" ); - f2.setAttribute( QStringLiteral( "col2" ), "multiline\nstring" ); - f2.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f3( multiLineLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col2" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col3" ), "multiline\nstring" ); - QgsFeature f4( multiLineLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), "long triple\nline\nstring" ); - f4.setAttribute( QStringLiteral( "col2" ), "double\nlinestring" ); - f4.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - mFrame2->setSceneRect( QRectF( 5, 40, 100, 90 ) ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - - mComposerAttributeTable->columns()->at( 0 )->setHAlignment( Qt::AlignLeft ); - mComposerAttributeTable->columns()->at( 0 )->setVAlignment( Qt::AlignTop ); - mComposerAttributeTable->columns()->at( 1 )->setHAlignment( Qt::AlignHCenter ); - mComposerAttributeTable->columns()->at( 1 )->setVAlignment( Qt::AlignVCenter ); - mComposerAttributeTable->columns()->at( 2 )->setHAlignment( Qt::AlignRight ); - mComposerAttributeTable->columns()->at( 2 )->setVAlignment( Qt::AlignBottom ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_align" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport ); - QVERIFY( result ); - - delete multiLineLayer; -} - -void TestQgsComposerTableV2::wrapChar() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 1 ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - mComposerAttributeTable->setWrapString( QStringLiteral( "in" ) ); - - QList expectedRows; - QStringList row; - row << QStringLiteral( "multil\ne\nstr\ng" ) << QStringLiteral( "s\nglel\ne str\ng" ) << QStringLiteral( "s\nglel\ne" ); - expectedRows.append( row ); - - //retrieve rows and check - compareTable( expectedRows ); -} - -void TestQgsComposerTableV2::autoWrap() -{ - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); - QVERIFY( multiLineLayer->isValid() ); - QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); - f1.setAttribute( QStringLiteral( "col1" ), "long multiline\nstring" ); - f1.setAttribute( QStringLiteral( "col2" ), "singleline string" ); - f1.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f2( multiLineLayer->dataProvider()->fields(), 2 ); - f2.setAttribute( QStringLiteral( "col1" ), "singleline string" ); - f2.setAttribute( QStringLiteral( "col2" ), "multiline\nstring" ); - f2.setAttribute( QStringLiteral( "col3" ), "singleline" ); - QgsFeature f3( multiLineLayer->dataProvider()->fields(), 3 ); - f3.setAttribute( QStringLiteral( "col1" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col2" ), "singleline" ); - f3.setAttribute( QStringLiteral( "col3" ), "multiline\nstring" ); - QgsFeature f4( multiLineLayer->dataProvider()->fields(), 4 ); - f4.setAttribute( QStringLiteral( "col1" ), "a bit long triple line string" ); - f4.setAttribute( QStringLiteral( "col2" ), "double toolongtofitononeline string with some more lines on the end andanotherreallylongline" ); - f4.setAttribute( QStringLiteral( "col3" ), "singleline" ); - multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 ); - - mFrame2->setSceneRect( QRectF( 5, 40, 100, 90 ) ); - - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setVectorLayer( multiLineLayer ); - mComposerAttributeTable->setWrapBehavior( QgsComposerTableV2::WrapText ); - - mComposerAttributeTable->columns()->at( 0 )->setWidth( 25 ); - mComposerAttributeTable->columns()->at( 1 )->setWidth( 25 ); - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_autowrap" ), mComposition ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - bool result = checker.testComposition( mReport, 0 ); - mComposerAttributeTable->columns()->at( 0 )->setWidth( 0 ); - QVERIFY( result ); -} - -void TestQgsComposerTableV2::cellStyles() -{ - QgsComposerTableStyle original; - original.enabled = true; - original.cellBackgroundColor = QColor( 200, 100, 150, 90 ); - - //write to xml - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - - //test writing with no node - QDomElement node = doc.createElement( QStringLiteral( "style" ) ); - QVERIFY( original.writeXml( node, doc ) ); - - //read from xml - QgsComposerTableStyle styleFromXml; - styleFromXml.readXml( node ); - - //check - QCOMPARE( original.enabled, styleFromXml.enabled ); - QCOMPARE( original.cellBackgroundColor, styleFromXml.cellBackgroundColor ); - - - // check writing/reading whole set of styles - QgsComposerAttributeTableV2 originalTable( mComposition, false ); - - QgsComposerTableStyle style1; - style1.enabled = true; - style1.cellBackgroundColor = QColor( 25, 50, 75, 100 ); - originalTable.setCellStyle( QgsComposerTableV2::FirstRow, style1 ); - QgsComposerTableStyle style2; - style1.enabled = false; - style1.cellBackgroundColor = QColor( 60, 62, 64, 68 ); - originalTable.setCellStyle( QgsComposerTableV2::LastColumn, style2 ); - - //write to XML - QDomElement tableElement = doc.createElement( QStringLiteral( "table" ) ); - QVERIFY( originalTable.writeXml( tableElement, doc, true ) ); - - //read from XML - QgsComposerAttributeTableV2 tableFromXml( mComposition, false ); - tableFromXml.readXml( tableElement, doc, true ); - - //check that styles were correctly read - QCOMPARE( tableFromXml.cellStyle( QgsComposerTableV2::FirstRow )->enabled, originalTable.cellStyle( QgsComposerTableV2::FirstRow )->enabled ); - QCOMPARE( tableFromXml.cellStyle( QgsComposerTableV2::FirstRow )->cellBackgroundColor, originalTable.cellStyle( QgsComposerTableV2::FirstRow )->cellBackgroundColor ); - QCOMPARE( tableFromXml.cellStyle( QgsComposerTableV2::LastColumn )->enabled, originalTable.cellStyle( QgsComposerTableV2::LastColumn )->enabled ); - QCOMPARE( tableFromXml.cellStyle( QgsComposerTableV2::LastColumn )->cellBackgroundColor, originalTable.cellStyle( QgsComposerTableV2::LastColumn )->cellBackgroundColor ); - - //check backgroundColor method - //build up rules in descending order of precedence - mComposerAttributeTable->setBackgroundColor( QColor( 50, 50, 50, 50 ) ); - QgsComposerTableStyle style; - style.enabled = true; - style.cellBackgroundColor = QColor( 25, 50, 75, 100 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::OddColumns, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 50, 50, 50, 50 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 3 ), QColor( 50, 50, 50, 50 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 50, 50, 50, 50 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 3 ), QColor( 50, 50, 50, 50 ) ); - style.cellBackgroundColor = QColor( 30, 80, 90, 23 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::EvenColumns, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 30, 80, 90, 23 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 3 ), QColor( 30, 80, 90, 23 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 30, 80, 90, 23 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 3 ), QColor( 30, 80, 90, 23 ) ); - style.cellBackgroundColor = QColor( 111, 112, 113, 114 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::OddRows, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 3 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 30, 80, 90, 23 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 25, 50, 75, 100 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 3 ), QColor( 30, 80, 90, 23 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 3 ), QColor( 111, 112, 113, 114 ) ); - style.cellBackgroundColor = QColor( 222, 223, 224, 225 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::EvenRows, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 3 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 3 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 3 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 3, 0 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 3, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 3, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 3, 3 ), QColor( 222, 223, 224, 225 ) ); - style.cellBackgroundColor = QColor( 1, 2, 3, 4 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::FirstColumn, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 3 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 3 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 1, 2, 3, 4 ) ); - style.cellBackgroundColor = QColor( 7, 8, 9, 10 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::LastColumn, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 5 ), QColor( 7, 8, 9, 10 ) ); - style.cellBackgroundColor = QColor( 87, 88, 89, 90 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::HeaderRow, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 0 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 1 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 2 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 5 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 111, 112, 113, 114 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 5 ), QColor( 7, 8, 9, 10 ) ); - style.cellBackgroundColor = QColor( 187, 188, 189, 190 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::FirstRow, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 0 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 1 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 2 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 5 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 5 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 5 ), QColor( 7, 8, 9, 10 ) ); - style.cellBackgroundColor = QColor( 147, 148, 149, 150 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::LastRow, style ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 0 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 1 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 2 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( -1, 5 ), QColor( 87, 88, 89, 90 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 0 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 1 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 2 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 0, 5 ), QColor( 187, 188, 189, 190 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 1 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 2 ), QColor( 222, 223, 224, 225 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 1, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 0 ), QColor( 1, 2, 3, 4 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 2, 5 ), QColor( 7, 8, 9, 10 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 9, 0 ), QColor( 147, 148, 149, 150 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 9, 1 ), QColor( 147, 148, 149, 150 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 9, 2 ), QColor( 147, 148, 149, 150 ) ); - QCOMPARE( mComposerAttributeTable->backgroundColor( 9, 5 ), QColor( 147, 148, 149, 150 ) ); - - mComposition->removeMultiFrame( &originalTable ); - mComposition->removeMultiFrame( &tableFromXml ); -} - -void TestQgsComposerTableV2::cellStylesRender() -{ - mComposerAttributeTable->setMaximumNumberOfFeatures( 3 ); - mComposerAttributeTable->setShowEmptyRows( true ); - - QgsComposerTableStyle style; - style.enabled = true; - style.cellBackgroundColor = QColor( 25, 50, 75, 100 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::OddColumns, style ); - style.cellBackgroundColor = QColor( 90, 110, 150, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::EvenRows, style ); - style.cellBackgroundColor = QColor( 150, 160, 210, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::HeaderRow, style ); - style.cellBackgroundColor = QColor( 0, 200, 50, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::FirstColumn, style ); - style.cellBackgroundColor = QColor( 200, 50, 0, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::LastColumn, style ); - style.cellBackgroundColor = QColor( 200, 50, 200, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::FirstRow, style ); - style.cellBackgroundColor = QColor( 50, 200, 200, 200 ); - mComposerAttributeTable->setCellStyle( QgsComposerTableV2::LastRow, style ); - - QgsCompositionChecker checker( QStringLiteral( "composerattributetable_cellstyle" ), mComposition ); - checker.setColorTolerance( 10 ); - checker.setControlPathPrefix( QStringLiteral( "composer_table" ) ); - QVERIFY( checker.testComposition( mReport, 0 ) ); - mComposerAttributeTable->setMaximumNumberOfFeatures( 20 ); - mComposerAttributeTable->setShowEmptyRows( false ); -} - -QGSTEST_MAIN( TestQgsComposerTableV2 ) -#include "testqgscomposertablev2.moc" diff --git a/tests/src/core/testqgscomposerutils.cpp b/tests/src/core/testqgscomposerutils.cpp deleted file mode 100644 index 18f25b3d09e..00000000000 --- a/tests/src/core/testqgscomposerutils.cpp +++ /dev/null @@ -1,754 +0,0 @@ -/*************************************************************************** - testqgscomposerutils.cpp - ----------------------- - begin : July 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" //for standard test font -#include "qgscomposerutils.h" -#include "qgscomposition.h" -#include "qgscomposermap.h" -#include "qgsmultirenderchecker.h" -#include "qgsfontutils.h" -#include "qgsproject.h" -#include "qgsproperty.h" -#include -#include "qgstest.h" -#include - -class TestQgsComposerUtils : public QObject -{ - Q_OBJECT - public: - TestQgsComposerUtils(); - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - void drawArrowHead(); //test drawing an arrow head - void angle(); //test angle utility function - void rotate(); //test rotation helper function - void normalizedAngle(); //test normalised angle function - void snappedAngle(); //test snapped angle function - void largestRotatedRect(); //test largest rotated rect helper function - void pointsToMM(); //test conversion of point size to mm - void mmToPoints(); //test conversion of mm to point size - void relativePosition(); //test relative position function - void relativeResizeRect(); //test relative resize of rectangle function - void decodePaperOrientation(); //test decoding paper orientation - void decodePaperSize(); //test decoding paper size - void readOldDataDefinedProperty(); //test reading a data defined property - void readOldDataDefinedPropertyMap(); //test reading a whole data defined property map - void scaledFontPixelSize(); //test creating a scaled font - void fontAscentMM(); //test calculating font ascent in mm - void fontDescentMM(); //test calculating font descent in mm - void fontHeightMM(); //test calculating font height in mm - void fontHeightCharacterMM(); //test calculating font character height in mm - void textWidthMM(); //test calculating text width in mm - void textHeightMM(); //test calculating text height in mm - void drawTextPos(); //test drawing text at a pos - void drawTextRect(); //test drawing text in a rect - void createRenderContextFromComposition(); - void createRenderContextFromMap(); - - private: - bool renderCheck( const QString &testName, QImage &image, int mismatchCount = 0 ); - QgsComposition *mComposition = nullptr; - QString mReport; - QFont mTestFont; - -}; - -TestQgsComposerUtils::TestQgsComposerUtils() = default; - - -void TestQgsComposerUtils::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); //for access to test font - - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - - mReport = QStringLiteral( "

Composer Utils Tests

\n" ); - - QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Oblique" ) ); - mTestFont = QgsFontUtils::getStandardTestFont( QStringLiteral( "Oblique " ) ); - mTestFont.setItalic( true ); - -} - -void TestQgsComposerUtils::cleanupTestCase() -{ - delete mComposition; - - QgsApplication::exitQgis(); - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } -} - -void TestQgsComposerUtils::init() -{ - -} - -void TestQgsComposerUtils::cleanup() -{ - -} - -void TestQgsComposerUtils::drawArrowHead() -{ - //test drawing with no painter - QgsComposerUtils::drawArrowHead( 0, 100, 100, 90, 30 ); - - //test painting on to image - QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); - testImage.fill( qRgb( 152, 219, 249 ) ); - QPainter testPainter; - testPainter.begin( &testImage ); - QgsComposerUtils::drawArrowHead( &testPainter, 100, 100, 45, 30 ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawarrowhead", testImage, 40 ) ); -} - -void TestQgsComposerUtils::angle() -{ - //test angle with zero length line - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 1, 1 ) ), 0.0 ); - - //test angles to different quadrants - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 1, 2 ) ), 180.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 2, 2 ) ), 135.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 2, 1 ) ), 90.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 2, 0 ) ), 45.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 1, 0 ) ), 0.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 0, 0 ) ), 315.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 0, 1 ) ), 270.0 ); - QCOMPARE( QgsComposerUtils::angle( QPointF( 1, 1 ), QPointF( 0, 2 ) ), 225.0 ); -} - -void TestQgsComposerUtils::rotate() -{ - // pairs of lines from before -> expected after position and angle to rotate - QList< QPair< QLineF, double > > testVals; - testVals << qMakePair( QLineF( 0, 1, 0, 1 ), 0.0 ); - testVals << qMakePair( QLineF( 0, 1, -1, 0 ), 90.0 ); - testVals << qMakePair( QLineF( 0, 1, 0, -1 ), 180.0 ); - testVals << qMakePair( QLineF( 0, 1, 1, 0 ), 270.0 ); - testVals << qMakePair( QLineF( 0, 1, 0, 1 ), 360.0 ); - - //test rotate helper function - QList< QPair< QLineF, double > >::const_iterator it = testVals.constBegin(); - for ( ; it != testVals.constEnd(); ++it ) - { - double x = ( *it ).first.x1(); - double y = ( *it ).first.y1(); - QgsComposerUtils::rotate( ( *it ).second, x, y ); - QGSCOMPARENEAR( x, ( *it ).first.x2(), 4 * DBL_EPSILON ); - QGSCOMPARENEAR( y, ( *it ).first.y2(), 4 * DBL_EPSILON ); - } -} - -void TestQgsComposerUtils::normalizedAngle() -{ - QList< QPair< double, double > > testVals; - testVals << qMakePair( 0.0, 0.0 ); - testVals << qMakePair( 90.0, 90.0 ); - testVals << qMakePair( 180.0, 180.0 ); - testVals << qMakePair( 270.0, 270.0 ); - testVals << qMakePair( 360.0, 0.0 ); - testVals << qMakePair( 390.0, 30.0 ); - testVals << qMakePair( 720.0, 0.0 ); - testVals << qMakePair( 730.0, 10.0 ); - testVals << qMakePair( -10.0, 350.0 ); - testVals << qMakePair( -360.0, 0.0 ); - testVals << qMakePair( -370.0, 350.0 ); - testVals << qMakePair( -760.0, 320.0 ); - - //test normalized angle helper function - QList< QPair< double, double > >::const_iterator it = testVals.constBegin(); - for ( ; it != testVals.constEnd(); ++it ) - { - QGSCOMPARENEAR( QgsComposerUtils::normalizedAngle( ( *it ).first ), ( *it ).second, 4 * DBL_EPSILON ); - } -} - -void TestQgsComposerUtils::snappedAngle() -{ - QList< QPair< double, double > > testVals; - testVals << qMakePair( 0.0, 0.0 ); - testVals << qMakePair( 10.0, 0.0 ); - testVals << qMakePair( 20.0, 0.0 ); - testVals << qMakePair( 30.0, 45.0 ); - testVals << qMakePair( 40.0, 45.0 ); - testVals << qMakePair( 50.0, 45.0 ); - testVals << qMakePair( 60.0, 45.0 ); - testVals << qMakePair( 70.0, 90.0 ); - testVals << qMakePair( 80.0, 90.0 ); - testVals << qMakePair( 90.0, 90.0 ); - testVals << qMakePair( 100.0, 90.0 ); - testVals << qMakePair( 110.0, 90.0 ); - testVals << qMakePair( 120.0, 135.0 ); - testVals << qMakePair( 130.0, 135.0 ); - testVals << qMakePair( 140.0, 135.0 ); - testVals << qMakePair( 150.0, 135.0 ); - testVals << qMakePair( 160.0, 180.0 ); - testVals << qMakePair( 170.0, 180.0 ); - testVals << qMakePair( 180.0, 180.0 ); - testVals << qMakePair( 190.0, 180.0 ); - testVals << qMakePair( 200.0, 180.0 ); - testVals << qMakePair( 210.0, 225.0 ); - testVals << qMakePair( 220.0, 225.0 ); - testVals << qMakePair( 230.0, 225.0 ); - testVals << qMakePair( 240.0, 225.0 ); - testVals << qMakePair( 250.0, 270.0 ); - testVals << qMakePair( 260.0, 270.0 ); - testVals << qMakePair( 270.0, 270.0 ); - testVals << qMakePair( 280.0, 270.0 ); - testVals << qMakePair( 290.0, 270.0 ); - testVals << qMakePair( 300.0, 315.0 ); - testVals << qMakePair( 310.0, 315.0 ); - testVals << qMakePair( 320.0, 315.0 ); - testVals << qMakePair( 330.0, 315.0 ); - testVals << qMakePair( 340.0, 0.0 ); - testVals << qMakePair( 350.0, 0.0 ); - testVals << qMakePair( 360.0, 0.0 ); - - //test snapped angle helper function - QList< QPair< double, double > >::const_iterator it = testVals.constBegin(); - for ( ; it != testVals.constEnd(); ++it ) - { - QGSCOMPARENEAR( QgsComposerUtils::snappedAngle( ( *it ).first ), ( *it ).second, 4 * DBL_EPSILON ); - } -} - -void TestQgsComposerUtils::largestRotatedRect() -{ - QRectF wideRect = QRectF( 0, 0, 2, 1 ); - QRectF highRect = QRectF( 0, 0, 1, 2 ); - QRectF bounds = QRectF( 0, 0, 4, 2 ); - - //simple cases - //0 rotation - QRectF result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, 0 ); - QCOMPARE( result, QRectF( 0, 0, 4, 2 ) ); - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, 0 ); - QCOMPARE( result, QRectF( 1.5, 0, 1, 2 ) ); - // 90 rotation - result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, 90 ); - QCOMPARE( result, QRectF( 1.5, 0, 2, 1 ) ); - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, 90 ); - QCOMPARE( result, QRectF( 0, 0, 2, 4 ) ); - // 180 rotation - result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, 180 ); - QCOMPARE( result, QRectF( 0, 0, 4, 2 ) ); - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, 0 ); - QCOMPARE( result, QRectF( 1.5, 0, 1, 2 ) ); - // 270 rotation - result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, 270 ); - QCOMPARE( result, QRectF( 1.5, 0, 2, 1 ) ); - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, 270 ); - QCOMPARE( result, QRectF( 0, 0, 2, 4 ) ); - //360 rotation - result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, 360 ); - QCOMPARE( result, QRectF( 0, 0, 4, 2 ) ); - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, 360 ); - QCOMPARE( result, QRectF( 1.5, 0, 1, 2 ) ); - - //full test, run through a circle in 10 degree increments - for ( double rotation = 10; rotation < 360; rotation += 10 ) - { - result = QgsComposerUtils::largestRotatedRectWithinBounds( wideRect, bounds, rotation ); - QTransform t; - t.rotate( rotation ); - QRectF rotatedRectBounds = t.mapRect( result ); - //one of the rotated rects dimensions must equal the bounding rectangles dimensions (ie, it has been constrained by one dimension) - //and the other dimension must be less than or equal to bounds dimension - QVERIFY( ( qgsDoubleNear( rotatedRectBounds.width(), bounds.width(), 0.001 ) && ( rotatedRectBounds.height() <= bounds.height() ) ) - || ( qgsDoubleNear( rotatedRectBounds.height(), bounds.height(), 0.001 ) && ( rotatedRectBounds.width() <= bounds.width() ) ) ); - - //also verify that aspect ratio of rectangle has not changed - QGSCOMPARENEAR( result.width() / result.height(), wideRect.width() / wideRect.height(), 4 * DBL_EPSILON ); - } - //and again for the high rectangle - for ( double rotation = 10; rotation < 360; rotation += 10 ) - { - result = QgsComposerUtils::largestRotatedRectWithinBounds( highRect, bounds, rotation ); - QTransform t; - t.rotate( rotation ); - QRectF rotatedRectBounds = t.mapRect( result ); - //one of the rotated rects dimensions must equal the bounding rectangles dimensions (ie, it has been constrained by one dimension) - //and the other dimension must be less than or equal to bounds dimension - QVERIFY( ( qgsDoubleNear( rotatedRectBounds.width(), bounds.width(), 0.001 ) && ( rotatedRectBounds.height() <= bounds.height() ) ) - || ( qgsDoubleNear( rotatedRectBounds.height(), bounds.height(), 0.001 ) && ( rotatedRectBounds.width() <= bounds.width() ) ) ); - - //also verify that aspect ratio of rectangle has not changed - QGSCOMPARENEAR( result.width() / result.height(), highRect.width() / highRect.height(), 4 * DBL_EPSILON ); - } -} - -void TestQgsComposerUtils::pointsToMM() -{ - //test conversion of points to mm, based on 1 point = 1 / 72 of an inch - QGSCOMPARENEAR( QgsComposerUtils::pointsToMM( 72 / 25.4 ), 1, 0.001 ); -} - -void TestQgsComposerUtils::mmToPoints() -{ - //test conversion of mm to points, based on 1 point = 1 / 72 of an inch - QGSCOMPARENEAR( QgsComposerUtils::mmToPoints( 25.4 / 72 ), 1, 0.001 ); -} - -void TestQgsComposerUtils::relativePosition() -{ - //+ve gradient - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 0, 2, 0, 4 ), 2, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 0, 2, 0, 4 ), 0, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 0, 2, 0, 4 ), 4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 0, 2, 0, 4 ), 8, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 0, 2, 0, 4 ), -4, 0.001 ); - //-ve gradient - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 0, 2, 4, 0 ), 2, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 0, 2, 4, 0 ), 4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 0, 2, 4, 0 ), 0, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 0, 2, 4, 0 ), -4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 0, 2, 4, 0 ), 8, 0.001 ); - //-ve domain - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 2, 0, 0, 4 ), 2, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 2, 0, 0, 4 ), 4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 2, 0, 0, 4 ), 0, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 2, 0, 0, 4 ), -4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 2, 0, 0, 4 ), 8, 0.001 ); - //-ve domain and gradient - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 1, 2, 0, 4, 0 ), 2, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 0, 2, 0, 4, 0 ), 0, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 2, 2, 0, 4, 0 ), 4, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( 4, 2, 0, 4, 0 ), 8, 0.001 ); - QGSCOMPARENEAR( QgsComposerUtils::relativePosition( -2, 2, 0, 4, 0 ), -4, 0.001 ); -} - -void TestQgsComposerUtils::relativeResizeRect() -{ - //test rectangle which fills bounds - QRectF testRect = QRectF( 0, 0, 1, 1 ); - QRectF boundsBefore = QRectF( 0, 0, 1, 1 ); - QRectF boundsAfter = QRectF( 0, 0, 1, 1 ); - QgsComposerUtils::relativeResizeRect( testRect, boundsBefore, boundsAfter ); - QCOMPARE( testRect, QRectF( 0, 0, 1, 1 ) ); - testRect = QRectF( 0, 0, 1, 1 ); - boundsAfter = QRectF( 0, 0, 2, 2 ); - QgsComposerUtils::relativeResizeRect( testRect, boundsBefore, boundsAfter ); - QCOMPARE( testRect, QRectF( 0, 0, 2, 2 ) ); - testRect = QRectF( 0, 0, 1, 1 ); - boundsAfter = QRectF( 0, 0, 0.5, 4 ); - QgsComposerUtils::relativeResizeRect( testRect, boundsBefore, boundsAfter ); - QCOMPARE( testRect, QRectF( 0, 0, 0.5, 4 ) ); - - //test rectangle which doesn't fill bounds - testRect = QRectF( 1, 2, 1, 2 ); - boundsBefore = QRectF( 0, 0, 4, 8 ); - boundsAfter = QRectF( 0, 0, 2, 4 ); - QgsComposerUtils::relativeResizeRect( testRect, boundsBefore, boundsAfter ); - QCOMPARE( testRect, QRectF( 0.5, 1, 0.5, 1 ) ); -} - -void TestQgsComposerUtils::decodePaperOrientation() -{ - QgsComposition::PaperOrientation orientation; - bool ok = false; - orientation = QgsComposerUtils::decodePaperOrientation( QStringLiteral( "bad string" ), ok ); - QVERIFY( !ok ); - QCOMPARE( orientation, QgsComposition::Landscape ); //should default to landscape - ok = false; - orientation = QgsComposerUtils::decodePaperOrientation( QStringLiteral( "portrait" ), ok ); - QVERIFY( ok ); - QCOMPARE( orientation, QgsComposition::Portrait ); - ok = false; - orientation = QgsComposerUtils::decodePaperOrientation( QStringLiteral( "LANDSCAPE" ), ok ); - QVERIFY( ok ); - QCOMPARE( orientation, QgsComposition::Landscape ); -} - -void TestQgsComposerUtils::decodePaperSize() -{ - double width = 0; - double height = 0; - QVERIFY( ! QgsComposerUtils::decodePresetPaperSize( "bad string", width, height ) ); - - //good strings - QVERIFY( QgsComposerUtils::decodePresetPaperSize( "a4", width, height ) ); - QCOMPARE( width, 210.0 ); - QCOMPARE( height, 297.0 ); - QVERIFY( QgsComposerUtils::decodePresetPaperSize( "B0", width, height ) ); - QCOMPARE( width, 1000.0 ); - QCOMPARE( height, 1414.0 ); - QVERIFY( QgsComposerUtils::decodePresetPaperSize( "letter", width, height ) ); - QCOMPARE( width, 215.9 ); - QCOMPARE( height, 279.4 ); - QVERIFY( QgsComposerUtils::decodePresetPaperSize( "LEGAL", width, height ) ); - QCOMPARE( width, 215.9 ); - QCOMPARE( height, 355.6 ); -} - -void TestQgsComposerUtils::readOldDataDefinedProperty() -{ - //create a test dom element - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QDomElement itemElem = doc.createElement( QStringLiteral( "item" ) ); - - //dd element - QDomElement ddElem = doc.createElement( QStringLiteral( "dataDefinedTestProperty" ) ); - ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "true" ) ); - ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "true" ) ); - ddElem.setAttribute( QStringLiteral( "expr" ), QStringLiteral( "test expression" ) ); - ddElem.setAttribute( QStringLiteral( "field" ), QStringLiteral( "test field" ) ); - itemElem.appendChild( ddElem ); - rootNode.appendChild( itemElem ); - - //try reading dd elements - - //bad data defined properties - should not be read into dataDefinedProperties map - QVERIFY( !QgsComposerUtils::readOldDataDefinedProperty( QgsComposerObject::NoProperty, ddElem ) ); - QVERIFY( !QgsComposerUtils::readOldDataDefinedProperty( QgsComposerObject::AllProperties, ddElem ) ); - - //read into valid property - QgsProperty p( QgsComposerUtils::readOldDataDefinedProperty( QgsComposerObject::TestProperty, ddElem ) ); - QVERIFY( p ); - QVERIFY( p.isActive() ); - QCOMPARE( p.propertyType(), QgsProperty::ExpressionBasedProperty ); - QCOMPARE( p.expressionString(), QString( "test expression" ) ); - - ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "false" ) ); - p = QgsComposerUtils::readOldDataDefinedProperty( QgsComposerObject::TestProperty, ddElem ); - QVERIFY( p ); - QVERIFY( p.isActive() ); - QCOMPARE( p.propertyType(), QgsProperty::FieldBasedProperty ); - QCOMPARE( p.field(), QString( "test field" ) ); - - //reading false parameters - QDomElement ddElem2 = doc.createElement( QStringLiteral( "dataDefinedProperty2" ) ); - ddElem2.setAttribute( QStringLiteral( "active" ), QStringLiteral( "false" ) ); - itemElem.appendChild( ddElem2 ); - p = QgsComposerUtils::readOldDataDefinedProperty( QgsComposerObject::TestProperty, ddElem2 ); - QVERIFY( p ); - QVERIFY( !p.isActive() ); -} - -void TestQgsComposerUtils::readOldDataDefinedPropertyMap() -{ - //create a test dom element - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QDomElement itemElem = doc.createElement( QStringLiteral( "item" ) ); - - //dd elements - QDomElement ddElem = doc.createElement( QStringLiteral( "dataDefinedBlendMode" ) ); - ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "true" ) ); - ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "true" ) ); - ddElem.setAttribute( QStringLiteral( "expr" ), QStringLiteral( "test expression" ) ); - ddElem.setAttribute( QStringLiteral( "field" ), QStringLiteral( "test field" ) ); - itemElem.appendChild( ddElem ); - QDomElement ddElem2 = doc.createElement( QStringLiteral( "dataDefinedTransparency" ) ); - ddElem2.setAttribute( QStringLiteral( "active" ), QStringLiteral( "false" ) ); - ddElem2.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "false" ) ); - ddElem2.setAttribute( QStringLiteral( "expr" ), QStringLiteral( "test expression 2" ) ); - ddElem2.setAttribute( QStringLiteral( "field" ), QStringLiteral( "test field 2" ) ); - itemElem.appendChild( ddElem2 ); - QDomElement ddElem3 = doc.createElement( QStringLiteral( "dataDefinedProperty" ) ); - ddElem3.setAttribute( QStringLiteral( "active" ), QStringLiteral( "true" ) ); - ddElem3.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "false" ) ); - ddElem3.setAttribute( QStringLiteral( "expr" ), QStringLiteral( "test expression 3" ) ); - ddElem3.setAttribute( QStringLiteral( "field" ), QStringLiteral( "test field 3" ) ); - itemElem.appendChild( ddElem3 ); - rootNode.appendChild( itemElem ); - - //try reading dd elements - QgsPropertyCollection dataDefinedProperties; - - QgsComposerUtils::readOldDataDefinedPropertyMap( itemElem, dataDefinedProperties ); - //check returned values - QCOMPARE( dataDefinedProperties.count(), 3 ); - QVERIFY( ( dataDefinedProperties.property( QgsComposerObject::BlendMode ) ).isActive() ); - QCOMPARE( ( dataDefinedProperties.property( QgsComposerObject::BlendMode ) ).propertyType(), QgsProperty::ExpressionBasedProperty ); - QCOMPARE( dataDefinedProperties.property( QgsComposerObject::BlendMode ).expressionString(), QString( "test expression" ) ); - QVERIFY( !( dataDefinedProperties.property( QgsComposerObject::Opacity ) ).isActive() ); - QCOMPARE( ( dataDefinedProperties.property( QgsComposerObject::Opacity ) ).propertyType(), QgsProperty::FieldBasedProperty ); - QCOMPARE( dataDefinedProperties.property( QgsComposerObject::Opacity ).field(), QString( "test field 2" ) ); - QVERIFY( ( dataDefinedProperties.property( QgsComposerObject::TestProperty ) ).isActive() ); - QCOMPARE( ( dataDefinedProperties.property( QgsComposerObject::TestProperty ) ).propertyType(), QgsProperty::FieldBasedProperty ); - QCOMPARE( dataDefinedProperties.property( QgsComposerObject::TestProperty ).field(), QString( "test field 3" ) ); -} - -void TestQgsComposerUtils::scaledFontPixelSize() -{ - //create a 12 point test font - mTestFont.setPointSize( 12 ); - - //test scaling of font for painting - QFont scaledFont = QgsComposerUtils::scaledFontPixelSize( mTestFont ); - QCOMPARE( scaledFont.pixelSize(), 42 ); - QCOMPARE( scaledFont.family(), mTestFont.family() ); -} - -void TestQgsComposerUtils::fontAscentMM() -{ - mTestFont.setPointSize( 12 ); - //platform specific font rendering differences mean these tests need to be very leniant - QGSCOMPARENEAR( QgsComposerUtils::fontAscentMM( mTestFont ), 3.9, 0.5 ); -} - -void TestQgsComposerUtils::fontDescentMM() -{ - mTestFont.setPointSize( 12 ); - QGSCOMPARENEAR( QgsComposerUtils::fontDescentMM( mTestFont ), 0.9, 0.05 ); -} - -void TestQgsComposerUtils::fontHeightMM() -{ - mTestFont.setPointSize( 12 ); - //platform specific font rendering differences mean these tests need to be very leniant - QGSCOMPARENEAR( QgsComposerUtils::fontHeightMM( mTestFont ), 4.9, 0.5 ); -} - -void TestQgsComposerUtils::fontHeightCharacterMM() -{ - mTestFont.setPointSize( 12 ); - //platform specific font rendering differences mean these tests need to be very leniant - QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'a' ) ), 2.4, 0.15 ); - QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'l' ) ), 3.15, 0.16 ); - QGSCOMPARENEAR( QgsComposerUtils::fontHeightCharacterMM( mTestFont, QChar( 'g' ) ), 3.2, 0.11 ); -} - -void TestQgsComposerUtils::textWidthMM() -{ - //platform specific font rendering differences mean this test needs to be very leniant - mTestFont.setPointSize( 12 ); - QGSCOMPARENEAR( QgsComposerUtils::textWidthMM( mTestFont, QString( "test string" ) ), 20, 2 ); -} - -void TestQgsComposerUtils::textHeightMM() -{ - //platform specific font rendering differences mean this test needs to be very leniant - mTestFont.setPointSize( 12 ); - QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test string" ) ) ) ); - QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test string" ) ), 3.9, 0.2 ); - QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ) ) ) ); - QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ) ), 8.7, 0.2 ); - QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ), 2 ) ) ); - QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring" ), 2 ), 13.5, 0.2 ); - QgsDebugMsg( QString( "height: %1" ).arg( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring\nstring" ) ) ) ); - QGSCOMPARENEAR( QgsComposerUtils::textHeightMM( mTestFont, QString( "test\nstring\nstring" ) ), 13.5, 0.2 ); -} - -void TestQgsComposerUtils::drawTextPos() -{ - //test drawing with no painter - QgsComposerUtils::drawText( 0, QPointF( 5, 15 ), QStringLiteral( "Abc123" ), mTestFont ); - - //test drawing text on to image - mTestFont.setPointSize( 48 ); - QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); - testImage.fill( qRgb( 152, 219, 249 ) ); - QPainter testPainter; - testPainter.begin( &testImage ); - QgsComposerUtils::drawText( &testPainter, QPointF( 5, 15 ), QStringLiteral( "Abc123" ), mTestFont, Qt::white ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_pos", testImage, 100 ) ); - - //test drawing with pen color set on painter and no specified color - //text should be drawn using painter pen color - testImage.fill( qRgb( 152, 219, 249 ) ); - testPainter.begin( &testImage ); - testPainter.setPen( QPen( Qt::green ) ); - QgsComposerUtils::drawText( &testPainter, QPointF( 5, 15 ), QStringLiteral( "Abc123" ), mTestFont ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_posnocolor", testImage, 100 ) ); -} - -void TestQgsComposerUtils::drawTextRect() -{ - //test drawing with no painter - QgsComposerUtils::drawText( 0, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont ); - - //test drawing text on to image - mTestFont.setPointSize( 48 ); - QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); - testImage.fill( qRgb( 152, 219, 249 ) ); - QPainter testPainter; - testPainter.begin( &testImage ); - QgsComposerUtils::drawText( &testPainter, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont, Qt::white ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_rect", testImage, 100 ) ); - - //test drawing with pen color set on painter and no specified color - //text should be drawn using painter pen color - testImage.fill( qRgb( 152, 219, 249 ) ); - testPainter.begin( &testImage ); - testPainter.setPen( QPen( Qt::green ) ); - QgsComposerUtils::drawText( &testPainter, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_rectnocolor", testImage, 100 ) ); - - //test alignment settings - testImage.fill( qRgb( 152, 219, 249 ) ); - testPainter.begin( &testImage ); - QgsComposerUtils::drawText( &testPainter, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont, Qt::black, Qt::AlignRight, Qt::AlignBottom ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_rectalign", testImage, 100 ) ); - - //test extra flags - render without clipping - testImage.fill( qRgb( 152, 219, 249 ) ); - testPainter.begin( &testImage ); - QgsComposerUtils::drawText( &testPainter, QRectF( 5, 15, 20, 50 ), QStringLiteral( "Abc123" ), mTestFont, Qt::white, Qt::AlignLeft, Qt::AlignTop, Qt::TextDontClip ); - testPainter.end(); - QVERIFY( renderCheck( "composerutils_drawtext_rectflag", testImage, 100 ) ); -} - -void TestQgsComposerUtils::createRenderContextFromComposition() -{ - QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); - testImage.setDotsPerMeterX( 150 / 25.4 * 1000 ); - testImage.setDotsPerMeterY( 150 / 25.4 * 1000 ); - QPainter p( &testImage ); - - // no composition - QgsRenderContext rc = QgsComposerUtils::createRenderContextForComposition( nullptr, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QCOMPARE( rc.painter(), &p ); - - // no composition, no painter - rc = QgsComposerUtils::createRenderContextForComposition( nullptr, nullptr ); - QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); - QVERIFY( !rc.painter() ); - - //create composition with no reference map - QgsRectangle extent( 2000, 2800, 2500, 2900 ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - rc = QgsComposerUtils::createRenderContextForComposition( composition, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QCOMPARE( rc.painter(), &p ); - - // composition, no map, no painter - rc = QgsComposerUtils::createRenderContextForComposition( composition, nullptr ); - QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); - QVERIFY( !rc.painter() ); - - // add a reference map - QgsComposerMap *map = new QgsComposerMap( composition ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - composition->addComposerMap( map ); - composition->setReferenceMap( map ); - - rc = QgsComposerUtils::createRenderContextForComposition( composition, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); - QCOMPARE( rc.painter(), &p ); - - // composition, reference map, no painter - rc = QgsComposerUtils::createRenderContextForComposition( composition, nullptr ); - QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); - QVERIFY( !rc.painter() ); - - p.end(); -} - -void TestQgsComposerUtils::createRenderContextFromMap() -{ - QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); - testImage.setDotsPerMeterX( 150 / 25.4 * 1000 ); - testImage.setDotsPerMeterY( 150 / 25.4 * 1000 ); - QPainter p( &testImage ); - - // no map - QgsRenderContext rc = QgsComposerUtils::createRenderContextForMap( nullptr, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QCOMPARE( rc.painter(), &p ); - - // no map, no painter - rc = QgsComposerUtils::createRenderContextForMap( nullptr, nullptr ); - QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); - QVERIFY( !rc.painter() ); - - //create composition with no reference map - QgsRectangle extent( 2000, 2800, 2500, 2900 ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - // add a map - QgsComposerMap *map = new QgsComposerMap( composition ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - composition->addComposerMap( map ); - - rc = QgsComposerUtils::createRenderContextForMap( map, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); - QCOMPARE( rc.painter(), &p ); - - // map, no painter - rc = QgsComposerUtils::createRenderContextForMap( map, nullptr ); - QGSCOMPARENEAR( rc.scaleFactor(), 88 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); - QVERIFY( !rc.painter() ); - - // secondary map - QgsComposerMap *map2 = new QgsComposerMap( composition ); - map2->setNewExtent( extent ); - map2->setSceneRect( QRectF( 30, 60, 100, 50 ) ); - composition->addComposerMap( map2 ); - rc = QgsComposerUtils::createRenderContextForMap( map2, &p ); - QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); - QGSCOMPARENEAR( rc.rendererScale(), map2->scale(), 1000000 ); - QVERIFY( rc.painter() ); - - p.end(); -} - -bool TestQgsComposerUtils::renderCheck( const QString &testName, QImage &image, int mismatchCount ) -{ - mReport += "

" + testName + "

\n"; - QString myTmpDir = QDir::tempPath() + '/'; - QString myFileName = myTmpDir + testName + ".png"; - image.save( myFileName, "PNG" ); - QgsRenderChecker myChecker; - myChecker.setControlPathPrefix( QStringLiteral( "composer_utils" ) ); - myChecker.setControlName( "expected_" + testName ); - myChecker.setRenderedImage( myFileName ); - bool myResultFlag = myChecker.compareImages( testName, mismatchCount ); - mReport += myChecker.report(); - return myResultFlag; -} - -QGSTEST_MAIN( TestQgsComposerUtils ) -#include "testqgscomposerutils.moc" diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp deleted file mode 100644 index bd4c9d75a40..00000000000 --- a/tests/src/core/testqgscomposition.cpp +++ /dev/null @@ -1,1023 +0,0 @@ -/*************************************************************************** - testqgscomposition.cpp - ---------------------- - begin : September 2014 - copyright : (C) 2014 by Nyall Dawson - email : nyall dot dawson at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsapplication.h" -#include "qgscomposition.h" -#include "qgscomposerattributetablev2.h" -#include "qgscomposerlabel.h" -#include "qgscomposershape.h" -#include "qgscomposerarrow.h" -#include "qgscomposerhtml.h" -#include "qgscomposerframe.h" -#include "qgscomposermap.h" -#include "qgsmapsettings.h" -#include "qgsmultirenderchecker.h" -#include "qgsfillsymbollayer.h" -#include "qgsproject.h" -#include "qgscomposerlegend.h" -#include "qgsrasterlayer.h" -#include "qgsvectorlayer.h" -#include "qgslayertreegroup.h" -#include "qgslayertreelayer.h" -#include "qgslayertree.h" -#include "qgslayoutitemlegend.h" - -#include -#include "qgstest.h" - -class TestQgsComposition : public QObject -{ - Q_OBJECT - - public: - TestQgsComposition(); - - private slots: - void initTestCase();// will be called before the first testfunction is executed. - void cleanupTestCase();// will be called after the last testfunction was executed. - void init();// will be called before each testfunction is executed. - void cleanup();// will be called after every testfunction. - - void itemsOnPage(); //test fetching matching items on a set page - void shouldExportPage(); //test the shouldExportPage method - void pageIsEmpty(); //test the pageIsEmpty method - void customProperties(); - void writeRetrieveCustomProperties(); - void bounds(); - void resizeToContents(); - void resizeToContentsMargin(); - void resizeToContentsMultiPage(); - void georeference(); - void variablesEdited(); - void itemVariablesFunction(); - void referenceMap(); - void legendRestoredFromTemplate(); - void legendRestoredFromTemplateAutoUpdate(); - void attributeTableRestoredFromTemplate(); - void mapLayersRestoredFromTemplate(); - void mapLayersStyleOverrideRestoredFromTemplate(); - void atlasLayerRestoredFromTemplate(); - - private: - QgsComposition *mComposition = nullptr; - QString mReport; - -}; - -TestQgsComposition::TestQgsComposition() = default; - -void TestQgsComposition::initTestCase() -{ - QgsApplication::init(); - QgsApplication::initQgis(); - - //create composition - mComposition = new QgsComposition( QgsProject::instance() ); - mComposition->setPaperSize( 297, 210 ); //A4 landscape - mComposition->setNumPages( 3 ); - - mReport = QStringLiteral( "

Composition Tests

\n" ); - -} - -void TestQgsComposition::cleanupTestCase() -{ - delete mComposition; - - QString myReportFile = QDir::tempPath() + "/qgistest.html"; - QFile myFile( myReportFile ); - if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) - { - QTextStream myQTextStream( &myFile ); - myQTextStream << mReport; - myFile.close(); - } - QgsApplication::exitQgis(); -} - -void TestQgsComposition::init() -{ -} - -void TestQgsComposition::cleanup() -{ -} - -void TestQgsComposition::itemsOnPage() -{ - //add some items to the composition - QgsComposerLabel *label1 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label1 ); - label1->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerLabel *label2 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label2 ); - label2->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerLabel *label3 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label3 ); - label3->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 2 ); - QgsComposerShape *shape1 = new QgsComposerShape( mComposition ); - mComposition->addComposerShape( shape1 ); - shape1->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerShape *shape2 = new QgsComposerShape( mComposition ); - mComposition->addComposerShape( shape2 ); - shape2->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 2 ); - QgsComposerArrow *arrow1 = new QgsComposerArrow( mComposition ); - mComposition->addComposerArrow( arrow1 ); - arrow1->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 3 ); - QgsComposerArrow *arrow2 = new QgsComposerArrow( mComposition ); - mComposition->addComposerArrow( arrow2 ); - arrow2->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 3 ); - - //fetch items - remember that these numbers include the paper item! - QList items; - mComposition->composerItemsOnPage( items, 0 ); - //should be 4 items on page 1 - QCOMPARE( items.length(), 4 ); - mComposition->composerItemsOnPage( items, 1 ); - //should be 3 items on page 2 - QCOMPARE( items.length(), 3 ); - mComposition->composerItemsOnPage( items, 2 ); - //should be 3 items on page 3 - QCOMPARE( items.length(), 3 ); - - //check fetching specific item types - QList labels; - mComposition->composerItemsOnPage( labels, 0 ); - //should be 2 labels on page 1 - QCOMPARE( labels.length(), 2 ); - mComposition->composerItemsOnPage( labels, 1 ); - //should be 1 label on page 2 - QCOMPARE( labels.length(), 1 ); - mComposition->composerItemsOnPage( labels, 2 ); - //should be no label on page 3 - QCOMPARE( labels.length(), 0 ); - - QList shapes; - mComposition->composerItemsOnPage( shapes, 0 ); - //should be 1 shapes on page 1 - QCOMPARE( shapes.length(), 1 ); - mComposition->composerItemsOnPage( shapes, 1 ); - //should be 1 shapes on page 2 - QCOMPARE( shapes.length(), 1 ); - mComposition->composerItemsOnPage( shapes, 2 ); - //should be no shapes on page 3 - QCOMPARE( shapes.length(), 0 ); - - QList arrows; - mComposition->composerItemsOnPage( arrows, 0 ); - //should be no arrows on page 1 - QCOMPARE( arrows.length(), 0 ); - mComposition->composerItemsOnPage( arrows, 1 ); - //should be no arrows on page 2 - QCOMPARE( arrows.length(), 0 ); - mComposition->composerItemsOnPage( arrows, 2 ); - //should be 2 arrows on page 3 - QCOMPARE( arrows.length(), 2 ); - - mComposition->removeComposerItem( label1 ); - mComposition->removeComposerItem( label2 ); - mComposition->removeComposerItem( label3 ); - mComposition->removeComposerItem( shape1 ); - mComposition->removeComposerItem( shape2 ); - mComposition->removeComposerItem( arrow1 ); - mComposition->removeComposerItem( arrow2 ); - - //check again with removed items - mComposition->composerItemsOnPage( labels, 0 ); - QCOMPARE( labels.length(), 0 ); - mComposition->composerItemsOnPage( labels, 1 ); - QCOMPARE( labels.length(), 0 ); - mComposition->composerItemsOnPage( labels, 2 ); - QCOMPARE( labels.length(), 0 ); -} - -void TestQgsComposition::shouldExportPage() -{ - mComposition->setPaperSize( 297, 200 ); - mComposition->setNumPages( 2 ); - - QgsComposerHtml *htmlItem = new QgsComposerHtml( mComposition, false ); - //frame on page 1 - QgsComposerFrame *frame1 = new QgsComposerFrame( mComposition, htmlItem, 0, 0, 100, 100 ); - //frame on page 2 - QgsComposerFrame *frame2 = new QgsComposerFrame( mComposition, htmlItem, 0, 320, 100, 100 ); - frame2->setHidePageIfEmpty( true ); - htmlItem->addFrame( frame1 ); - htmlItem->addFrame( frame2 ); - htmlItem->setContentMode( QgsComposerHtml::ManualHtml ); - //short content, so frame 2 should be empty - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( mComposition->shouldExportPage( 1 ), true ); - QCOMPARE( mComposition->shouldExportPage( 2 ), false ); - - //long content, so frame 2 should not be empty - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( mComposition->shouldExportPage( 1 ), true ); - QCOMPARE( mComposition->shouldExportPage( 2 ), true ); - - //...and back again... - htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); - htmlItem->loadHtml(); - - QCOMPARE( mComposition->shouldExportPage( 1 ), true ); - QCOMPARE( mComposition->shouldExportPage( 2 ), false ); - - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; -} - -void TestQgsComposition::pageIsEmpty() -{ - //add some items to the composition - QgsComposerLabel *label1 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label1 ); - label1->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerLabel *label2 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label2 ); - label2->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerLabel *label3 = new QgsComposerLabel( mComposition ); - mComposition->addComposerLabel( label3 ); - label3->setItemPosition( 10, 10, 50, 50, QgsComposerItem::UpperLeft, false, 3 ); - - //only page 2 should be empty - QCOMPARE( mComposition->pageIsEmpty( 1 ), false ); - QCOMPARE( mComposition->pageIsEmpty( 2 ), true ); - QCOMPARE( mComposition->pageIsEmpty( 3 ), false ); - - //remove the items - mComposition->removeComposerItem( label1 ); - mComposition->removeComposerItem( label2 ); - mComposition->removeComposerItem( label3 ); - - //expect everything to be empty now - QCOMPARE( mComposition->pageIsEmpty( 1 ), true ); - QCOMPARE( mComposition->pageIsEmpty( 2 ), true ); - QCOMPARE( mComposition->pageIsEmpty( 3 ), true ); -} - - -void TestQgsComposition::customProperties() -{ - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - QCOMPARE( composition->customProperty( "noprop", "defaultval" ).toString(), QString( "defaultval" ) ); - QVERIFY( composition->customProperties().isEmpty() ); - composition->setCustomProperty( QStringLiteral( "testprop" ), "testval" ); - QCOMPARE( composition->customProperty( "testprop", "defaultval" ).toString(), QString( "testval" ) ); - QCOMPARE( composition->customProperties().length(), 1 ); - QCOMPARE( composition->customProperties().at( 0 ), QString( "testprop" ) ); - - //test no crash - composition->removeCustomProperty( QStringLiteral( "badprop" ) ); - - composition->removeCustomProperty( QStringLiteral( "testprop" ) ); - QVERIFY( composition->customProperties().isEmpty() ); - QCOMPARE( composition->customProperty( "noprop", "defaultval" ).toString(), QString( "defaultval" ) ); - - composition->setCustomProperty( QStringLiteral( "testprop1" ), "testval1" ); - composition->setCustomProperty( QStringLiteral( "testprop2" ), "testval2" ); - QStringList keys = composition->customProperties(); - QCOMPARE( keys.length(), 2 ); - QVERIFY( keys.contains( "testprop1" ) ); - QVERIFY( keys.contains( "testprop2" ) ); - - delete composition; -} - -void TestQgsComposition::writeRetrieveCustomProperties() -{ - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - composition->setCustomProperty( QStringLiteral( "testprop" ), "testval" ); - composition->setCustomProperty( QStringLiteral( "testprop2" ), 5 ); - - //test writing composition with custom properties - QDomImplementation DomImplementation; - QDomDocumentType documentType = - DomImplementation.createDocumentType( - QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); - QDomDocument doc( documentType ); - QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); - QVERIFY( composition->writeXml( rootNode, doc ) ); - - //check if composition node was written - QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "Composition" ) ); - QCOMPARE( evalNodeList.count(), 1 ); - QDomElement compositionElem = evalNodeList.at( 0 ).toElement(); - - //test reading node containing custom properties - QgsComposition *readComposition = new QgsComposition( QgsProject::instance() ); - QVERIFY( readComposition->readXml( compositionElem, doc ) ); - - //test retrieved custom properties - QCOMPARE( readComposition->customProperties().length(), 2 ); - QVERIFY( readComposition->customProperties().contains( QString( "testprop" ) ) ); - QVERIFY( readComposition->customProperties().contains( QString( "testprop2" ) ) ); - QCOMPARE( readComposition->customProperty( "testprop" ).toString(), QString( "testval" ) ); - QCOMPARE( readComposition->customProperty( "testprop2" ).toInt(), 5 ); - - delete composition; - delete readComposition; -} - -void TestQgsComposition::bounds() -{ - //add some items to a composition - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - QgsComposerShape *shape1 = new QgsComposerShape( composition ); - shape1->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape1 ); - shape1->setItemPosition( 90, 50, 90, 50, QgsComposerItem::UpperLeft, false, 1 ); - shape1->setItemRotation( 45 ); - QgsComposerShape *shape2 = new QgsComposerShape( composition ); - shape2->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape2 ); - shape2->setItemPosition( 100, 150, 110, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerShape *shape3 = new QgsComposerShape( composition ); - shape3->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape3 ); - shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 2 ); - QgsComposerShape *shape4 = new QgsComposerShape( composition ); - shape4->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape4 ); - shape4->setItemPosition( 10, 120, 50, 30, QgsComposerItem::UpperLeft, false, 2 ); - shape4->setVisibility( false ); - - //check bounds - QRectF compositionBounds = composition->compositionBounds( false ); - QGSCOMPARENEAR( compositionBounds.height(), 372.15, 0.01 ); - QGSCOMPARENEAR( compositionBounds.width(), 301.00, 0.01 ); - QGSCOMPARENEAR( compositionBounds.left(), -2, 0.01 ); - QGSCOMPARENEAR( compositionBounds.top(), -2, 0.01 ); - - QRectF compositionBoundsNoPage = composition->compositionBounds( true ); - QGSCOMPARENEAR( compositionBoundsNoPage.height(), 320.36, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.width(), 250.30, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.left(), 9.85, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.top(), 49.79, 0.01 ); - - QRectF page1Bounds = composition->pageItemBounds( 0, true ); - QGSCOMPARENEAR( page1Bounds.height(), 150.36, 0.01 ); - QGSCOMPARENEAR( page1Bounds.width(), 155.72, 0.01 ); - QGSCOMPARENEAR( page1Bounds.left(), 54.43, 0.01 ); - QGSCOMPARENEAR( page1Bounds.top(), 49.79, 0.01 ); - - QRectF page2Bounds = composition->pageItemBounds( 1, true ); - QGSCOMPARENEAR( page2Bounds.height(), 100.30, 0.01 ); - QGSCOMPARENEAR( page2Bounds.width(), 50.30, 0.01 ); - QGSCOMPARENEAR( page2Bounds.left(), 209.85, 0.01 ); - QGSCOMPARENEAR( page2Bounds.top(), 249.85, 0.01 ); - - QRectF page2BoundsWithHidden = composition->pageItemBounds( 1, false ); - QGSCOMPARENEAR( page2BoundsWithHidden.height(), 120.30, 0.01 ); - QGSCOMPARENEAR( page2BoundsWithHidden.width(), 250.30, 0.01 ); - QGSCOMPARENEAR( page2BoundsWithHidden.left(), 9.85, 0.01 ); - QGSCOMPARENEAR( page2BoundsWithHidden.top(), 249.85, 0.01 ); - - delete composition; -} - - -void TestQgsComposition::resizeToContents() -{ - //add some items to a composition - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::yellow ); - simpleFill->setStrokeColor( Qt::transparent ); - composition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - QgsComposerShape *shape1 = new QgsComposerShape( composition ); - shape1->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - shape1->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape1 ); - shape1->setItemPosition( 90, 50, 90, 50, QgsComposerItem::UpperLeft, false, 1 ); - shape1->setItemRotation( 45 ); - QgsComposerShape *shape2 = new QgsComposerShape( composition ); - shape2->setShapeType( QgsComposerShape::Rectangle ); - shape2->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape2 ); - shape2->setItemPosition( 100, 150, 110, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerShape *shape3 = new QgsComposerShape( composition ); - shape3->setShapeType( QgsComposerShape::Rectangle ); - shape3->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape3 ); - shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 1 ); - - //resize to contents, no margin - composition->resizePageToContents(); - - QgsCompositionChecker checker( QStringLiteral( "composition_bounds" ), composition ); - checker.setSize( QSize( 774, 641 ) ); - checker.setControlPathPrefix( QStringLiteral( "composition" ) ); - QVERIFY( checker.testComposition( mReport ) ); - - delete composition; -} - -void TestQgsComposition::resizeToContentsMargin() -{ - //resize to contents, with margin - - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::yellow ); - simpleFill->setStrokeColor( Qt::transparent ); - composition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - QgsComposerShape *shape1 = new QgsComposerShape( composition ); - shape1->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - shape1->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape1 ); - shape1->setItemPosition( 90, 50, 90, 50, QgsComposerItem::UpperLeft, false, 1 ); - shape1->setItemRotation( 45 ); - QgsComposerShape *shape2 = new QgsComposerShape( composition ); - shape2->setShapeType( QgsComposerShape::Rectangle ); - shape2->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape2 ); - shape2->setItemPosition( 100, 150, 110, 50, QgsComposerItem::UpperLeft, false, 1 ); - QgsComposerShape *shape3 = new QgsComposerShape( composition ); - shape3->setShapeType( QgsComposerShape::Rectangle ); - shape3->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape3 ); - shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 1 ); - - //resize to contents, with margin - composition->resizePageToContents( 30, 20, 50, 40 ); - - QgsCompositionChecker checker( QStringLiteral( "composition_bounds_margin" ), composition ); - checker.setSize( QSize( 1000, 942 ) ); - checker.setControlPathPrefix( QStringLiteral( "composition" ) ); - QVERIFY( checker.testComposition( mReport ) ); - - delete composition; -} - -void TestQgsComposition::resizeToContentsMultiPage() -{ - //resize to contents with multi-page composition, should result in a single page - - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); - QgsFillSymbol *fillSymbol = new QgsFillSymbol(); - fillSymbol->changeSymbolLayer( 0, simpleFill ); - simpleFill->setColor( Qt::yellow ); - simpleFill->setStrokeColor( Qt::transparent ); - composition->setPageStyleSymbol( fillSymbol ); - delete fillSymbol; - - composition->setNumPages( 3 ); - - QgsComposerShape *shape1 = new QgsComposerShape( composition ); - shape1->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - shape1->setShapeType( QgsComposerShape::Rectangle ); - composition->addComposerShape( shape1 ); - shape1->setItemPosition( 90, 50, 90, 50, QgsComposerItem::UpperLeft, false, 1 ); - shape1->setItemRotation( 45 ); - QgsComposerShape *shape2 = new QgsComposerShape( composition ); - shape2->setShapeType( QgsComposerShape::Rectangle ); - shape2->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape2 ); - shape2->setItemPosition( 100, 150, 110, 50, QgsComposerItem::UpperLeft, false, 2 ); - QgsComposerShape *shape3 = new QgsComposerShape( composition ); - shape3->setShapeType( QgsComposerShape::Rectangle ); - shape3->setBackgroundColor( QColor::fromRgb( 255, 150, 100 ) ); - composition->addComposerShape( shape3 ); - shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 3 ); - - //resize to contents, no margin - composition->resizePageToContents(); - - QCOMPARE( composition->numPages(), 1 ); - - QgsCompositionChecker checker( QStringLiteral( "composition_bounds_multipage" ), composition ); - checker.setSize( QSize( 394, 996 ) ); - checker.setControlPathPrefix( QStringLiteral( "composition" ) ); - QVERIFY( checker.testComposition( mReport ) ); - - delete composition; -} - -void TestQgsComposition::georeference() -{ - QgsRectangle extent( 2000, 2800, 2500, 2900 ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - // no map - double *t = composition->computeGeoTransform( nullptr ); - QVERIFY( !t ); - - QgsComposerMap *map = new QgsComposerMap( composition ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - composition->addComposerMap( map ); - - t = composition->computeGeoTransform( map ); - QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); - QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); - QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[3], 3200, 1 ); - QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); - delete[] t; - - // don't specify map - composition->setReferenceMap( map ); - t = composition->computeGeoTransform(); - QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); - QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); - QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[3], 3200, 1 ); - QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); - delete[] t; - - // specify extent - t = composition->computeGeoTransform( map, QRectF( 70, 100, 50, 60 ) ); - QGSCOMPARENEAR( t[0], 2100.0, 1.0 ); - QGSCOMPARENEAR( t[1], 0.211864, 0.0001 ); - QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[3], 2950, 1 ); - QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[5], -0.211864, 0.0001 ); - delete[] t; - - // specify dpi - t = composition->computeGeoTransform( map, QRectF(), 75 ); - QGSCOMPARENEAR( t[0], 1925.0, 1 ); - QGSCOMPARENEAR( t[1], 0.847603, 0.0001 ); - QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[3], 3200.0, 1 ); - QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); - QGSCOMPARENEAR( t[5], -0.846774, 0.0001 ); - delete[] t; - - // rotation - map->setMapRotation( 45 ); - t = composition->computeGeoTransform( map ); - QGSCOMPARENEAR( t[0], 1825.7, 1 ); - QGSCOMPARENEAR( t[1], 0.149708, 0.0001 ); - QGSCOMPARENEAR( t[2], 0.149708, 0.0001 ); - QGSCOMPARENEAR( t[3], 2889.64, 1 ); - QGSCOMPARENEAR( t[4], 0.14969, 0.0001 ); - QGSCOMPARENEAR( t[5], -0.14969, 0.0001 ); - delete[] t; - - delete composition; -} - -void TestQgsComposition::variablesEdited() -{ - QgsComposition c( QgsProject::instance() ); - QSignalSpy spyVariablesChanged( &c, SIGNAL( variablesChanged() ) ); - - c.setCustomProperty( QStringLiteral( "not a variable" ), "1" ); - QVERIFY( spyVariablesChanged.count() == 0 ); - c.setCustomProperty( QStringLiteral( "variableNames" ), "1" ); - QVERIFY( spyVariablesChanged.count() == 1 ); - c.setCustomProperty( QStringLiteral( "variableValues" ), "1" ); - QVERIFY( spyVariablesChanged.count() == 2 ); -} - -void TestQgsComposition::itemVariablesFunction() -{ - QgsRectangle extent( 2000, 2800, 2500, 2900 ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - QgsExpression e( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_scale' )" ) ); - // no map - QgsExpressionContext c = composition->createExpressionContext(); - QVariant r = e.evaluate( &c ); - QVERIFY( !r.isValid() ); - - QgsComposerMap *map = new QgsComposerMap( composition ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); - composition->addComposerMap( map ); - map->setId( QStringLiteral( "map_id" ) ); - - c = composition->createExpressionContext(); - r = e.evaluate( &c ); - QGSCOMPARENEAR( r.toDouble(), 1.38916e+08, 100 ); - - QgsExpression e2( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs' )" ) ); - r = e2.evaluate( &c ); - QCOMPARE( r.toString(), QString( "EPSG:4326" ) ); - - QgsExpression e3( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs_definition' )" ) ); - r = e3.evaluate( &c ); - QCOMPARE( r.toString(), QString( "+proj=longlat +datum=WGS84 +no_defs" ) ); - - QgsExpression e4( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_units' )" ) ); - r = e4.evaluate( &c ); - QCOMPARE( r.toString(), QString( "degrees" ) ); - - delete composition; -} - -void TestQgsComposition::referenceMap() -{ - QgsRectangle extent( 2000, 2800, 2500, 2900 ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - // no maps - QVERIFY( !composition->referenceMap() ); - - QgsComposerMap *map = new QgsComposerMap( composition ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - composition->addComposerMap( map ); - - QCOMPARE( composition->referenceMap(), map ); - - // add a larger map - QgsComposerMap *map2 = new QgsComposerMap( composition ); - map2->setNewExtent( extent ); - map2->setSceneRect( QRectF( 30, 60, 250, 150 ) ); - composition->addComposerMap( map2 ); - - QCOMPARE( composition->referenceMap(), map2 ); - - // explicitly set reference map - composition->setReferenceMap( map ); - QCOMPARE( composition->referenceMap(), map ); - - delete composition; -} - -void TestQgsComposition::legendRestoredFromTemplate() -{ - // load a layer - - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject p; - p.addMapLayer( layer ); - - // create composition - QgsComposition c( &p ); - // add a legend - QgsComposerLegend *legend = new QgsComposerLegend( &c ); - c.addComposerLegend( legend ); - legend->setAutoUpdateModel( false ); - - QgsLegendModel *model = legend->model(); - QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 ); - // make sure we've got right node - QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node ); - QVERIFY( layerNode ); - QCOMPARE( layerNode->layer(), layer ); - - // got it! - layerNode->setCustomProperty( QStringLiteral( "legend/title-label" ), QStringLiteral( "new title!" ) ); - // make sure new title stuck - QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - - // make a new composition from template - QgsComposition c2( &p ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // get legend from new composition - QList< QgsComposerLegend * > legends2; - c2.composerItems( legends2 ); - QgsComposerLegend *legend2 = legends2.at( 0 ); - QVERIFY( legend2 ); - - QgsLegendModel *model2 = legend2->model(); - QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 ); - QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 ); - QVERIFY( layerNode2 ); - QCOMPARE( layerNode2->layer(), layer ); - QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); - - QString oldId = layer->id(); - // new test - // remove existing layer - p.removeMapLayer( layer ); - - // reload it, with a new id - QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - p.addMapLayer( layer2 ); - QVERIFY( oldId != layer2->id() ); - - // load composition from template - QgsComposition c3( &p ); - QVERIFY( c3.loadFromTemplate( doc ) ); - // get legend from new composition - QList< QgsComposerLegend * > legends3; - c3.composerItems( legends3 ); - QgsComposerLegend *legend3 = legends3.at( 0 ); - QVERIFY( legend3 ); - - //make sure customisation remains intact - QgsLegendModel *model3 = legend3->model(); - QgsLayerTreeNode *node3 = model3->rootGroup()->children().at( 0 ); - QgsLayerTreeLayer *layerNode3 = dynamic_cast< QgsLayerTreeLayer * >( node3 ); - QVERIFY( layerNode3 ); - QCOMPARE( layerNode3->layer(), layer2 ); - QCOMPARE( model3->data( model->node2index( layerNode3 ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); -} - -void TestQgsComposition::legendRestoredFromTemplateAutoUpdate() -{ - // load a layer - - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject p; - p.addMapLayer( layer ); - - // create composition - QgsComposition c( &p ); - // add a legend - QgsComposerLegend *legend = new QgsComposerLegend( &c ); - c.addComposerLegend( legend ); - legend->setAutoUpdateModel( true ); - - QgsLegendModel *model = legend->model(); - QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 ); - // make sure we've got right node - QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node ); - QVERIFY( layerNode ); - QCOMPARE( layerNode->layer(), layer ); - QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "points" ) ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - //new project - QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject p2; - p2.addMapLayer( layer2 ); - - // make a new composition from template - QgsComposition c2( &p2 ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // get legend from new composition - QList< QgsComposerLegend * > legends2; - c2.composerItems( legends2 ); - QgsComposerLegend *legend2 = legends2.at( 0 ); - QVERIFY( legend2 ); - - QgsLegendModel *model2 = legend2->model(); - QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 ); - QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 ); - QVERIFY( layerNode2 ); - QCOMPARE( layerNode2->layer(), layer2 ); - QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "points" ) ); -} - -void TestQgsComposition::attributeTableRestoredFromTemplate() -{ - // load some layers - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsVectorLayer *layer2 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "memory" ), QStringLiteral( "memory" ) ); - QgsProject p; - p.addMapLayer( layer2 ); - p.addMapLayer( layer ); - - // create composition - QgsComposition c( &p ); - // add an attribute table - QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( &c, false ); - c.addMultiFrame( table ); - table->setVectorLayer( layer ); - QgsComposerFrame *frame = new QgsComposerFrame( &c, table, 1, 1, 10, 10 ); - c.addComposerTableFrame( table, frame ); - table->addFrame( frame ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - // new project - QgsProject p2; - QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsVectorLayer *layer4 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "memory" ), QStringLiteral( "memory" ) ); - p2.addMapLayer( layer4 ); - p2.addMapLayer( layer3 ); - - // make a new composition from template - QgsComposition c2( &p2 ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // get table from new composition - QList< QgsComposerFrame * > frames2; - c2.composerItems( frames2 ); - QgsComposerAttributeTableV2 *table2 = static_cast< QgsComposerAttributeTableV2 *>( frames2.at( 0 )->multiFrame() ); - QVERIFY( table2 ); - - QCOMPARE( table2->vectorLayer(), layer3 ); -} - -void TestQgsComposition::mapLayersRestoredFromTemplate() -{ - // load some layers - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QFileInfo vectorFileInfo2( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ); - QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(), - vectorFileInfo2.completeBaseName(), - QStringLiteral( "ogr" ) ); - QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif" ); - QgsRasterLayer *rl = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - - QgsProject p; - p.addMapLayer( layer2 ); - p.addMapLayer( layer ); - p.addMapLayer( rl ); - - // create composition - QgsComposition c( &p ); - // add a map - QgsComposerMap *map = new QgsComposerMap( &c, 1, 1, 10, 10 ); - c.addComposerMap( map ); - map->setLayers( QList() << layer << layer2 << rl ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - // new project - QgsProject p2; - QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(), - vectorFileInfo2.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsRasterLayer *rl5 = new QgsRasterLayer( rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName() ); - p2.addMapLayer( layer4 ); - p2.addMapLayer( layer3 ); - p2.addMapLayer( rl5 ); - - // make a new composition from template - QgsComposition c2( &p2 ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // get map from new composition - QList< QgsComposerMap * > maps; - c2.composerItems( maps ); - QgsComposerMap *map2 = static_cast< QgsComposerMap *>( maps.at( 0 ) ); - QVERIFY( map2 ); - - QCOMPARE( map2->layers(), QList() << layer3 << layer4 << rl5 ); -} - -void TestQgsComposition::mapLayersStyleOverrideRestoredFromTemplate() -{ - // load some layers - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QFileInfo vectorFileInfo2( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ); - QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(), - vectorFileInfo2.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject p; - p.addMapLayer( layer2 ); - p.addMapLayer( layer ); - - // create composition - QgsComposition c( &p ); - // add a map - QgsComposerMap *map = new QgsComposerMap( &c, 1, 1, 10, 10 ); - c.addComposerMap( map ); - map->setKeepLayerStyles( true ); - QgsStringMap styles; - // just close your eyes and pretend these are real styles - styles.insert( layer->id(), QStringLiteral( "xxxxx" ) ); - styles.insert( layer2->id(), QStringLiteral( "yyyyy" ) ); - map->setLayerStyleOverrides( styles ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - // new project - QgsProject p2; - QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(), - vectorFileInfo2.completeBaseName(), - QStringLiteral( "ogr" ) ); - p2.addMapLayer( layer4 ); - p2.addMapLayer( layer3 ); - - // make a new composition from template - QgsComposition c2( &p2 ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // get map from new composition - QList< QgsComposerMap * > maps; - c2.composerItems( maps ); - QgsComposerMap *map2 = static_cast< QgsComposerMap *>( maps.at( 0 ) ); - QVERIFY( map2 ); - QVERIFY( map2->keepLayerStyles() ); - - QgsStringMap restoredStyles = map2->layerStyleOverrides(); - QVERIFY( restoredStyles.contains( layer3->id() ) ); - QCOMPARE( restoredStyles.value( layer3->id() ).trimmed(), QStringLiteral( "xxxxx" ) ); - QVERIFY( restoredStyles.contains( layer4->id() ) ); - QCOMPARE( restoredStyles.value( layer4->id() ).trimmed(), QStringLiteral( "yyyyy" ) ); -} - -void TestQgsComposition::atlasLayerRestoredFromTemplate() -{ - // load some layers - QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); - QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - QgsProject p; - p.addMapLayer( layer ); - - // create composition - QgsComposition c( &p ); - // set atlas layer - c.atlasComposition().setEnabled( true ); - c.atlasComposition().setCoverageLayer( layer ); - - // save composition to template - QDomDocument doc; - QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) ); - doc.appendChild( composerElem ); - c.writeXml( composerElem, doc ); - c.atlasComposition().writeXml( composerElem, doc ); - - // new project - QgsProject p2; - QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), - vectorFileInfo.completeBaseName(), - QStringLiteral( "ogr" ) ); - p2.addMapLayer( layer2 ); - - // make a new composition from template - QgsComposition c2( &p2 ); - QVERIFY( c2.loadFromTemplate( doc ) ); - // check atlas layer - QCOMPARE( c2.atlasComposition().coverageLayer(), layer2 ); -} - -QGSTEST_MAIN( TestQgsComposition ) -#include "testqgscomposition.moc" diff --git a/tests/src/core/testqgscompositionconverter.cpp b/tests/src/core/testqgscompositionconverter.cpp new file mode 100644 index 00000000000..7528d59ef5f --- /dev/null +++ b/tests/src/core/testqgscompositionconverter.cpp @@ -0,0 +1,672 @@ +/*************************************************************************** + TestQgsCompositionConverter.cpp + ----------------- + begin : December 2017 + copyright : (C) 2017 by Alessandro Pasotti + email : elpaso at itopen dot it + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include "qgstest.h" +#include "qgsprintlayout.h" +#include "qgscompositionconverter.h" +#include "qgsproject.h" +#include "qgsreadwritecontext.h" +#include "qgslayoutexporter.h" +#include "qgsmultirenderchecker.h" +#include "qgssettings.h" + +#include "qgsmultibandcolorrenderer.h" +#include "qgsrasterlayer.h" + +#include "qgslayoutmanager.h" +#include "qgslayoutpagecollection.h" +#include "qgslayoutitemlabel.h" +#include "qgslayoutitemshape.h" +#include "qgslayoutitempicture.h" +#include "qgslayoutitempolygon.h" +#include "qgslayoutitempolyline.h" +#include "qgslayoutitemmap.h" +#include "qgslayoutitemscalebar.h" +#include "qgslayoutitemlegend.h" +#include "qgslayoutatlas.h" +#include "qgslayoutitemhtml.h" +#include "qgslayoutitemattributetable.h" + + +class TestQgsCompositionConverter: public QObject +{ + Q_OBJECT + + + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init();// will be called before each testfunction is executed. + void cleanup();// will be called after every testfunction. + + + /** + * Test import legend from a composer template + */ + void importComposerTemplateLegend(); + + /** + * Test import attribute table from a composer template + */ + void importComposerTemplateAttributeTable(); + + /** + * Test import HTML from a composer template + */ + void importComposerTemplateHtml(); + + /** + * Test import label from a composer template + */ + void importComposerTemplateLabel(); + + /** + * Test import shape from a composer template + */ + void importComposerTemplateShape(); + + /** + * Test import pictures from a composer template + */ + void importComposerTemplatePicture(); + + /** + * Test import polygon from a composer template + */ + void importComposerTemplatePolygon(); + + /** + * Test import polyline from a composer template + */ + void importComposerTemplatePolyline(); + + /** + * Test import arrow from a composer template + */ + void importComposerTemplateArrow(); + + /** + * Test import map from a composer template + */ + void importComposerTemplateMap(); + + /** + * Test import scalebar from a composer template + */ + void importComposerTemplateScaleBar(); + + /** + * Test import multiple elements from a composer template + */ + void importComposerTemplate(); + + /** + * Test import atlas from a composer template + */ + void importComposerAtlas(); + + /** + * Test automatic conversion from a composer template + */ + void convertComposerTemplate(); + + + private: + + void checkRenderedImage( QgsLayout *layout, const QString testName, const int pageNumber = 0 ); + + QDomElement loadComposer( const QString name ); + + QString mReport; + +}; + +void TestQgsCompositionConverter::initTestCase() +{ + QgsApplication::init(); + QgsApplication::initQgis(); + mReport = QStringLiteral( "

Layout Tests

\n" ); + QgsSettings settings; + settings.setValue( QStringLiteral( "svg/searchPathsForSVG" ), QStringLiteral( TEST_DATA_DIR ) ) ; +} + +void TestQgsCompositionConverter::cleanupTestCase() +{ + QString myReportFile = QDir::tempPath() + QDir::separator() + "qgistest.html"; + QFile myFile( myReportFile ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) + { + QTextStream myQTextStream( &myFile ); + myQTextStream << mReport; + myFile.close(); + } +} + +void TestQgsCompositionConverter::init() +{ + +} + +void TestQgsCompositionConverter::cleanup() +{ + +} + + +void TestQgsCompositionConverter::importComposerTemplateLabel() +{ + QDomElement composerElem( loadComposer( "2x_template_label.qpt" ) ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QVERIFY( items.size() > 0 ); + + // Check the label + const QgsLayoutItemLabel *label = items.at( 0 ); + QVERIFY( label ); + QCOMPARE( label->text(), QStringLiteral( "QGIS" ) ); + QCOMPARE( label->pos().x(), 55.5333 ); + QCOMPARE( label->pos().y(), 35.3929 ); + QCOMPARE( label->sizeWithUnits().width(), 15.3686 ); + QCOMPARE( label->sizeWithUnits().height(), 7.93747 ); + QCOMPARE( label->referencePoint(), QgsLayoutItem::ReferencePoint::UpperRight ); + QCOMPARE( label->frameStrokeColor(), QColor( 251, 0, 0, 255 ) ); + QCOMPARE( label->frameStrokeWidth().length(), 0.2 ); + QCOMPARE( ( int )label->rotation(), 4 ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); +} + +void TestQgsCompositionConverter::importComposerTemplateShape() +{ + QDomElement composerElem( loadComposer( "2x_template_shape.qpt" ) ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QVERIFY( items.size() > 0 ); + + // Check the shape + const QgsLayoutItemShape *shape = items.at( 0 ); + QCOMPARE( shape->pos().x(), 261.132 ); + QCOMPARE( shape->pos().y(), 83.1791 ); + QCOMPARE( shape->sizeWithUnits().width(), 12.0988 ); + QCOMPARE( shape->sizeWithUnits().height(), 33.2716 ); + QCOMPARE( shape->sizeWithUnits().units(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ); + QCOMPARE( shape->referencePoint(), QgsLayoutItem::ReferencePoint::MiddleRight ); + QCOMPARE( shape->frameStrokeColor(), QColor( 0, 0, 0, 255 ) ); + QCOMPARE( shape->frameStrokeWidth().length(), 0.3 ); + QCOMPARE( shape->backgroundColor(), QColor( 255, 255, 255, 255 ) ); + QCOMPARE( ( int )shape->rotation(), 0 ); + QCOMPARE( shape->frameEnabled(), false ); + QCOMPARE( shape->hasBackground(), false ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); +} + +void TestQgsCompositionConverter::importComposerTemplatePicture() +{ + QDomElement composerElem( loadComposer( "2x_template_pictures.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + QVERIFY( QFile( items.at( 0 )->picturePath() ).exists() ); + + QgsLayoutItemPicture *item = items.at( 0 ); + QCOMPARE( item->mPictureHeight, 18.1796 ); + QCOMPARE( item->mPictureWidth, 18.1796 ); + QCOMPARE( item->sizeWithUnits().width(), 25.7099 ); + QCOMPARE( item->sizeWithUnits().height(), 30.7511 ); + QCOMPARE( item->pos().x(), 207.192 ); + QCOMPARE( item->pos().y(), 12.6029 ); + QVERIFY( item->isVisible() ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + +void TestQgsCompositionConverter::importComposerTemplatePolygon() +{ + QDomElement composerElem( loadComposer( "2x_template_polygon.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + + QgsLayoutItemPolygon *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + QCOMPARE( item->nodes().count(), 7 ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + +void TestQgsCompositionConverter::importComposerTemplatePolyline() +{ + QDomElement composerElem( loadComposer( "2x_template_polyline.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + + QgsLayoutItemPolyline *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + QCOMPARE( item->nodes().count(), 4 ); + QCOMPARE( item->startMarker(), QgsLayoutItemPolyline::MarkerMode::NoMarker ); + QCOMPARE( item->endMarker(), QgsLayoutItemPolyline::MarkerMode::NoMarker ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + +void TestQgsCompositionConverter::importComposerTemplateArrow() +{ + QDomElement composerElem( loadComposer( "2x_template_arrow.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + + QgsLayoutItemPolyline *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + QCOMPARE( item->nodes().count(), 2 ); + QCOMPARE( item->startMarker(), QgsLayoutItemPolyline::MarkerMode::NoMarker ); + QCOMPARE( item->endMarker(), QgsLayoutItemPolyline::MarkerMode::ArrowHead ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + + +void TestQgsCompositionConverter::importComposerTemplateMap() +{ + QDomElement composerElem( loadComposer( "2x_template_map_overview.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 2 ); + + QgsLayoutItemMap *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + + item->setLayers( project.mapLayers().values() ); + + for ( auto const &l : project.mapLayers().values() ) + { + QVERIFY( l->isValid() ); + } + + QgsLayoutItemMap *item1 = items.at( 1 ); + QVERIFY( item1->isVisible() ); + QCOMPARE( item1->opacity(), 0.78 ); + item1->setLayers( project.mapLayers().values() ); + item1->setExtent( QgsRectangle( -126.5731570061082038, -4.69162199770811128, -88.56641716083402116, 69.08616711370645191 ) ); + + // Check map ids + QStringList mapUuids; + QList mapItems; + layout->layoutItems( mapItems ); + for ( auto const &item : mapItems ) + { + mapUuids << item->uuid(); + } + + { + int count = 0; + QList items; + layout->layoutItems( items ); + for ( auto const &mapItem : items ) + { + for ( auto const &item : mapItem->overviews()->asList() ) + { + if ( ! item->map( )->uuid().isEmpty( ) ) + { + QVERIFY( mapUuids.contains( item->map()->uuid() ) ); + count ++; + } + } + } + // We have at least one item linked to a map for this test + QVERIFY( count > 0 ); + } + + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + +void TestQgsCompositionConverter::importComposerTemplateLegend() +{ + QDomElement composerElem( loadComposer( "2x_template_legend.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + + QgsLayoutItemLegend *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + QVERIFY( ! item->autoUpdateModel() ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + +void TestQgsCompositionConverter::importComposerTemplateAttributeTable() +{ + QDomElement composerElem( loadComposer( "2x_template_attributetable.qpt" ) ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + // Check the table + QList items; + layout->layoutObjects( items ); + QVERIFY( items.size() > 0 ); + const QgsLayoutItemAttributeTable *table = items.at( 0 ); + QVERIFY( table ); + QVERIFY( table->sourceLayer() ); + QVERIFY( table->sourceLayer()->isValid() ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + +} + +void TestQgsCompositionConverter::importComposerTemplateHtml() +{ + QDomElement composerElem( loadComposer( "2x_template_html.qpt" ) ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 7 ); + + // Check the HTML + QList items; + layout->layoutObjects( items ); + QVERIFY( items.size() > 0 ); + const QgsLayoutItemHtml *html = items.at( 0 ); + QVERIFY( html ); + QCOMPARE( html->contentMode(), QgsLayoutItemHtml::ContentMode::ManualHtml ); + QCOMPARE( html->html(), QStringLiteral( "
aaaaA
\t\n" ) ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + +} + +void TestQgsCompositionConverter::importComposerTemplateScaleBar() +{ + QDomElement composerElem( loadComposer( "2x_template_scalebar.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + + QList items; + layout->layoutItems( items ); + QCOMPARE( items.size(), 1 ); + + QgsLayoutItemScaleBar *item = items.at( 0 ); + QVERIFY( item->isVisible() ); + + QVERIFY( ! item->linkedMap() ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + + qDeleteAll( items ); + +} + + +void TestQgsCompositionConverter::convertComposerTemplate() +{ + + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + + QgsPrintLayout *layout = dynamic_cast( project.layoutManager()->layouts().first() ); + + QVERIFY( layout ); + QCOMPARE( layout->pageCollection()->pageCount(), 2 ); + QCOMPARE( layout->name(), QStringLiteral( "composer title" ) ); + + // Check guides + QCOMPARE( layout->guides().rowCount( QModelIndex() ), 8 ); + +} + +void TestQgsCompositionConverter::importComposerTemplate() +{ + QDomElement composerElem( loadComposer( "2x_template.qpt" ) ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 2 ); + QCOMPARE( layout->name(), QStringLiteral( "composer title" ) ); + + // Check map ids + QStringList mapUuids; + QList mapItems; + layout->layoutItems( mapItems ); + for ( auto const &item : mapItems ) + { + mapUuids << item->uuid(); + } + + // Check that picture elements with a map id point to a valid map uuid + { + int count = 0; + QList items; + layout->layoutItems( items ); + for ( auto const &item : items ) + { + if ( item->linkedMap() ) + { + QVERIFY( mapUuids.contains( item->linkedMap()->uuid() ) ); + count ++; + } + } + // We have at least one item linked to a map for this test + QVERIFY( count > 0 ); + } + + + // Check that elements with a map id point to a valid map uuid + { + int count = 0; + QList items; + layout->layoutItems( items ); + for ( auto const &item : items ) + { + if ( item->linkedMap() ) + { + QVERIFY( mapUuids.contains( item->linkedMap()->uuid() ) ); + count ++; + } + } + // We have at least one item linked to a map for this test + QVERIFY( count > 0 ); + } + + // Check that elements with a map id point to a valid map uuid + { + int count = 0; + QList items; + layout->layoutItems( items ); + for ( auto const &item : items ) + { + if ( item->linkedMap( ) ) + { + QVERIFY( mapUuids.contains( item->linkedMap()->uuid() ) ); + count ++; + } + } + // We have at least one item linked to a map for this test + QVERIFY( count > 0 ); + } + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 1 ); + +} + +void TestQgsCompositionConverter::importComposerAtlas() +{ + QDomElement composerElem( loadComposer( "2x_template_atlas.qpt" ) ); + QVERIFY( !composerElem.isNull() ); + QVERIFY( !composerElem.attribute( QStringLiteral( "title" ) ).isEmpty() ); + QgsProject project; + project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" ); + QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement(); + + std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) ); + QVERIFY( layout.get() ); + QCOMPARE( layout->pageCollection()->pageCount(), 1 ); + QCOMPARE( layout->name(), QStringLiteral( "composer atlas" ) ); + + QVERIFY( layout->atlas()->enabled() ); + + checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 ); + +} + +void TestQgsCompositionConverter::checkRenderedImage( QgsLayout *layout, const QString testName, const int pageNumber ) +{ + QgsLayoutChecker checker( testName + "_" + QString::number( pageNumber ), layout ); + QSize size( layout->pageCollection()->page( pageNumber )->sizeWithUnits().width() * 3.77, layout->pageCollection()->page( pageNumber )->sizeWithUnits().height() * 3.77 ); + checker.setSize( size ); + checker.setControlPathPrefix( QStringLiteral( "compositionconverter" ) ); + QVERIFY( checker.testLayout( mReport, pageNumber, 0, true ) ); +} + + +QDomElement TestQgsCompositionConverter::loadComposer( const QString name ) +{ + QString templatePath( QStringLiteral( TEST_DATA_DIR ) + "/layouts/" + name ); + QDomDocument doc( "mydocument" ); + QFile file( templatePath ); + file.open( QIODevice::ReadOnly ); + doc.setContent( &file ); + file.close(); + QDomNodeList nodes( doc.elementsByTagName( QStringLiteral( "Composer" ) ) ); + if ( nodes.length() > 0 ) + return nodes.at( 0 ).toElement(); + else + { + QDomElement elem; + return elem; + } +} + + +QGSTEST_MAIN( TestQgsCompositionConverter ) +#include "testqgscompositionconverter.moc" diff --git a/tests/src/core/testqgscoordinatereferencesystem.cpp b/tests/src/core/testqgscoordinatereferencesystem.cpp index bc00efa844e..10094425f4a 100644 --- a/tests/src/core/testqgscoordinatereferencesystem.cpp +++ b/tests/src/core/testqgscoordinatereferencesystem.cpp @@ -62,8 +62,7 @@ class TestQgsCoordinateReferenceSystem: public QObject void equality(); void noEquality(); void equalityInvalid(); - void readXml(); - void writeXml(); + void readWriteXml(); void setCustomSrsValidation(); void customSrsValidation(); void postgisSrid(); @@ -80,6 +79,7 @@ class TestQgsCoordinateReferenceSystem: public QObject void bounds(); void saveAsUserCrs(); void projectWithCustomCrs(); + void projectEPSG25833(); private: void debugPrint( QgsCoordinateReferenceSystem &crs ); @@ -600,18 +600,38 @@ void TestQgsCoordinateReferenceSystem::equalityInvalid() QgsCoordinateReferenceSystem invalidCrs2; QVERIFY( invalidCrs1 == invalidCrs2 ); } -void TestQgsCoordinateReferenceSystem::readXml() +void TestQgsCoordinateReferenceSystem::readWriteXml() { - //QgsCoordinateReferenceSystem myCrs; - //myCrs.createFromSrid( GEOSRID ); - //QgsCoordinateReferenceSystem myCrs2; - //QVERIFY( myCrs2.readXml( QDomNode & node ) ); -} -void TestQgsCoordinateReferenceSystem::writeXml() -{ - //QgsCoordinateReferenceSystem myCrs; - //bool writeXml( QDomNode & node, QDomDocument & doc ) const; - //QVERIFY( myCrs.isValid() ); + QgsCoordinateReferenceSystem myCrs; + myCrs.createFromSrid( GEOSRID ); + QVERIFY( myCrs.isValid() ); + QDomDocument document( "test" ); + QDomElement node = document.createElement( QStringLiteral( "crs" ) ); + document.appendChild( node ); + QVERIFY( myCrs.writeXml( node, document ) ); + QgsCoordinateReferenceSystem myCrs2; + QVERIFY( myCrs2.readXml( node ) ); + QVERIFY( myCrs == myCrs2 ); + + // Empty XML made from writeXml operation + QgsCoordinateReferenceSystem myCrs3; + QDomDocument document2( "test" ); + QDomElement node2 = document2.createElement( QStringLiteral( "crs" ) ); + document2.appendChild( node2 ); + QVERIFY( ! myCrs3.isValid() ); + QVERIFY( myCrs3.writeXml( node2, document2 ) ); + QgsCoordinateReferenceSystem myCrs4; + QVERIFY( myCrs4.readXml( node2 ) ); + QVERIFY( ! myCrs4.isValid() ); + QVERIFY( myCrs3 == myCrs4 ); + + // Empty XML node + QDomDocument document3( "test" ); + QDomElement node3 = document3.createElement( QStringLiteral( "crs" ) ); + document3.appendChild( node3 ); + QgsCoordinateReferenceSystem myCrs5; + QVERIFY( ! myCrs5.readXml( node3 ) ); + QVERIFY( myCrs5 == QgsCoordinateReferenceSystem() ); } void TestQgsCoordinateReferenceSystem::setCustomSrsValidation() { @@ -842,6 +862,14 @@ void TestQgsCoordinateReferenceSystem::saveAsUserCrs() QCOMPARE( userCrs2.srsid(), userCrs.srsid() ); QCOMPARE( userCrs2.authid(), QStringLiteral( "USER:100000" ) ); QCOMPARE( userCrs2.description(), QStringLiteral( "babies first projection" ) ); + + // createFromString with user crs + QgsCoordinateReferenceSystem userCrs3; + userCrs3.createFromString( QStringLiteral( "USER:100000" ) ); + QVERIFY( userCrs3.isValid() ); + QCOMPARE( userCrs3.authid(), QString( "USER:100000" ) ); + QCOMPARE( userCrs3.toProj4(), madeUpProjection ); + QCOMPARE( userCrs3.description(), QStringLiteral( "babies first projection" ) ); } void TestQgsCoordinateReferenceSystem::projectWithCustomCrs() @@ -855,5 +883,15 @@ void TestQgsCoordinateReferenceSystem::projectWithCustomCrs() QCOMPARE( spyCrsChanged.count(), 1 ); } +void TestQgsCoordinateReferenceSystem::projectEPSG25833() +{ + // tests loading a 2.x project with a predefined EPSG that has non unique proj.4 string + QgsProject p; + QSignalSpy spyCrsChanged( &p, &QgsProject::crsChanged ); + QVERIFY( p.read( TEST_DATA_DIR + QStringLiteral( "/projects/epsg25833.qgs" ) ) ); + QVERIFY( p.crs().isValid() ); + QVERIFY( p.crs().authid() == QStringLiteral( "EPSG:25833" ) ); + QCOMPARE( spyCrsChanged.count(), 1 ); +} QGSTEST_MAIN( TestQgsCoordinateReferenceSystem ) #include "testqgscoordinatereferencesystem.moc" diff --git a/tests/src/core/testqgscoordinatetransform.cpp b/tests/src/core/testqgscoordinatetransform.cpp index b9d554b4230..056842bc434 100644 --- a/tests/src/core/testqgscoordinatetransform.cpp +++ b/tests/src/core/testqgscoordinatetransform.cpp @@ -17,6 +17,8 @@ #include "qgscoordinatetransform.h" #include "qgsapplication.h" #include "qgsrectangle.h" +#include "qgscoordinatetransformcontext.h" +#include "qgsproject.h" #include #include "qgstest.h" #include "qgsexception.h" @@ -33,6 +35,7 @@ class TestQgsCoordinateTransform: public QObject void assignment(); void isValid(); void isShortCircuited(); + void contextShared(); private: @@ -62,7 +65,7 @@ void TestQgsCoordinateTransform::copy() QgsCoordinateReferenceSystem destination; destination.createFromId( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ); - QgsCoordinateTransform original( source, destination ); + QgsCoordinateTransform original( source, destination, QgsProject::instance() ); QVERIFY( original.isValid() ); QgsCoordinateTransform copy( original ); @@ -91,7 +94,7 @@ void TestQgsCoordinateTransform::assignment() QgsCoordinateReferenceSystem destination; destination.createFromId( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ); - QgsCoordinateTransform original( source, destination ); + QgsCoordinateTransform original( source, destination, QgsProject::instance() ); QVERIFY( original.isValid() ); QgsCoordinateTransform copy; @@ -125,22 +128,22 @@ void TestQgsCoordinateTransform::isValid() srs2.createFromSrid( 4326 ); // valid source, invalid destination - QgsCoordinateTransform tr2( srs1, QgsCoordinateReferenceSystem() ); + QgsCoordinateTransform tr2( srs1, QgsCoordinateReferenceSystem(), QgsProject::instance() ); QVERIFY( !tr2.isValid() ); // invalid source, valid destination - QgsCoordinateTransform tr3( QgsCoordinateReferenceSystem(), srs2 ); + QgsCoordinateTransform tr3( QgsCoordinateReferenceSystem(), srs2, QgsProject::instance() ); QVERIFY( !tr3.isValid() ); // valid source, valid destination - QgsCoordinateTransform tr4( srs1, srs2 ); + QgsCoordinateTransform tr4( srs1, srs2, QgsProject::instance() ); QVERIFY( tr4.isValid() ); // try to invalidate by setting source as invalid tr4.setSourceCrs( QgsCoordinateReferenceSystem() ); QVERIFY( !tr4.isValid() ); - QgsCoordinateTransform tr5( srs1, srs2 ); + QgsCoordinateTransform tr5( srs1, srs2, QgsProject::instance() ); // try to invalidate by setting destination as invalid tr5.setDestinationCrs( QgsCoordinateReferenceSystem() ); QVERIFY( !tr5.isValid() ); @@ -158,19 +161,19 @@ void TestQgsCoordinateTransform::isShortCircuited() srs2.createFromSrid( 4326 ); // valid source, invalid destination - QgsCoordinateTransform tr2( srs1, QgsCoordinateReferenceSystem() ); + QgsCoordinateTransform tr2( srs1, QgsCoordinateReferenceSystem(), QgsProject::instance() ); QVERIFY( tr2.isShortCircuited() ); // invalid source, valid destination - QgsCoordinateTransform tr3( QgsCoordinateReferenceSystem(), srs2 ); + QgsCoordinateTransform tr3( QgsCoordinateReferenceSystem(), srs2, QgsProject::instance() ); QVERIFY( tr3.isShortCircuited() ); // equal, valid source and destination - QgsCoordinateTransform tr4( srs1, srs1 ); + QgsCoordinateTransform tr4( srs1, srs1, QgsProject::instance() ); QVERIFY( tr4.isShortCircuited() ); // valid but different source and destination - QgsCoordinateTransform tr5( srs1, srs2 ); + QgsCoordinateTransform tr5( srs1, srs2, QgsProject::instance() ); QVERIFY( !tr5.isShortCircuited() ); // try to short circuit by changing dest @@ -178,6 +181,38 @@ void TestQgsCoordinateTransform::isShortCircuited() QVERIFY( tr5.isShortCircuited() ); } +void TestQgsCoordinateTransform::contextShared() +{ + //test implicit sharing of QgsCoordinateTransformContext + QgsCoordinateTransformContext original; + original.addSourceDestinationDatumTransform( QgsCoordinateReferenceSystem( 3111 ), QgsCoordinateReferenceSystem( 3113 ), 1, 2 ); + + QgsCoordinateTransformContext copy( original ); + QMap< QPair< QString, QString >, QgsDatumTransform::TransformPair > expected; + expected.insert( qMakePair( QStringLiteral( "EPSG:3111" ), QStringLiteral( "EPSG:3113" ) ), QgsDatumTransform::TransformPair( 1, 2 ) ); + QCOMPARE( original.sourceDestinationDatumTransforms(), expected ); + QCOMPARE( copy.sourceDestinationDatumTransforms(), expected ); + + // trigger detach + copy.addSourceDestinationDatumTransform( QgsCoordinateReferenceSystem( 3111 ), QgsCoordinateReferenceSystem( 3113 ), 3, 4 ); + QCOMPARE( original.sourceDestinationDatumTransforms(), expected ); + + expected.insert( qMakePair( QStringLiteral( "EPSG:3111" ), QStringLiteral( "EPSG:3113" ) ), QgsDatumTransform::TransformPair( 3, 4 ) ); + QCOMPARE( copy.sourceDestinationDatumTransforms(), expected ); + + // copy via assignment + QgsCoordinateTransformContext copy2; + copy2 = original; + expected.insert( qMakePair( QStringLiteral( "EPSG:3111" ), QStringLiteral( "EPSG:3113" ) ), QgsDatumTransform::TransformPair( 1, 2 ) ); + QCOMPARE( original.sourceDestinationDatumTransforms(), expected ); + QCOMPARE( copy2.sourceDestinationDatumTransforms(), expected ); + + copy2.addSourceDestinationDatumTransform( QgsCoordinateReferenceSystem( 3111 ), QgsCoordinateReferenceSystem( 3113 ), 3, 4 ); + QCOMPARE( original.sourceDestinationDatumTransforms(), expected ); + expected.insert( qMakePair( QStringLiteral( "EPSG:3111" ), QStringLiteral( "EPSG:3113" ) ), QgsDatumTransform::TransformPair( 3, 4 ) ); + QCOMPARE( copy2.sourceDestinationDatumTransforms(), expected ); +} + void TestQgsCoordinateTransform::transformBoundingBox() { @@ -187,7 +222,7 @@ void TestQgsCoordinateTransform::transformBoundingBox() QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4326 ); - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); QgsRectangle crossingRect( 6374985, -3626584, 7021195, -3272435 ); QgsRectangle resultRect = tr.transformBoundingBox( crossingRect, QgsCoordinateTransform::ForwardTransform, true ); QgsRectangle expectedRect; @@ -207,7 +242,7 @@ void TestQgsCoordinateTransform::transformBoundingBox() QGSCOMPARENEAR( resultRect.yMaximum(), expectedRect.yMaximum(), 0.001 ); // test transforming a bounding box, resulting in an invalid transform - exception must be thrown - tr = QgsCoordinateTransform( QgsCoordinateReferenceSystem( 4326 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ); + tr = QgsCoordinateTransform( QgsCoordinateReferenceSystem( 4326 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), QgsProject::instance() ); QgsRectangle rect( -99999999999, 99999999999, -99999999998, 99999999998 ); bool errorObtained = false; try diff --git a/tests/src/core/testqgsdataitem.cpp b/tests/src/core/testqgsdataitem.cpp index 2aef3f6f87f..5dac9106eab 100644 --- a/tests/src/core/testqgsdataitem.cpp +++ b/tests/src/core/testqgsdataitem.cpp @@ -72,7 +72,7 @@ void TestQgsDataItem::initTestCase() mScanItemsSetting = settings.value( QStringLiteral( "/qgis/scanItemsInBrowser2" ), QVariant( "" ) ).toString(); //create a directory item that will be used in all tests... - mDirItem = new QgsDirectoryItem( 0, QStringLiteral( "Test" ), TEST_DATA_DIR ); + mDirItem = new QgsDirectoryItem( nullptr, QStringLiteral( "Test" ), TEST_DATA_DIR ); } void TestQgsDataItem::cleanupTestCase() @@ -108,7 +108,7 @@ void TestQgsDataItem::testDirItemChildren() Q_FOREACH ( const QString &tmpSetting, tmpSettings ) { settings.setValue( QStringLiteral( "/qgis/scanItemsInBrowser2" ), tmpSetting ); - QgsDirectoryItem *dirItem = new QgsDirectoryItem( 0, QStringLiteral( "Test" ), TEST_DATA_DIR ); + QgsDirectoryItem *dirItem = new QgsDirectoryItem( nullptr, QStringLiteral( "Test" ), TEST_DATA_DIR ); QVERIFY( isValidDirItem( dirItem ) ); QVector children = dirItem->createChildren(); diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp index 9dd6db30e65..97aa8100d3d 100644 --- a/tests/src/core/testqgsdistancearea.cpp +++ b/tests/src/core/testqgsdistancearea.cpp @@ -26,6 +26,7 @@ #include "qgsgeometryfactory.h" #include "qgsgeometry.h" #include "qgis.h" +#include "qgsproject.h" #include class TestQgsDistanceArea: public QObject @@ -76,7 +77,7 @@ void TestQgsDistanceArea::basic() QCOMPARE( resultA, 5.0 ); // Now, on an ellipsoid. Always less? - daA.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3006 ) ); + daA.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3006 ), QgsProject::instance()->transformContext() ); daA.setEllipsoid( QStringLiteral( "WGS84" ) ); resultA = daA.measureLine( p1, p2 ); QVERIFY( resultA < 5.0 ); @@ -177,7 +178,7 @@ void TestQgsDistanceArea::test_distances() // Set up DA QgsDistanceArea myDa; - myDa.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:4030" ) ) ); + myDa.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:4030" ) ), QgsProject::instance()->transformContext() ); myDa.setEllipsoid( QStringLiteral( "WGS84" ) ); QString myFileName = QStringLiteral( TEST_DATA_DIR ) + "/GeodTest-nano.dat"; @@ -216,7 +217,7 @@ void TestQgsDistanceArea::regression13601() //test regression #13601 QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "NONE" ) ); - calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 1108L ) ); + calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 1108L ), QgsProject::instance()->transformContext() ); QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((252000 1389000, 265000 1389000, 265000 1385000, 252000 1385000, 252000 1389000))" ) ).release() ); QGSCOMPARENEAR( calc.measureArea( geom ), 52000000, 0.0001 ); } @@ -225,7 +226,7 @@ void TestQgsDistanceArea::collections() { //test measuring for collections QgsDistanceArea myDa; - myDa.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:4030" ) ) ); + myDa.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:4030" ) ), QgsProject::instance()->transformContext() ); myDa.setEllipsoid( QStringLiteral( "WGS84" ) ); //collection of lines, should be sum of line length @@ -257,7 +258,7 @@ void TestQgsDistanceArea::measureUnits() //test regression #13610 QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "NONE" ) ); - calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 254L ) ); + calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 254L ), QgsProject::instance()->transformContext() ); QgsUnitTypes::DistanceUnit units; QgsPointXY p1( 1341683.9854275715, 408256.9562717728 ); QgsPointXY p2( 1349321.7807031618, 408256.9562717728 ); @@ -279,7 +280,7 @@ void TestQgsDistanceArea::measureUnits() void TestQgsDistanceArea::measureAreaAndUnits() { QgsDistanceArea da; - da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3452 ) ); + da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3452 ), QgsProject::instance()->transformContext() ); da.setEllipsoid( QStringLiteral( "NONE" ) ); QgsCoordinateReferenceSystem daCRS; daCRS.createFromSrsId( da.sourceCrs().srsid() ); @@ -331,7 +332,7 @@ void TestQgsDistanceArea::measureAreaAndUnits() poly << ring; polygon = QgsGeometry::fromPolygonXY( poly ); - da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 27469 ) ); + da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 27469 ), QgsProject::instance()->transformContext() ); da.setEllipsoid( QStringLiteral( "NONE" ) ); // measurement should be in square feet area = da.measureArea( polygon ); @@ -361,7 +362,7 @@ void TestQgsDistanceArea::measureAreaAndUnits() void TestQgsDistanceArea::emptyPolygon() { QgsDistanceArea da; - da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3452 ) ); + da.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 3452 ), QgsProject::instance()->transformContext() ); da.setEllipsoid( QStringLiteral( "WGS84" ) ); //test that measuring an empty polygon doesn't crash @@ -373,7 +374,7 @@ void TestQgsDistanceArea::regression14675() //test regression #14675 QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "GRS80" ) ); - calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 145L ) ); + calc.setSourceCrs( QgsCoordinateReferenceSystem::fromSrsId( 145L ), QgsProject::instance()->transformContext() ); QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((917593.5791854317067191 6833700.00807378999888897, 917596.43389983859378844 6833700.67099479306489229, 917599.53056440979707986 6833700.78673478215932846, 917593.5791854317067191 6833700.00807378999888897))" ) ).release() ); //lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats QGSCOMPARENEAR( calc.measureArea( geom ), 0.833010, 0.03 ); @@ -383,7 +384,7 @@ void TestQgsDistanceArea::regression16820() { QgsDistanceArea calc; calc.setEllipsoid( QStringLiteral( "WGS84" ) ); - calc.setSourceCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:32634" ) ) ); + calc.setSourceCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:32634" ) ), QgsProject::instance()->transformContext() ); QgsGeometry geom( QgsGeometryFactory::geomFromWkt( QStringLiteral( "Polygon ((110250.54038314701756462 5084495.57398066483438015, 110243.46975068224128336 5084507.17200060561299324, 110251.23908144699817058 5084506.68309532757848501, 110251.2394439501222223 5084506.68307251576334238, 110250.54048078990308568 5084495.57553235255181789, 110250.54038314701756462 5084495.57398066483438015))" ) ).release() ); //lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats QGSCOMPARENEAR( calc.measureArea( geom ), 43.3280029296875, 0.2 ); diff --git a/tests/src/core/testqgsdxfexport.cpp b/tests/src/core/testqgsdxfexport.cpp index d3ff8b961c7..6e407b10c12 100644 --- a/tests/src/core/testqgsdxfexport.cpp +++ b/tests/src/core/testqgsdxfexport.cpp @@ -73,15 +73,15 @@ void TestQgsDxfExport::init() { QString filename = QStringLiteral( TEST_DATA_DIR ) + "/points.shp"; mPointLayer = new QgsVectorLayer( filename, QStringLiteral( "points" ), QStringLiteral( "ogr" ) ); - Q_ASSERT( mPointLayer->isValid() ); + QVERIFY( mPointLayer->isValid() ); QgsProject::instance()->addMapLayer( mPointLayer ); filename = QStringLiteral( TEST_DATA_DIR ) + "/lines.shp"; mLineLayer = new QgsVectorLayer( filename, QStringLiteral( "lines" ), QStringLiteral( "ogr" ) ); - Q_ASSERT( mLineLayer->isValid() ); + QVERIFY( mLineLayer->isValid() ); QgsProject::instance()->addMapLayer( mLineLayer ); filename = QStringLiteral( TEST_DATA_DIR ) + "/polys.shp"; mPolygonLayer = new QgsVectorLayer( filename, QStringLiteral( "polygons" ), QStringLiteral( "ogr" ) ); - Q_ASSERT( mPolygonLayer->isValid() ); + QVERIFY( mPolygonLayer->isValid() ); QgsProject::instance()->addMapLayer( mPolygonLayer ); } @@ -186,6 +186,7 @@ void TestQgsDxfExport::testMtext() format.setColor( QColor( 200, 0, 200 ) ); settings.setFormat( format ); mPointLayer->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); + mPointLayer->setLabelsEnabled( true ); QgsDxfExport d; d.addLayers( QList< QPair< QgsVectorLayer *, int > >() << qMakePair( mPointLayer, -1 ) ); @@ -247,6 +248,7 @@ void TestQgsDxfExport::testText() format.setColor( QColor( 200, 0, 200 ) ); settings.setFormat( format ); mPointLayer->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); + mPointLayer->setLabelsEnabled( true ); QgsDxfExport d; d.addLayers( QList< QPair< QgsVectorLayer *, int > >() << qMakePair( mPointLayer, -1 ) ); diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 0fcf2f065d2..3d081c9b036 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -359,7 +359,7 @@ class TestQgsExpression: public QObject } QgsExpressionContext context; - Q_ASSERT( exp.prepare( &context ) ); + QVERIFY( exp.prepare( &context ) ); QVariant res = exp.evaluate(); if ( exp.hasEvalError() ) @@ -388,10 +388,10 @@ class TestQgsExpression: public QObject QgsExpression expression( "represent_value(\"Pilots\", 'Pilots')" ); if ( expression.hasParserError() ) qDebug() << expression.parserErrorString(); - Q_ASSERT( !expression.hasParserError() ); + QVERIFY( !expression.hasParserError() ); if ( expression.hasEvalError() ) qDebug() << expression.evalErrorString(); - Q_ASSERT( !expression.hasEvalError() ); + QVERIFY( !expression.hasEvalError() ); expression.prepare( &context ); QgsFeature feature; @@ -403,10 +403,10 @@ class TestQgsExpression: public QObject QgsExpression expression2( "represent_value(\"Class\", 'Class')" ); if ( expression2.hasParserError() ) qDebug() << expression2.parserErrorString(); - Q_ASSERT( !expression2.hasParserError() ); + QVERIFY( !expression2.hasParserError() ); if ( expression2.hasEvalError() ) qDebug() << expression2.evalErrorString(); - Q_ASSERT( !expression2.hasEvalError() ); + QVERIFY( !expression2.hasEvalError() ); expression2.prepare( &context ); mPointsLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "Class = 'Jet'" ) ).nextFeature( feature ); context.setFeature( feature ); @@ -416,10 +416,10 @@ class TestQgsExpression: public QObject QgsExpression expression3( "represent_value(\"Pilots\")" ); if ( expression3.hasParserError() ) qDebug() << expression.parserErrorString(); - Q_ASSERT( !expression3.hasParserError() ); + QVERIFY( !expression3.hasParserError() ); if ( expression3.hasEvalError() ) qDebug() << expression3.evalErrorString(); - Q_ASSERT( !expression3.hasEvalError() ); + QVERIFY( !expression3.hasEvalError() ); mPointsLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "Pilots = 1" ) ).nextFeature( feature ); context.setFeature( feature ); @@ -428,12 +428,22 @@ class TestQgsExpression: public QObject expression3.prepare( &context ); QCOMPARE( expression.evaluate( &context ).toString(), QStringLiteral( "one" ) ); - QgsExpression expression4( "represent_value('Class')" ); + expression4.evaluate(); if ( expression4.hasParserError() ) qDebug() << expression4.parserErrorString(); - Q_ASSERT( !expression4.hasParserError() ); - Q_ASSERT( expression4.hasEvalError() ); + QVERIFY( !expression4.hasParserError() ); + if ( expression4.hasEvalError() ) + qDebug() << expression4.evalErrorString(); + QVERIFY( expression4.hasEvalError() ); + + expression4.prepare( &context ); + if ( expression4.hasParserError() ) + qDebug() << expression4.parserErrorString(); + QVERIFY( !expression4.hasParserError() ); + if ( expression4.hasEvalError() ) + qDebug() << expression4.evalErrorString(); + QVERIFY( expression4.hasEvalError() ); } void evaluation_data() @@ -613,12 +623,21 @@ class TestQgsExpression: public QObject QTest::newRow( "round(1234.557,2) - round up" ) << "round(1234.557,2)" << false << QVariant( 1234.56 ); QTest::newRow( "round(1234.554,2) - round down" ) << "round(1234.554,2)" << false << QVariant( 1234.55 ); QTest::newRow( "round(1234.6) - round up to int" ) << "round(1234.6)" << false << QVariant( 1235 ); - QTest::newRow( "round(1234.6) - round down to int" ) << "round(1234.4)" << false << QVariant( 1234 ); + QTest::newRow( "round(1234.4) - round down to int" ) << "round(1234.4)" << false << QVariant( 1234 ); QTest::newRow( "max(1)" ) << "max(1)" << false << QVariant( 1. ); QTest::newRow( "max(1,3.5,-2.1)" ) << "max(1,3.5,-2.1)" << false << QVariant( 3.5 ); + QTest::newRow( "max(3.5,-2.1,1)" ) << "max(3.5,-2.1,1)" << false << QVariant( 3.5 ); + QTest::newRow( "max with null value" ) << "max(1,3.5,null)" << false << QVariant( 3.5 ); + QTest::newRow( "max with null value first" ) << "max(null,-3.5,2)" << false << QVariant( 2. ); + QTest::newRow( "max with no params" ) << "max()" << false << QVariant( QVariant::Double ); + QTest::newRow( "max with only null value" ) << "max(null)" << false << QVariant( QVariant::Double ); QTest::newRow( "min(-1.5)" ) << "min(-1.5)" << false << QVariant( -1.5 ); QTest::newRow( "min(-16.6,3.5,-2.1)" ) << "min(-16.6,3.5,-2.1)" << false << QVariant( -16.6 ); QTest::newRow( "min(5,3.5,-2.1)" ) << "min(5,3.5,-2.1)" << false << QVariant( -2.1 ); + QTest::newRow( "min with null value" ) << "min(5,null,-2.1)" << false << QVariant( -2.1 ); + QTest::newRow( "min with null value first" ) << "min(null,3.2,6.5)" << false << QVariant( 3.2 ); + QTest::newRow( "min with no params" ) << "min()" << false << QVariant( QVariant::Double ); + QTest::newRow( "min with only null value" ) << "min(null)" << false << QVariant( QVariant::Double ); QTest::newRow( "clamp(-2,1,5)" ) << "clamp(-2,1,5)" << false << QVariant( 1.0 ); QTest::newRow( "clamp(min:=-2,value:=1,max:=5)" ) << "clamp(min:=-2,value:=1,max:=5)" << false << QVariant( 1.0 ); QTest::newRow( "clamp(-2,-10,5)" ) << "clamp(-2,-10,5)" << false << QVariant( -2.0 ); @@ -976,7 +995,7 @@ class TestQgsExpression: public QObject QTest::newRow( "rpad" ) << "rpad('Hello', 10, 'x')" << false << QVariant( "Helloxxxxx" ); QTest::newRow( "rpad truncate" ) << "rpad('Hello', 4, 'x')" << false << QVariant( "Hell" ); QTest::newRow( "lpad" ) << "lpad('Hello', 10, 'x')" << false << QVariant( "xxxxxHello" ); - QTest::newRow( "lpad truncate" ) << "rpad('Hello', 4, 'x')" << false << QVariant( "Hell" ); + QTest::newRow( "lpad truncate" ) << "lpad('Hello', 4, 'x')" << false << QVariant( "Hell" ); QTest::newRow( "title" ) << "title(' HeLlO WORLD ')" << false << QVariant( " Hello World " ); QTest::newRow( "trim" ) << "trim(' Test String ')" << false << QVariant( "Test String" ); QTest::newRow( "trim empty string" ) << "trim('')" << false << QVariant( "" ); @@ -1045,6 +1064,7 @@ class TestQgsExpression: public QObject QTest::newRow( "year with interval" ) << "year(tointerval('2 years'))" << false << QVariant( 2.0 ); QTest::newRow( "age" ) << "age('2012-06-30','2012-06-28')" << false << QVariant::fromValue( QgsInterval( 172800 ) ); QTest::newRow( "negative age" ) << "age('2012-06-28','2012-06-30')" << false << QVariant::fromValue( QgsInterval( -172800 ) ); + QTest::newRow( "big age" ) << "age('2000-01-01','1000-01-01')" << false << QVariant::fromValue( QgsInterval( 31556908800LL ) ); QTest::newRow( "day of week date" ) << "day_of_week(todate('2015-09-21'))" << false << QVariant( 1 ); QTest::newRow( "day of week datetime" ) << "day_of_week(to_datetime('2015-09-20 13:01:43'))" << false << QVariant( 0 ); QTest::newRow( "hour datetime" ) << "hour(to_datetime('2015-09-20 13:01:43'))" << false << QVariant( 13 ); @@ -1215,7 +1235,7 @@ class TestQgsExpression: public QObject qDebug() << exp.evalErrorString(); if ( result.type() != expected.type() ) { - qDebug() << "got " << result.typeName() << " instead of " << expected.typeName(); + qDebug() << "got type " << result.typeName() << "(" << result.type() << ") instead of " << expected.typeName() << "(" << expected.type() << ")"; } //qDebug() << res.type() << " " << result.type(); //qDebug() << "type " << res.typeName(); @@ -1223,7 +1243,7 @@ class TestQgsExpression: public QObject QgsExpressionContext context; - Q_ASSERT( exp.prepare( &context ) ); + QVERIFY( exp.prepare( &context ) ); QVariant::Type resultType = result.type(); QVariant::Type expectedType = expected.type(); @@ -1277,7 +1297,7 @@ class TestQgsExpression: public QObject break; } default: - Q_ASSERT( false ); // should never happen + QVERIFY( false ); // should never happen } } @@ -1990,7 +2010,7 @@ class TestQgsExpression: public QObject { //test calculations with and without geometry calculator set QgsDistanceArea da; - da.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:3111" ) ) ); + da.setSourceCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:3111" ) ), QgsProject::instance()->transformContext() ); da.setEllipsoid( QStringLiteral( "WGS84" ) ); QgsFeature feat; @@ -2231,7 +2251,7 @@ class TestQgsExpression: public QObject s.createFromOgcWmsCrs( QStringLiteral( "EPSG:4326" ) ); QgsCoordinateReferenceSystem d; d.createFromOgcWmsCrs( QStringLiteral( "EPSG:3857" ) ); - QgsCoordinateTransform t( s, d ); + QgsCoordinateTransform t( s, d, QgsProject::instance() ); QgsGeometry tLine = QgsGeometry::fromPolylineXY( line ); tLine.transform( t ); @@ -2416,7 +2436,7 @@ class TestQgsExpression: public QObject QgsExpression exp1( QStringLiteral( "eval()" ) ); QVariant v1 = exp1.evaluate( &context ); - Q_ASSERT( !v1.isValid() ); + QVERIFY( !v1.isValid() ); QgsExpression exp2( QStringLiteral( "eval('4')" ) ); QVariant v2 = exp2.evaluate( &context ); @@ -2902,7 +2922,7 @@ class TestQgsExpression: public QObject QgsExpression e3( QStringLiteral( "env('TESTENV_I_DO_NOT_EXIST')" ) ); QVariant result3 = e3.evaluate( &context ); - Q_ASSERT( result3.isNull() ); + QVERIFY( result3.isNull() ); } void test_formatPreviewString() diff --git a/tests/src/core/testqgsexpressioncontext.cpp b/tests/src/core/testqgsexpressioncontext.cpp index eae3e2460b1..1df61d755e7 100644 --- a/tests/src/core/testqgsexpressioncontext.cpp +++ b/tests/src/core/testqgsexpressioncontext.cpp @@ -63,7 +63,7 @@ class TestQgsExpressionContext : public QObject GetTestValueFunction() : QgsScopedExpressionFunction( QStringLiteral( "get_test_value" ), 1, QStringLiteral( "test" ) ) {} - virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override + QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override { return 42; } @@ -81,7 +81,7 @@ class TestQgsExpressionContext : public QObject GetTestValueFunction2() : QgsScopedExpressionFunction( QStringLiteral( "get_test_value" ), 1, QStringLiteral( "test" ) ) {} - virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override + QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override { return 43; } @@ -100,7 +100,7 @@ class TestQgsExpressionContext : public QObject , mVal( v ) {} - virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override + QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override { if ( !mVal ) return QVariant(); @@ -116,7 +116,7 @@ class TestQgsExpressionContext : public QObject /** * This function is not static, it's value changes with every invocation. */ - virtual bool isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override + bool isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override { Q_UNUSED( node ) Q_UNUSED( parent ) @@ -229,7 +229,7 @@ void TestQgsExpressionContext::contextScopeFunctions() QVERIFY( scope.hasFunction( "get_test_value" ) ); QVERIFY( scope.function( "get_test_value" ) ); QgsExpressionContext temp; - QCOMPARE( scope.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 ); + QCOMPARE( scope.function( "get_test_value" )->func( QVariantList(), &temp, nullptr, nullptr ).toInt(), 42 ); //test functionNames scope.addFunction( QStringLiteral( "get_test_value2" ), new GetTestValueFunction() ); @@ -372,27 +372,27 @@ void TestQgsExpressionContext::contextStackFunctions() QVERIFY( context.hasFunction( "get_test_value" ) ); QVERIFY( context.function( "get_test_value" ) ); QgsExpressionContext temp; - QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 ); + QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, nullptr, nullptr ).toInt(), 42 ); //add a second scope, should override the first context << new QgsExpressionContextScope(); //test without setting function first... QVERIFY( context.hasFunction( "get_test_value" ) ); QVERIFY( context.function( "get_test_value" ) ); - QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 ); + QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, nullptr, nullptr ).toInt(), 42 ); //then set the variable so it overrides QgsExpressionContextScope *scope2 = context.scope( 1 ); scope2->addFunction( QStringLiteral( "get_test_value" ), new GetTestValueFunction2() ); QVERIFY( context.hasFunction( "get_test_value" ) ); QVERIFY( context.function( "get_test_value" ) ); - QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 43 ); + QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, nullptr, nullptr ).toInt(), 43 ); //make sure stack falls back to earlier contexts scope2->addFunction( QStringLiteral( "get_test_value2" ), new GetTestValueFunction() ); QVERIFY( context.hasFunction( "get_test_value2" ) ); QVERIFY( context.function( "get_test_value2" ) ); - QCOMPARE( context.function( "get_test_value2" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 ); + QCOMPARE( context.function( "get_test_value2" )->func( QVariantList(), &temp, nullptr, nullptr ).toInt(), 42 ); //test functionNames QStringList names = context.functionNames(); @@ -430,7 +430,7 @@ void TestQgsExpressionContext::evaluate() QVERIFY( !expShorthandBad.evaluate( &context ).isValid() ); //test with a function provided by a context - QgsExpression::registerFunction( new ModifiableFunction( 0 ), true ); + QgsExpression::registerFunction( new ModifiableFunction( nullptr ), true ); QgsExpression testExpWContextFunction( QStringLiteral( "test_function(1)" ) ); QVERIFY( !testExpWContextFunction.evaluate().isValid() ); @@ -533,13 +533,13 @@ void TestQgsExpressionContext::takeScopes() auto scopes = context.takeScopes(); QCOMPARE( scopes.length(), 2 ); - Q_ASSERT( scopes.at( 0 )->hasVariable( "test_global" ) ); - Q_ASSERT( scopes.at( 1 )->hasVariable( "test_project" ) ); + QVERIFY( scopes.at( 0 )->hasVariable( "test_global" ) ); + QVERIFY( scopes.at( 1 )->hasVariable( "test_project" ) ); qDeleteAll( scopes ); - Q_ASSERT( !context.variable( "test_global" ).isValid() ); - Q_ASSERT( !context.variable( "test_project" ).isValid() ); + QVERIFY( !context.variable( "test_global" ).isValid() ); + QVERIFY( !context.variable( "test_project" ).isValid() ); } void TestQgsExpressionContext::globalScope() @@ -588,12 +588,11 @@ void TestQgsExpressionContext::globalScope() //test removeGlobalVariables QgsExpressionContextUtils::setGlobalVariable( QStringLiteral( "key" ), "value" ); - QgsExpressionContextScope *globalScope3 = QgsExpressionContextUtils::globalScope(); + std::unique_ptr< QgsExpressionContextScope > globalScope3( QgsExpressionContextUtils::globalScope() ); QVERIFY( globalScope3->hasVariable( "key" ) ); QgsExpressionContextUtils::removeGlobalVariable( QStringLiteral( "key" ) ); - globalScope3 = QgsExpressionContextUtils::globalScope(); + globalScope3.reset( QgsExpressionContextUtils::globalScope() ); QVERIFY( !globalScope3->hasVariable( "key" ) ); - delete globalScope3; } void TestQgsExpressionContext::projectScope() @@ -648,7 +647,7 @@ void TestQgsExpressionContext::projectScope() projectScope = QgsExpressionContextUtils::projectScope( project ); QVERIFY( !projectScope->hasVariable( "key" ) ); delete projectScope; - projectScope = 0; + projectScope = nullptr; //test project scope functions @@ -673,11 +672,11 @@ void TestQgsExpressionContext::projectScope() void TestQgsExpressionContext::layerScope() { //test passing no layer - should be no crash - QgsExpressionContextScope *layerScope = QgsExpressionContextUtils::layerScope( 0 ); + QgsExpressionContextScope *layerScope = QgsExpressionContextUtils::layerScope( nullptr ); QCOMPARE( layerScope->name(), tr( "Layer" ) ); QCOMPARE( layerScope->variableCount(), 0 ); delete layerScope; - layerScope = 0; + layerScope = nullptr; //create a map layer std::unique_ptr vectorLayer( new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:integer&field=col3:integer" ), QStringLiteral( "test layer" ), QStringLiteral( "memory" ) ) ); diff --git a/tests/src/core/testqgsfield.cpp b/tests/src/core/testqgsfield.cpp index b5795f14ef5..2f6de8f25c8 100644 --- a/tests/src/core/testqgsfield.cpp +++ b/tests/src/core/testqgsfield.cpp @@ -135,7 +135,7 @@ void TestQgsField::gettersSetters() QCOMPARE( field.comment(), QString( "comment" ) ); field.setAlias( QStringLiteral( "alias" ) ); QCOMPARE( field.alias(), QString( "alias" ) ); - field.setDefaultValueDefinition( QStringLiteral( "1+2" ) ); + field.setDefaultValueDefinition( QgsDefaultValue( QStringLiteral( "1+2" ) ) ); QCOMPARE( field.defaultValueDefinition().expression(), QString( "1+2" ) ); QgsFieldConstraints constraints; constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider ); @@ -244,10 +244,10 @@ void TestQgsField::equality() QVERIFY( !( field1 == field2 ) ); QVERIFY( field1 != field2 ); field2.setAlias( QString() ); - field2.setDefaultValueDefinition( QStringLiteral( "1+2" ) ); + field2.setDefaultValueDefinition( QgsDefaultValue( QStringLiteral( "1+2" ) ) ); QVERIFY( !( field1 == field2 ) ); QVERIFY( field1 != field2 ); - field2.setDefaultValueDefinition( QString() ); + field2.setDefaultValueDefinition( QgsDefaultValue() ); constraints = field2.constraints(); constraints.removeConstraint( QgsFieldConstraints::ConstraintNotNull ); field2.setConstraints( constraints ); @@ -461,7 +461,7 @@ void TestQgsField::dataStream() original.setTypeName( QStringLiteral( "typename1" ) ); original.setComment( QStringLiteral( "comment1" ) ); original.setAlias( QStringLiteral( "alias" ) ); - original.setDefaultValueDefinition( QStringLiteral( "default" ) ); + original.setDefaultValueDefinition( QgsDefaultValue( QStringLiteral( "default" ) ) ); QgsFieldConstraints constraints; constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider ); constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginLayer ); diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index d6e31d18881..32b61ff0486 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -49,6 +49,7 @@ #include "qgsgeometrycollection.h" #include "qgsgeometryfactory.h" #include "qgscurvepolygon.h" +#include "qgsproject.h" //qgs unit test utility class #include "qgsrenderchecker.h" @@ -79,8 +80,10 @@ class TestQgsGeometry : public QObject void asVariant(); //test conversion to and from a QVariant void isEmpty(); void operatorBool(); + void equality(); void vertexIterator(); + // geometry types void point(); //test QgsPointV2 void lineString(); //test QgsLineString @@ -430,6 +433,43 @@ void TestQgsGeometry::operatorBool() QVERIFY( !geom ); } +void TestQgsGeometry::equality() +{ + // null geometries + QVERIFY( !QgsGeometry().equals( QgsGeometry() ) ); + + // compare to null + QgsGeometry g1( qgis::make_unique< QgsPoint >( 1.0, 2.0 ) ); + QVERIFY( !g1.equals( QgsGeometry() ) ); + QVERIFY( !QgsGeometry().equals( g1 ) ); + + // compare implicitly shared copies + QgsGeometry g2( g1 ); + QVERIFY( g2.equals( g1 ) ); + QVERIFY( g1.equals( g2 ) ); + QVERIFY( g1.equals( g1 ) ); + + // equal geometry, but different internal data + g2 = QgsGeometry::fromWkt( "Point( 1.0 2.0 )" ); + QVERIFY( g2.equals( g1 ) ); + QVERIFY( g1.equals( g2 ) ); + + // different dimensionality + g2 = QgsGeometry::fromWkt( "PointM( 1.0 2.0 3.0)" ); + QVERIFY( !g2.equals( g1 ) ); + QVERIFY( !g1.equals( g2 ) ); + + // different type + g2 = QgsGeometry::fromWkt( "LineString( 1.0 2.0, 3.0 4.0 )" ); + QVERIFY( !g2.equals( g1 ) ); + QVERIFY( !g1.equals( g2 ) ); + + // different direction + g1 = QgsGeometry::fromWkt( "LineString( 3.0 4.0, 1.0 2.0 )" ); + QVERIFY( !g2.equals( g1 ) ); + QVERIFY( !g1.equals( g2 ) ); +} + void TestQgsGeometry::vertexIterator() { QgsGeometry geom; @@ -578,6 +618,10 @@ void TestQgsGeometry::point() QVERIFY( !( QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) != QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) ) ); QVERIFY( QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 1 / 3.0 ) != QgsPoint( QgsWkbTypes::PointZ, 2 / 3.0, 1 / 3.0 ) ); + QgsLineString nonPoint; + QVERIFY( p8 != nonPoint ); + QVERIFY( !( p8 == nonPoint ) ); + //test setters and getters //x QgsPoint p10( QgsWkbTypes::PointZM ); @@ -715,7 +759,7 @@ void TestQgsGeometry::point() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); QgsPoint p16( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ); p16.transform( tr, QgsCoordinateTransform::ForwardTransform ); QGSCOMPARENEAR( p16.x(), 175.771, 0.001 ); @@ -1593,7 +1637,7 @@ void TestQgsGeometry::circularString() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); // 2d CRS transform QgsCircularString l21; @@ -3043,6 +3087,10 @@ void TestQgsGeometry::lineString() QVERIFY( e5 != e6 ); QVERIFY( e6 != QgsCircularString() ); + QgsPoint p1; + QVERIFY( !( e6 == p1 ) ); + QVERIFY( e6 != p1 ); + QVERIFY( e6 == e6 ); //close/isClosed QgsLineString l11; @@ -3317,7 +3365,7 @@ void TestQgsGeometry::lineString() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); // 2d CRS transform QgsLineString l21; @@ -4241,7 +4289,6 @@ void TestQgsGeometry::lineString() << QgsPoint( 11, 12, 4 ) << QgsPoint( 111, 12, 5 ) << QgsPoint( 111.01, 11.99, 6 ) ); QVERIFY( !nodeLine.removeDuplicateNodes( 0.02, true ) ); QCOMPARE( nodeLine.asWkt( 2 ), QStringLiteral( "LineStringZ (11 2 1, 11.01 1.99 2, 11.02 2.01 3, 11 12 4, 111 12 5, 111.01 11.99 6)" ) ); - } void TestQgsGeometry::polygon() @@ -4743,6 +4790,10 @@ void TestQgsGeometry::polygon() QVERIFY( p10 == p10b ); QVERIFY( !( p10 != p10b ) ); + QgsLineString nonPolygon; + QVERIFY( p10 != nonPolygon ); + QVERIFY( !( p10 == nonPolygon ) ); + //clone QgsPolygon p11; @@ -5154,7 +5205,7 @@ void TestQgsGeometry::polygon() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); // 2d CRS transform QgsPolygon pTransform; @@ -6973,6 +7024,13 @@ void TestQgsGeometry::circle() QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ); QGSCOMPARENEARPOINT( circ_tgt.center(), QgsPoint( 1.4645, 1.4645 ), 0.0001 ); QGSCOMPARENEAR( circ_tgt.radius(), 1.4645, 0.0001 ); + // with parallels + circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 0 ), QgsPoint( 1, 5 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) ); + QVERIFY( circ_tgt.isEmpty() ); + circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 0 ), QgsPoint( 1, 5 ) ); + QVERIFY( circ_tgt.isEmpty() ); + circ_tgt = QgsCircle().from3Tangents( QgsPoint( 5, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 0 ), QgsPoint( 1, 5 ) ); + QVERIFY( circ_tgt.isEmpty() ); // minimalCircleFrom3points QgsCircle minCircle3Points = QgsCircle().minimalCircleFrom3Points( QgsPoint( 0, 5 ), QgsPoint( 0, -5 ), QgsPoint( 1, 2 ) ); QGSCOMPARENEARPOINT( minCircle3Points.center(), QgsPoint( 0, 0 ), 0.0001 ); @@ -9390,7 +9448,7 @@ void TestQgsGeometry::compoundCurve() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); // 2d CRS transform QgsCompoundCurve c21; @@ -13929,6 +13987,28 @@ void TestQgsGeometry::geometryCollection() QCOMPARE( *static_cast< const QgsLineString * >( c15.geometryN( 0 ) ), part ); QCOMPARE( *static_cast< const QgsLineString * >( c15.geometryN( 1 ) ), part2 ); + //equality + QgsGeometryCollection emptyCollection; + QVERIFY( !( emptyCollection == c15 ) ); + QVERIFY( emptyCollection != c15 ); + QgsPoint notCollection; + QVERIFY( !( emptyCollection == notCollection ) ); + QVERIFY( emptyCollection != notCollection ); + QgsMultiPoint mp; + QgsMultiLineString ml; + QVERIFY( mp != ml ); + QgsMultiLineString ml2; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + ml.addGeometry( part.clone() ); + QVERIFY( ml != ml2 ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + ml2.addGeometry( part.clone() ); + QVERIFY( ml != ml2 ); + //toCurveType std::unique_ptr< QgsGeometryCollection > curveType( c12.toCurveType() ); QCOMPARE( curveType->wkbType(), QgsWkbTypes::GeometryCollection ); @@ -14206,7 +14286,7 @@ void TestQgsGeometry::geometryCollection() sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change - QgsCoordinateTransform tr( sourceSrs, destSrs ); + QgsCoordinateTransform tr( sourceSrs, destSrs, QgsProject::instance() ); // 2d CRS transform QgsGeometryCollection pTransform; diff --git a/tests/src/core/testqgsgeometryutils.cpp b/tests/src/core/testqgsgeometryutils.cpp index 7aa03b65185..a2ca18ae5d4 100644 --- a/tests/src/core/testqgsgeometryutils.cpp +++ b/tests/src/core/testqgsgeometryutils.cpp @@ -54,6 +54,8 @@ class TestQgsGeometryUtils: public QObject void testCoefficients(); void testPerpendicularSegment(); void testClosestPoint(); + void testSegmentIntersection(); + void testLineCircleIntersection(); }; @@ -647,5 +649,171 @@ void TestQgsGeometryUtils::testClosestPoint() QGSCOMPARENEAR( pt4.m(), 1, 0.0001 ); } +void TestQgsGeometryUtils::testSegmentIntersection() +{ + const double epsilon = 1e-8; + bool intersection = false, isIntersect = false; + QgsPoint inter; + // null + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 5, 5 ), QgsPoint( 5, 5 ), QgsPoint( 1, 1 ), QgsPoint( 1, 0 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 5, 5 ), QgsPoint( 5, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + // parallel + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 1, 1 ), QgsPoint( 1, 0 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 1, 1 ), QgsPoint( 1, 0 ), inter, isIntersect, epsilon, true ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 1, 1 ), QgsPoint( 0, 1 ), QgsPoint( 1, 2 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 1, 1 ), QgsPoint( -1, -1 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 1, 1 ), QgsPoint( 0, 0 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + // contigus + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 5 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 5 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 5 ), QgsPoint( 0, 0 ), QgsPoint( 1, 5 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 5 ), QgsPoint( 0, 0 ), QgsPoint( 1, 5 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 5 ) ); + // colinear + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 0, 6 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 0, 6 ), inter, isIntersect, epsilon, true ); + QVERIFY( !intersection ); + QVERIFY( !isIntersect ); + QVERIFY( inter == QgsPoint() ); + // improper + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 2 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 2 ), QgsPoint( 1, 5 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 1, 5 ), QgsPoint( 0, 2 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 1, 5 ), QgsPoint( 0, 2 ), QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 2 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 2 ), QgsPoint( 1, 5 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), QgsPoint( 0, 2 ), inter, isIntersect, epsilon ); + QVERIFY( !intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), QgsPoint( 0, 2 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 2 ) ); + // normal + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ), QgsPoint( 2, 0 ), QgsPoint( -1, 0 ), inter, isIntersect, epsilon ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 0 ) ); + inter = QgsPoint(); + intersection = QgsGeometryUtils::segmentIntersection( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ), QgsPoint( 2, 0 ), QgsPoint( -1, 0 ), inter, isIntersect, epsilon, true ); + QVERIFY( intersection ); + QVERIFY( isIntersect ); + QVERIFY( inter == QgsPoint( 0, 0 ) ); +} + +void TestQgsGeometryUtils::testLineCircleIntersection() +{ + QgsPointXY center = QgsPoint( 2, 2 ); + double radius = 2.0; + QgsPointXY linePoint1 = QgsPoint( 0, 2 ); + QgsPointXY linePoint2 = QgsPoint( 2, 2 ); + QgsPointXY intersection = QgsPoint( 3, 3 ); + bool isIntersection = QgsGeometryUtils::lineCircleIntersection( center, radius, linePoint1, linePoint2, intersection ); + QVERIFY( isIntersection ); + QVERIFY( intersection == QgsPointXY( 4, 2 ) ); + + linePoint1 = QgsPoint( 5, 0 ); + linePoint2 = QgsPoint( 5, 2 ); + isIntersection = QgsGeometryUtils::lineCircleIntersection( center, radius, linePoint1, linePoint2, intersection ); + QVERIFY( !isIntersection ); +} + QGSTEST_MAIN( TestQgsGeometryUtils ) #include "testqgsgeometryutils.moc" diff --git a/tests/src/core/testqgsinvertedpolygonrenderer.cpp b/tests/src/core/testqgsinvertedpolygonrenderer.cpp index eeae6e01a34..93362b0c427 100644 --- a/tests/src/core/testqgsinvertedpolygonrenderer.cpp +++ b/tests/src/core/testqgsinvertedpolygonrenderer.cpp @@ -61,7 +61,7 @@ class TestQgsInvertedPolygon : public QObject private: bool mTestHasError = false ; bool setQml( QgsVectorLayer *vlayer, const QString &qmlFile ); - bool imageCheck( const QString &type, const QgsRectangle * = 0 ); + bool imageCheck( const QString &type, const QgsRectangle * = nullptr ); QgsMapSettings mMapSettings; QgsVectorLayer *mpPolysLayer = nullptr; QString mTestDataDir; diff --git a/tests/src/core/testqgslabelingengine.cpp b/tests/src/core/testqgslabelingengine.cpp index 3c011c2a12d..ae434b8ce1d 100644 --- a/tests/src/core/testqgslabelingengine.cpp +++ b/tests/src/core/testqgslabelingengine.cpp @@ -27,6 +27,7 @@ #include #include "qgsrenderchecker.h" #include "qgsfontutils.h" +#include "qgsnullsymbolrenderer.h" class TestQgsLabelingEngine : public QObject { @@ -48,6 +49,7 @@ class TestQgsLabelingEngine : public QObject void testCapitalization(); void testParticipatingLayers(); void testRegisterFeatureUnprojectible(); + void testRotateHidePartial(); private: QgsVectorLayer *vl = nullptr; @@ -86,7 +88,7 @@ void TestQgsLabelingEngine::init() { QString filename = QStringLiteral( TEST_DATA_DIR ) + "/points.shp"; vl = new QgsVectorLayer( filename, QStringLiteral( "points" ), QStringLiteral( "ogr" ) ); - Q_ASSERT( vl->isValid() ); + QVERIFY( vl->isValid() ); QgsProject::instance()->addMapLayer( vl ); } @@ -132,6 +134,7 @@ void TestQgsLabelingEngine::testBasic() setDefaultLabelParams( settings ); vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary! + vl->setLabelsEnabled( true ); QgsLabelingEngine engine; engine.setMapSettings( mapSettings ); @@ -179,7 +182,7 @@ void TestQgsLabelingEngine::testDiagrams() bool res; vl->loadNamedStyle( QStringLiteral( TEST_DATA_DIR ) + "/points_diagrams.qml", res ); - Q_ASSERT( res ); + QVERIFY( res ); QgsLabelingEngine engine; engine.setMapSettings( mapSettings ); @@ -244,6 +247,7 @@ void TestQgsLabelingEngine::testRuleBased() root->appendChild( new QgsRuleBasedLabeling::Rule( new QgsPalLayerSettings( s2 ), 0, 0, QStringLiteral( "Class = 'Jet'" ) ) ); vl->setLabeling( new QgsRuleBasedLabeling( root ) ); + vl->setLabelsEnabled( true ); //setDefaultLabelParams( vl ); QgsMapRendererSequentialJob job( mapSettings ); @@ -353,7 +357,7 @@ void TestQgsLabelingEngine::zOrder() //add a second layer QString filename = QStringLiteral( TEST_DATA_DIR ) + "/points.shp"; QgsVectorLayer *vl2 = new QgsVectorLayer( filename, QStringLiteral( "points" ), QStringLiteral( "ogr" ) ); - Q_ASSERT( vl2->isValid() ); + QVERIFY( vl2->isValid() ); QgsProject::instance()->addMapLayer( vl2 ); QgsPalLayerSettings pls2( pls1 ); @@ -623,5 +627,74 @@ void TestQgsLabelingEngine::testRegisterFeatureUnprojectible() QCOMPARE( provider->mLabels.size(), 0 ); } +void TestQgsLabelingEngine::testRotateHidePartial() +{ + QgsPalLayerSettings settings; + setDefaultLabelParams( settings ); + + QgsTextFormat format = settings.format(); + format.setSize( 20 ); + format.setColor( QColor( 0, 0, 0 ) ); + settings.setFormat( format ); + + settings.fieldName = QStringLiteral( "'label'" ); + settings.isExpression = true; + settings.placement = QgsPalLayerSettings::OverPoint; + + std::unique_ptr< QgsVectorLayer> vl2( new QgsVectorLayer( QStringLiteral( "polygon?crs=epsg:4326&field=id:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) ); + vl2->setRenderer( new QgsNullSymbolRenderer() ); + + QgsVectorLayerLabelProvider *provider = new QgsVectorLayerLabelProvider( vl2.get(), QStringLiteral( "test" ), true, &settings ); + QgsFeature f( vl2->fields(), 1 ); + + f.setGeometry( QgsGeometry().fromWkt( QStringLiteral( "POLYGON((0 0,8 0,8 8,0 8,0 0))" ) ) ); + vl2->dataProvider()->addFeature( f ); + f.setGeometry( QgsGeometry().fromWkt( QStringLiteral( "POLYGON((20 20,28 20,28 28,20 28,20 20))" ) ) ); + vl2->dataProvider()->addFeature( f ); + f.setGeometry( QgsGeometry().fromWkt( QStringLiteral( "POLYGON((0 20,8 20,8 28,0 28,0 20))" ) ) ); + vl2->dataProvider()->addFeature( f ); + + vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary! + vl2->setLabelsEnabled( true ); + + // make a fake render context + QSize size( 640, 480 ); + QgsMapSettings mapSettings; + QgsCoordinateReferenceSystem tgtCrs; + tgtCrs.createFromString( QStringLiteral( "EPSG:4326" ) ); + mapSettings.setDestinationCrs( tgtCrs ); + + mapSettings.setOutputSize( size ); + mapSettings.setExtent( vl2->extent() ); + mapSettings.setLayers( QList() << vl2.get() ); + mapSettings.setOutputDpi( 96 ); + mapSettings.setRotation( 45 ); + + QgsLabelingEngineSettings engineSettings = mapSettings.labelingEngineSettings(); + engineSettings.setFlag( QgsLabelingEngineSettings::UsePartialCandidates, false ); + engineSettings.setFlag( QgsLabelingEngineSettings::DrawLabelRectOnly, true ); + mapSettings.setLabelingEngineSettings( engineSettings ); + + QgsMapRendererSequentialJob job( mapSettings ); + job.start(); + job.waitForFinished(); + + QImage img = job.renderedImage(); + + QPainter p( &img ); + QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings ); + context.setPainter( &p ); + + QgsLabelingEngine engine; + engine.setMapSettings( mapSettings ); + engine.addProvider( provider ); + + engine.run( context ); + p.end(); + engine.removeProvider( provider ); + + QVERIFY( imageCheck( "label_rotate_hide_partial", img, 20 ) ); +} + QGSTEST_MAIN( TestQgsLabelingEngine ) #include "testqgslabelingengine.moc" diff --git a/tests/src/core/testqgslayertree.cpp b/tests/src/core/testqgslayertree.cpp index 6d046a7bc4e..85209c8b81d 100644 --- a/tests/src/core/testqgslayertree.cpp +++ b/tests/src/core/testqgslayertree.cpp @@ -303,7 +303,7 @@ void TestQgsLayerTree::testShowHideAllSymbolNodes() QgsLayerTree *root = new QgsLayerTree(); QgsLayerTreeLayer *n = new QgsLayerTreeLayer( vl ); root->addChildNode( n ); - QgsLayerTreeModel *m = new QgsLayerTreeModel( root, 0 ); + QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr ); m->refreshLayerLegend( n ); //test that all nodes are initially checked @@ -351,7 +351,7 @@ void TestQgsLayerTree::testFindLegendNode() //create legend with symbology nodes for categorized renderer QgsLayerTree *root = new QgsLayerTree(); - QgsLayerTreeModel *m = new QgsLayerTreeModel( root, 0 ); + QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr ); QVERIFY( !m->findLegendNode( QString( "id" ), QString( "rule" ) ) ); QgsLayerTreeLayer *n = new QgsLayerTreeLayer( vl ); root->addChildNode( n ); @@ -408,7 +408,7 @@ void TestQgsLayerTree::testLegendSymbolGraduated() void TestQgsLayerTree::testLegendSymbolRuleBased() { //test retrieving/setting a rule based renderer's symbol through the legend node - QgsRuleBasedRenderer::Rule *root = new QgsRuleBasedRenderer::Rule( 0 ); + QgsRuleBasedRenderer::Rule *root = new QgsRuleBasedRenderer::Rule( nullptr ); QgsStringMap props; props.insert( QStringLiteral( "color" ), QStringLiteral( "#ff0000" ) ); root->appendChild( new QgsRuleBasedRenderer::Rule( QgsMarkerSymbol::createSimple( props ), 0, 0, QStringLiteral( "\"col1\"=1" ) ) ); @@ -484,7 +484,7 @@ void TestQgsLayerTree::testRendererLegend( QgsFeatureRenderer *renderer ) QgsLayerTree *root = new QgsLayerTree(); QgsLayerTreeLayer *n = new QgsLayerTreeLayer( vl ); root->addChildNode( n ); - QgsLayerTreeModel *m = new QgsLayerTreeModel( root, 0 ); + QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr ); m->refreshLayerLegend( n ); //test initial symbol diff --git a/tests/src/core/testqgslayout.cpp b/tests/src/core/testqgslayout.cpp index 7fb5036dd70..cf30f840de0 100644 --- a/tests/src/core/testqgslayout.cpp +++ b/tests/src/core/testqgslayout.cpp @@ -20,6 +20,19 @@ #include "qgsproject.h" #include "qgslayoutitemmap.h" #include "qgslayoutitemshape.h" +#include "qgslayoutpagecollection.h" +#include "qgslayoutundostack.h" +#include "qgslayoutitemlabel.h" +#include "qgslayoutitempolyline.h" +#include "qgslayoutitemhtml.h" +#include "qgslayoutframe.h" +#include "qgsprintlayout.h" +#include "qgslayoutatlas.h" +#include "qgsreadwritecontext.h" +#include "qgslayoutitemlegend.h" +#include "qgslayertree.h" +#include "qgslayoutitemattributetable.h" +#include "qgsrasterlayer.h" class TestQgsLayout: public QObject { @@ -34,6 +47,7 @@ class TestQgsLayout: public QObject void units(); void name(); void customProperties(); + void writeRetrieveCustomProperties(); void variablesEdited(); void scope(); void referenceMap(); @@ -41,9 +55,20 @@ class TestQgsLayout: public QObject void addItem(); void layoutItems(); void layoutItemByUuid(); + void layoutItemById(); void undoRedoOccurred(); void itemsOnPage(); //test fetching matching items on a set page + void shouldExportPage(); void pageIsEmpty(); + void clear(); + void georeference(); + void clone(); + void legendRestoredFromTemplate(); + void legendRestoredFromTemplateAutoUpdate(); + void attributeTableRestoredFromTemplate(); + void mapLayersRestoredFromTemplate(); + void mapLayersStyleOverrideRestoredFromTemplate(); + void atlasLayerRestoredFromTemplate(); private: QString mReport; @@ -52,6 +77,9 @@ class TestQgsLayout: public QObject void TestQgsLayout::initTestCase() { + QgsApplication::init(); + QgsApplication::initQgis(); + mReport = QStringLiteral( "

Layout Tests

\n" ); } @@ -65,6 +93,7 @@ void TestQgsLayout::cleanupTestCase() myQTextStream << mReport; myFile.close(); } + QgsApplication::exitQgis(); } void TestQgsLayout::init() @@ -101,8 +130,8 @@ void TestQgsLayout::units() //check with dpi conversion layout.setUnits( QgsUnitTypes::LayoutInches ); - layout.context().setDpi( 96.0 ); - QCOMPARE( layout.context().dpi(), 96.0 ); + layout.renderContext().setDpi( 96.0 ); + QCOMPARE( layout.renderContext().dpi(), 96.0 ); QCOMPARE( layout.convertToLayoutUnits( QgsLayoutMeasurement( 96, QgsUnitTypes::LayoutPixels ) ), 1.0 ); QCOMPARE( layout.convertToLayoutUnits( QgsLayoutSize( 96, 96, QgsUnitTypes::LayoutPixels ) ), QSizeF( 1.0, 1.0 ) ); QCOMPARE( layout.convertToLayoutUnits( QgsLayoutPoint( 96, 96, QgsUnitTypes::LayoutPixels ) ), QPointF( 1.0, 1.0 ) ); @@ -138,7 +167,7 @@ void TestQgsLayout::units() void TestQgsLayout::name() { QgsProject p; - QgsLayout layout( &p ); + QgsPrintLayout layout( &p ); QString layoutName = QStringLiteral( "test name" ); layout.setName( layoutName ); QCOMPARE( layout.name(), layoutName ); @@ -173,6 +202,33 @@ void TestQgsLayout::customProperties() delete layout; } +void TestQgsLayout::writeRetrieveCustomProperties() +{ + QgsLayout layout( QgsProject::instance() ); + layout.setCustomProperty( QStringLiteral( "testprop" ), "testval" ); + layout.setCustomProperty( QStringLiteral( "testprop2" ), 5 ); + + //test writing composition with custom properties + QDomImplementation DomImplementation; + QDomDocumentType documentType = + DomImplementation.createDocumentType( + QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); + QDomDocument doc( documentType ); + QDomElement layoutNode = layout.writeXml( doc, QgsReadWriteContext() ); + QVERIFY( !layoutNode.isNull() ); + + //test reading node containing custom properties + QgsLayout readLayout( QgsProject::instance() ); + QVERIFY( readLayout.readXml( layoutNode, doc, QgsReadWriteContext() ) ); + + //test retrieved custom properties + QCOMPARE( readLayout.customProperties().length(), 2 ); + QVERIFY( readLayout.customProperties().contains( QString( "testprop" ) ) ); + QVERIFY( readLayout.customProperties().contains( QString( "testprop2" ) ) ); + QCOMPARE( readLayout.customProperty( "testprop" ).toString(), QString( "testval" ) ); + QCOMPARE( readLayout.customProperty( "testprop2" ).toInt(), 5 ); +} + void TestQgsLayout::variablesEdited() { QgsProject p; @@ -190,7 +246,7 @@ void TestQgsLayout::variablesEdited() void TestQgsLayout::scope() { QgsProject p; - QgsLayout l( &p ); + QgsPrintLayout l( &p ); // no crash std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::layoutScope( nullptr ) ); @@ -231,27 +287,23 @@ void TestQgsLayout::referenceMap() // no maps QVERIFY( !l.referenceMap() ); -#if 0 QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - l.addComposerMap( map ); + map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setExtent( extent ); + l.addLayoutItem( map ); QCOMPARE( l.referenceMap(), map ); -#endif -#if 0 // TODO // add a larger map QgsLayoutItemMap *map2 = new QgsLayoutItemMap( &l ); - map2->setNewExtent( extent ); - map2->setSceneRect( QRectF( 30, 60, 250, 150 ) ); - l.addComposerMap( map2 ); + map2->attemptSetSceneRect( QRectF( 30, 60, 250, 150 ) ); + map2->setExtent( extent ); + l.addLayoutItem( map2 ); + QCOMPARE( l.referenceMap(), map2 ); // explicitly set reference map l.setReferenceMap( map ); QCOMPARE( l.referenceMap(), map ); -#endif - } void TestQgsLayout::bounds() @@ -259,73 +311,62 @@ void TestQgsLayout::bounds() //add some items to a layout QgsProject p; QgsLayout l( &p ); - l.initializeDefaults(); + QgsLayoutItemPage *page = new QgsLayoutItemPage( &l ); + page->setPageSize( "A4", QgsLayoutItemPage::Landscape ); + l.pageCollection()->addPage( page ); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4", QgsLayoutItemPage::Landscape ); + l.pageCollection()->addPage( page2 ); QgsLayoutItemShape *shape1 = new QgsLayoutItemShape( &l ); shape1->attemptResize( QgsLayoutSize( 90, 50 ) ); shape1->attemptMove( QgsLayoutPoint( 90, 50 ) ); - shape1->setItemRotation( 45 ); + shape1->setItemRotation( 45, false ); l.addLayoutItem( shape1 ); QgsLayoutItemShape *shape2 = new QgsLayoutItemShape( &l ); shape2->attemptResize( QgsLayoutSize( 110, 50 ) ); - shape2->attemptMove( QgsLayoutPoint( 100, 150 ) ); + shape2->attemptMove( QgsLayoutPoint( 100, 150 ), true, false, 0 ); l.addLayoutItem( shape2 ); - -#if 0 - QgsLayoutItemRectangularShape *shape3 = new QgsLayoutItemRectangularShape( &l ); + QgsLayoutItemShape *shape3 = new QgsLayoutItemShape( &l ); l.addLayoutItem( shape3 ); - shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 2 ); - QgsLayoutItemRectangularShape *shape4 = new QgsLayoutItemRectangularShape( &l ); + shape3->attemptResize( QgsLayoutSize( 50, 100 ) ); + shape3->attemptMove( QgsLayoutPoint( 210, 30 ), true, false, 1 ); + QgsLayoutItemShape *shape4 = new QgsLayoutItemShape( &l ); l.addLayoutItem( shape4 ); - shape4->setItemPosition( 10, 120, 50, 30, QgsComposerItem::UpperLeft, false, 2 ); + shape4->attemptResize( QgsLayoutSize( 50, 30 ) ); + shape4->attemptMove( QgsLayoutPoint( 10, 120 ), true, false, 1 ); shape4->setVisibility( false ); -#endif //check bounds QRectF layoutBounds = l.layoutBounds( false ); -#if 0 // correct values when 2nd page items are added back in - QGSCOMPARENEAR( layoutBounds.height(), 372.15, 0.01 ); - QGSCOMPARENEAR( layoutBounds.width(), 301.00, 0.01 ); - QGSCOMPARENEAR( layoutBounds.left(), -2, 0.01 ); - QGSCOMPARENEAR( layoutBounds.top(), -2, 0.01 ); + QGSCOMPARENEAR( layoutBounds.height(), 430, 0.01 ); + QGSCOMPARENEAR( layoutBounds.width(), 297.00, 0.01 ); + QGSCOMPARENEAR( layoutBounds.left(), 0.0, 0.01 ); + QGSCOMPARENEAR( layoutBounds.top(), 0.0, 0.01 ); - QRectF compositionBoundsNoPage = l.layoutBounds( true ); - QGSCOMPARENEAR( compositionBoundsNoPage.height(), 320.36, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.width(), 250.30, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.left(), 9.85, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.top(), 49.79, 0.01 ); -#endif + QRectF layoutBoundsNoPage = l.layoutBounds( true ); + QGSCOMPARENEAR( layoutBoundsNoPage.height(), 320.36, 0.01 ); + QGSCOMPARENEAR( layoutBoundsNoPage.width(), 250.30, 0.01 ); + QGSCOMPARENEAR( layoutBoundsNoPage.left(), 9.85, 0.01 ); + QGSCOMPARENEAR( layoutBoundsNoPage.top(), 49.79, 0.01 ); - QGSCOMPARENEAR( layoutBounds.height(), 210.000000, 0.01 ); - QGSCOMPARENEAR( layoutBounds.width(), 297.000000, 0.01 ); - QGSCOMPARENEAR( layoutBounds.left(), 0.00000, 0.01 ); - QGSCOMPARENEAR( layoutBounds.top(), 0.00000, 0.01 ); - - QRectF compositionBoundsNoPage = l.layoutBounds( true ); - QGSCOMPARENEAR( compositionBoundsNoPage.height(), 174.859607, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.width(), 124.859607, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.left(), 85.290393, 0.01 ); - QGSCOMPARENEAR( compositionBoundsNoPage.top(), 25.290393, 0.01 ); - -#if 0 - QRectF page1Bounds = composition->pageItemBounds( 0, true ); + QRectF page1Bounds = l.pageItemBounds( 0, true ); QGSCOMPARENEAR( page1Bounds.height(), 150.36, 0.01 ); QGSCOMPARENEAR( page1Bounds.width(), 155.72, 0.01 ); QGSCOMPARENEAR( page1Bounds.left(), 54.43, 0.01 ); QGSCOMPARENEAR( page1Bounds.top(), 49.79, 0.01 ); - QRectF page2Bounds = composition->pageItemBounds( 1, true ); + QRectF page2Bounds = l.pageItemBounds( 1, true ); QGSCOMPARENEAR( page2Bounds.height(), 100.30, 0.01 ); QGSCOMPARENEAR( page2Bounds.width(), 50.30, 0.01 ); QGSCOMPARENEAR( page2Bounds.left(), 209.85, 0.01 ); QGSCOMPARENEAR( page2Bounds.top(), 249.85, 0.01 ); - QRectF page2BoundsWithHidden = composition->pageItemBounds( 1, false ); + QRectF page2BoundsWithHidden = l.pageItemBounds( 1, false ); QGSCOMPARENEAR( page2BoundsWithHidden.height(), 120.30, 0.01 ); QGSCOMPARENEAR( page2BoundsWithHidden.width(), 250.30, 0.01 ); QGSCOMPARENEAR( page2BoundsWithHidden.left(), 9.85, 0.01 ); QGSCOMPARENEAR( page2BoundsWithHidden.top(), 249.85, 0.01 ); -#endif } void TestQgsLayout::addItem() @@ -405,7 +446,6 @@ void TestQgsLayout::layoutItemByUuid() { QgsProject p; QgsLayout l( &p ); - l.pageCollection()->deletePage( 0 ); QgsLayoutItemShape *shape1 = new QgsLayoutItemShape( &l ); l.addLayoutItem( shape1 ); @@ -422,6 +462,28 @@ void TestQgsLayout::layoutItemByUuid() QCOMPARE( l.itemByUuid( map1->uuid() ), map1 ); } +void TestQgsLayout::layoutItemById() +{ + QgsProject p; + QgsLayout l( &p ); + + QgsLayoutItemShape *shape1 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( shape1 ); + shape1->setId( QStringLiteral( "shape" ) ); + + QgsLayoutItemShape *shape2 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( shape2 ); + shape2->setId( QStringLiteral( "shape" ) ); + + QgsLayoutItemMap *map1 = new QgsLayoutItemMap( &l ); + l.addLayoutItem( map1 ); + map1->setId( QStringLiteral( "map" ) ); + + QVERIFY( !l.itemById( QStringLiteral( "xxx" ) ) ); + QVERIFY( l.itemById( QStringLiteral( "shape" ) ) == shape1 || l.itemById( QStringLiteral( "shape" ) ) == shape2 ); + QCOMPARE( l.itemById( map1->id() ), map1 ); +} + void TestQgsLayout::undoRedoOccurred() { // test emitting undo/redo occurred signal @@ -518,13 +580,13 @@ void TestQgsLayout::itemsOnPage() page3->setPageSize( "A4" ); l.pageCollection()->addPage( page3 ); - QgsLayoutItemShape *label1 = new QgsLayoutItemShape( &l ); + QgsLayoutItemLabel *label1 = new QgsLayoutItemLabel( &l ); l.addLayoutItem( label1 ); label1->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 0 ); - QgsLayoutItemShape *label2 = new QgsLayoutItemShape( &l ); + QgsLayoutItemLabel *label2 = new QgsLayoutItemLabel( &l ); l.addLayoutItem( label2 ); label2->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 0 ); - QgsLayoutItemShape *label3 = new QgsLayoutItemShape( &l ); + QgsLayoutItemLabel *label3 = new QgsLayoutItemLabel( &l ); l.addLayoutItem( label3 ); label3->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 1 ); QgsLayoutItemShape *shape1 = new QgsLayoutItemShape( &l ); @@ -533,10 +595,10 @@ void TestQgsLayout::itemsOnPage() QgsLayoutItemShape *shape2 = new QgsLayoutItemShape( &l ); l.addLayoutItem( shape2 ); shape2->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 1 ); - QgsLayoutItemShape *arrow1 = new QgsLayoutItemShape( &l ); + QgsLayoutItemPolyline *arrow1 = new QgsLayoutItemPolyline( &l ); l.addLayoutItem( arrow1 ); arrow1->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 2 ); - QgsLayoutItemShape *arrow2 = new QgsLayoutItemShape( &l ); + QgsLayoutItemPolyline *arrow2 = new QgsLayoutItemPolyline( &l ); l.addLayoutItem( arrow2 ); arrow2->attemptMove( QgsLayoutPoint( 10, 10 ), true, false, 2 ); @@ -551,6 +613,40 @@ void TestQgsLayout::itemsOnPage() //should be 3 items on page 3 QCOMPARE( items.length(), 3 ); + //check fetching specific item types + QList labels; + l.pageCollection()->itemsOnPage( labels, 0 ); + //should be 2 labels on page 1 + QCOMPARE( labels.length(), 2 ); + l.pageCollection()->itemsOnPage( labels, 1 ); + //should be 1 label on page 2 + QCOMPARE( labels.length(), 1 ); + l.pageCollection()->itemsOnPage( labels, 2 ); + //should be no label on page 3 + QCOMPARE( labels.length(), 0 ); + + QList shapes; + l.pageCollection()->itemsOnPage( shapes, 0 ); + //should be 1 shapes on page 1 + QCOMPARE( shapes.length(), 1 ); + l.pageCollection()->itemsOnPage( shapes, 1 ); + //should be 1 shapes on page 2 + QCOMPARE( shapes.length(), 1 ); + l.pageCollection()->itemsOnPage( shapes, 2 ); + //should be no shapes on page 3 + QCOMPARE( shapes.length(), 0 ); + + QList arrows; + l.pageCollection()->itemsOnPage( arrows, 0 ); + //should be no arrows on page 1 + QCOMPARE( arrows.length(), 0 ); + l.pageCollection()->itemsOnPage( arrows, 1 ); + //should be no arrows on page 2 + QCOMPARE( arrows.length(), 0 ); + l.pageCollection()->itemsOnPage( arrows, 2 ); + //should be 2 arrows on page 3 + QCOMPARE( arrows.length(), 2 ); + l.removeLayoutItem( label1 ); l.removeLayoutItem( label2 ); l.removeLayoutItem( label3 ); @@ -566,6 +662,85 @@ void TestQgsLayout::itemsOnPage() QCOMPARE( items.length(), 1 ); items = l.pageCollection()->itemsOnPage( 2 ); QCOMPARE( items.length(), 1 ); + + l.pageCollection()->itemsOnPage( labels, 0 ); + QCOMPARE( labels.length(), 0 ); + l.pageCollection()->itemsOnPage( labels, 1 ); + QCOMPARE( labels.length(), 0 ); + l.pageCollection()->itemsOnPage( labels, 2 ); + QCOMPARE( labels.length(), 0 ); +} + +void TestQgsLayout::shouldExportPage() +{ + QgsProject proj; + QgsLayout l( &proj ); + QgsLayoutItemPage *page = new QgsLayoutItemPage( &l ); + page->setPageSize( "A4" ); + l.pageCollection()->addPage( page ); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4" ); + l.pageCollection()->addPage( page2 ); + l.renderContext().mIsPreviewRender = false; + + QgsLayoutItemHtml *htmlItem = new QgsLayoutItemHtml( &l ); + //frame on page 1 + QgsLayoutFrame *frame1 = new QgsLayoutFrame( &l, htmlItem ); + frame1->attemptSetSceneRect( QRectF( 0, 0, 100, 100 ) ); + //frame on page 2 + QgsLayoutFrame *frame2 = new QgsLayoutFrame( &l, htmlItem ); + frame2->attemptSetSceneRect( QRectF( 0, 320, 100, 100 ) ); + frame2->setHidePageIfEmpty( true ); + htmlItem->addFrame( frame1 ); + htmlItem->addFrame( frame2 ); + htmlItem->setContentMode( QgsLayoutItemHtml::ManualHtml ); + //short content, so frame 2 should be empty + htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); + htmlItem->loadHtml(); + + QVERIFY( l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) ); + + //long content, so frame 2 should not be empty + htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); + htmlItem->loadHtml(); + + QVERIFY( l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( l.pageCollection()->shouldExportPage( 1 ) ); + + //...and back again... + htmlItem->setHtml( QStringLiteral( "

Test manual html

" ) ); + htmlItem->loadHtml(); + + QVERIFY( l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) ); + + // get rid of frames + l.removeItem( frame1 ); + l.removeItem( frame2 ); + l.removeMultiFrame( htmlItem ); + delete htmlItem; + QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete ); + + QVERIFY( l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( l.pageCollection()->shouldExportPage( 1 ) ); + + // explicitly set exclude from exports + l.pageCollection()->page( 0 )->setExcludeFromExports( true ); + QVERIFY( !l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( l.pageCollection()->shouldExportPage( 1 ) ); + + l.pageCollection()->page( 0 )->setExcludeFromExports( false ); + l.pageCollection()->page( 1 )->setExcludeFromExports( true ); + QVERIFY( l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) ); + + l.pageCollection()->page( 1 )->setExcludeFromExports( false ); + l.pageCollection()->page( 0 )->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromExpression( "1" ) ); + l.pageCollection()->page( 1 )->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromValue( true ) ); + l.refresh(); + QVERIFY( !l.pageCollection()->shouldExportPage( 0 ) ); + QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) ); } void TestQgsLayout::pageIsEmpty() @@ -609,6 +784,486 @@ void TestQgsLayout::pageIsEmpty() QCOMPARE( l.pageCollection()->pageIsEmpty( 2 ), true ); } +void TestQgsLayout::clear() +{ + QgsProject proj; + QgsLayout l( &proj ); + QgsLayoutItemPage *page = new QgsLayoutItemPage( &l ); + page->setPageSize( "A4" ); + l.pageCollection()->addPage( page ); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4" ); + l.pageCollection()->addPage( page2 ); + QgsLayoutItemPage *page3 = new QgsLayoutItemPage( &l ); + page3->setPageSize( "A4" ); + l.pageCollection()->addPage( page3 ); + + //add some items to the composition + QgsLayoutItemShape *label1 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label1 ); + QPointer< QgsLayoutItem > item1P = label1; + QgsLayoutItemShape *label2 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label2 ); + QPointer< QgsLayoutItem > item2P = label2; + QgsLayoutItemShape *label3 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label3 ); + QPointer< QgsLayoutItem > item3P = label3; + + l.clear(); + QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete ); + QCOMPARE( l.pageCollection()->pageCount(), 0 ); + QVERIFY( !item1P ); + QVERIFY( !item2P ); + QVERIFY( !item3P ); + QList< QgsLayoutItem * > items; + l.layoutItems( items ); + QVERIFY( items.empty() ); + QCOMPARE( l.undoStack()->stack()->count(), 0 ); +} + +void TestQgsLayout::georeference() +{ + QgsRectangle extent( 2000, 2800, 2500, 2900 ); + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + + QgsLayoutExporter exporter( &l ); + + // no map + std::unique_ptr< double [] > t = exporter.computeGeoTransform( nullptr ); + QVERIFY( !t ); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setExtent( extent ); + l.addLayoutItem( map ); + + t = exporter.computeGeoTransform( map ); + QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3050, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); + t.reset(); + + // don't specify map + l.setReferenceMap( map ); + t = exporter.computeGeoTransform(); + QGSCOMPARENEAR( t[0], 1925.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211719, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3050, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211694, 0.0001 ); + t.reset(); + + // specify extent + t = exporter.computeGeoTransform( map, QRectF( 70, 100, 50, 60 ) ); + QGSCOMPARENEAR( t[0], 2100.0, 1.0 ); + QGSCOMPARENEAR( t[1], 0.211864, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 2800, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.211864, 0.0001 ); + t.reset(); + + // specify dpi + t = exporter.computeGeoTransform( map, QRectF(), 75 ); + QGSCOMPARENEAR( t[0], 1925.0, 1 ); + QGSCOMPARENEAR( t[1], 0.847603, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[3], 3050.0, 1 ); + QGSCOMPARENEAR( t[4], 0.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( t[5], -0.846774, 0.0001 ); + t.reset(); + + // rotation + map->setMapRotation( 45 ); + t = exporter.computeGeoTransform( map ); + QGSCOMPARENEAR( t[0], 1878.768940, 1 ); + QGSCOMPARENEAR( t[1], 0.149708, 0.0001 ); + QGSCOMPARENEAR( t[2], 0.149708, 0.0001 ); + QGSCOMPARENEAR( t[3], 2761.611652, 1 ); + QGSCOMPARENEAR( t[4], 0.14969, 0.0001 ); + QGSCOMPARENEAR( t[5], -0.14969, 0.0001 ); + t.reset(); +} + +void TestQgsLayout::clone() +{ + QgsProject proj; + QgsLayout l( &proj ); + QgsLayoutItemPage *page = new QgsLayoutItemPage( &l ); + page->setPageSize( "A4" ); + l.pageCollection()->addPage( page ); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4" ); + l.pageCollection()->addPage( page2 ); + QgsLayoutItemPage *page3 = new QgsLayoutItemPage( &l ); + page3->setPageSize( "A4" ); + l.pageCollection()->addPage( page3 ); + + //add some items to the composition + QgsLayoutItemShape *label1 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label1 ); + QgsLayoutItemShape *label2 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label2 ); + QgsLayoutItemShape *label3 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label3 ); + + // clone and check a few poperties + std::unique_ptr< QgsLayout > cloned( l.clone() ); + QVERIFY( cloned.get() ); + QCOMPARE( cloned->pageCollection()->pageCount(), 3 ); + QList< QgsLayoutItem * > items; + cloned->layoutItems( items ); + QCOMPARE( items.count(), 6 ); // 3 pages + 3 items + + // clone a print layout + QgsPrintLayout pl( &proj ); + pl.atlas()->setPageNameExpression( QStringLiteral( "not a real expression" ) ); + std::unique_ptr< QgsPrintLayout > plClone( pl.clone() ); + QVERIFY( plClone.get() ); + QCOMPARE( plClone->atlas()->pageNameExpression(), QStringLiteral( "not a real expression" ) ); +} + + +void TestQgsLayout::legendRestoredFromTemplate() +{ + // load a layer + + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QVERIFY( layer->isValid() ); + QgsProject p; + p.addMapLayer( layer ); + + // create layout + QgsLayout c( &p ); + // add a legend + QgsLayoutItemLegend *legend = new QgsLayoutItemLegend( &c ); + c.addLayoutItem( legend ); + legend->setAutoUpdateModel( false ); + + QgsLegendModel *model = legend->model(); + QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 ); + // make sure we've got right node + QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node ); + QVERIFY( layerNode ); + QCOMPARE( layerNode->layer(), layer ); + + // got it! + layerNode->setCustomProperty( QStringLiteral( "legend/title-label" ), QStringLiteral( "new title!" ) ); + // make sure new title stuck + QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // make a new composition from template + QgsLayout c2( &p ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get legend from new composition + QList< QgsLayoutItemLegend * > legends2; + c2.layoutItems( legends2 ); + QgsLayoutItemLegend *legend2 = legends2.at( 0 ); + QVERIFY( legend2 ); + + QgsLegendModel *model2 = legend2->model(); + QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 ); + QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 ); + QVERIFY( layerNode2 ); + QCOMPARE( layerNode2->layer(), layer ); + QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); + + QString oldId = layer->id(); + // new test + // remove existing layer + p.removeMapLayer( layer ); + + // reload it, with a new id + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + p.addMapLayer( layer2 ); + QVERIFY( oldId != layer2->id() ); + + // load composition from template + QgsLayout c3( &p ); + c3.loadFromTemplate( doc, QgsReadWriteContext() ); + // get legend from new composition + QList< QgsLayoutItemLegend * > legends3; + c3.layoutItems( legends3 ); + QgsLayoutItemLegend *legend3 = legends3.at( 0 ); + QVERIFY( legend3 ); + + //make sure customisation remains intact + QgsLegendModel *model3 = legend3->model(); + QgsLayerTreeNode *node3 = model3->rootGroup()->children().at( 0 ); + QgsLayerTreeLayer *layerNode3 = dynamic_cast< QgsLayerTreeLayer * >( node3 ); + QVERIFY( layerNode3 ); + QCOMPARE( layerNode3->layer(), layer2 ); + QCOMPARE( model3->data( model->node2index( layerNode3 ), Qt::DisplayRole ).toString(), QString( "new title!" ) ); +} + +void TestQgsLayout::legendRestoredFromTemplateAutoUpdate() +{ + // load a layer + + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsProject p; + p.addMapLayer( layer ); + + // create composition + QgsLayout c( &p ); + // add a legend + QgsLayoutItemLegend *legend = new QgsLayoutItemLegend( &c ); + c.addLayoutItem( legend ); + legend->setAutoUpdateModel( true ); + + QgsLegendModel *model = legend->model(); + QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 ); + // make sure we've got right node + QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node ); + QVERIFY( layerNode ); + QCOMPARE( layerNode->layer(), layer ); + QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "points" ) ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + //new project + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsProject p2; + p2.addMapLayer( layer2 ); + + // make a new composition from template + QgsLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get legend from new composition + QList< QgsLayoutItemLegend * > legends2; + c2.layoutItems( legends2 ); + QgsLayoutItemLegend *legend2 = legends2.at( 0 ); + QVERIFY( legend2 ); + + QgsLegendModel *model2 = legend2->model(); + QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 ); + QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 ); + QVERIFY( layerNode2 ); + QCOMPARE( layerNode2->layer(), layer2 ); + QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "points" ) ); +} + +void TestQgsLayout::attributeTableRestoredFromTemplate() +{ + // load some layers + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsVectorLayer *layer2 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "memory" ), QStringLiteral( "memory" ) ); + QgsProject p; + p.addMapLayer( layer2 ); + p.addMapLayer( layer ); + + // create composition + QgsLayout c( &p ); + // add an attribute table + QgsLayoutItemAttributeTable *table = new QgsLayoutItemAttributeTable( &c ); + c.addMultiFrame( table ); + table->setVectorLayer( layer ); + QgsLayoutFrame *frame = new QgsLayoutFrame( &c, table ); + frame->attemptSetSceneRect( QRectF( 1, 1, 10, 10 ) ); + c.addLayoutItem( frame ); + table->addFrame( frame ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // new project + QgsProject p2; + QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsVectorLayer *layer4 = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "memory" ), QStringLiteral( "memory" ) ); + p2.addMapLayer( layer4 ); + p2.addMapLayer( layer3 ); + + // make a new composition from template + QgsLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get table from new composition + QList< QgsLayoutFrame * > frames2; + c2.layoutItems( frames2 ); + QgsLayoutItemAttributeTable *table2 = static_cast< QgsLayoutItemAttributeTable *>( frames2.at( 0 )->multiFrame() ); + QVERIFY( table2 ); + + QCOMPARE( table2->vectorLayer(), layer3 ); +} + +void TestQgsLayout::mapLayersRestoredFromTemplate() +{ + // load some layers + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QFileInfo vectorFileInfo2( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ); + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(), + vectorFileInfo2.completeBaseName(), + QStringLiteral( "ogr" ) ); + QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif" ); + QgsRasterLayer *rl = new QgsRasterLayer( rasterFileInfo.filePath(), + rasterFileInfo.completeBaseName() ); + + QgsProject p; + p.addMapLayer( layer2 ); + p.addMapLayer( layer ); + p.addMapLayer( rl ); + + // create composition + QgsLayout c( &p ); + // add a map + QgsLayoutItemMap *map = new QgsLayoutItemMap( &c ); + map->attemptSetSceneRect( QRectF( 1, 1, 10, 10 ) ); + c.addLayoutItem( map ); + map->setLayers( QList() << layer << layer2 << rl ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // new project + QgsProject p2; + QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(), + vectorFileInfo2.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsRasterLayer *rl5 = new QgsRasterLayer( rasterFileInfo.filePath(), + rasterFileInfo.completeBaseName() ); + p2.addMapLayer( layer4 ); + p2.addMapLayer( layer3 ); + p2.addMapLayer( rl5 ); + + // make a new composition from template + QgsLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get map from new composition + QList< QgsLayoutItemMap * > maps; + c2.layoutItems( maps ); + QgsLayoutItemMap *map2 = static_cast< QgsLayoutItemMap *>( maps.at( 0 ) ); + QVERIFY( map2 ); + + QCOMPARE( map2->layers(), QList() << layer3 << layer4 << rl5 ); +} + +void TestQgsLayout::mapLayersStyleOverrideRestoredFromTemplate() +{ + // load some layers + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QFileInfo vectorFileInfo2( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ); + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(), + vectorFileInfo2.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsProject p; + p.addMapLayer( layer2 ); + p.addMapLayer( layer ); + + // create composition + QgsLayout c( &p ); + // add a map + QgsLayoutItemMap *map = new QgsLayoutItemMap( &c ); + map->attemptSetSceneRect( QRectF( 1, 1, 10, 10 ) ); + c.addLayoutItem( map ); + map->setKeepLayerStyles( true ); + QgsStringMap styles; + // just close your eyes and pretend these are real styles + styles.insert( layer->id(), QStringLiteral( "xxxxx" ) ); + styles.insert( layer2->id(), QStringLiteral( "yyyyy" ) ); + map->setLayerStyleOverrides( styles ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // new project + QgsProject p2; + QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(), + vectorFileInfo2.completeBaseName(), + QStringLiteral( "ogr" ) ); + p2.addMapLayer( layer4 ); + p2.addMapLayer( layer3 ); + + // make a new composition from template + QgsLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // get map from new composition + QList< QgsLayoutItemMap * > maps; + c2.layoutItems( maps ); + QgsLayoutItemMap *map2 = static_cast< QgsLayoutItemMap *>( maps.at( 0 ) ); + QVERIFY( map2 ); + QVERIFY( map2->keepLayerStyles() ); + + QgsStringMap restoredStyles = map2->layerStyleOverrides(); + QVERIFY( restoredStyles.contains( layer3->id() ) ); + QCOMPARE( restoredStyles.value( layer3->id() ).trimmed(), QStringLiteral( "xxxxx" ) ); + QVERIFY( restoredStyles.contains( layer4->id() ) ); + QCOMPARE( restoredStyles.value( layer4->id() ).trimmed(), QStringLiteral( "yyyyy" ) ); +} + +void TestQgsLayout::atlasLayerRestoredFromTemplate() +{ + // load some layers + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" ); + QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + QgsProject p; + p.addMapLayer( layer ); + + // create composition + QgsPrintLayout c( &p ); + // set atlas layer + c.atlas()->setEnabled( true ); + c.atlas()->setCoverageLayer( layer ); + + // save composition to template + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // new project + QgsProject p2; + QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + p2.addMapLayer( layer2 ); + + // make a new composition from template + QgsPrintLayout c2( &p2 ); + c2.loadFromTemplate( doc, QgsReadWriteContext() ); + // check atlas layer + QCOMPARE( c2.atlas()->coverageLayer(), layer2 ); +} + QGSTEST_MAIN( TestQgsLayout ) #include "testqgslayout.moc" diff --git a/tests/src/core/testqgslayoutatlas.cpp b/tests/src/core/testqgslayoutatlas.cpp new file mode 100644 index 00000000000..f76ffa91a26 --- /dev/null +++ b/tests/src/core/testqgslayoutatlas.cpp @@ -0,0 +1,428 @@ +/*************************************************************************** + testqgslayoutatlas.cpp + --------------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsapplication.h" +#include "qgslayout.h" +#include "qgsmultirenderchecker.h" +#include "qgslayoutitemmap.h" +#include "qgslayoutitemmapoverview.h" +#include "qgslayoutatlas.h" +#include "qgslayoutitemlabel.h" +#include "qgsproject.h" +#include "qgsvectorlayer.h" +#include "qgsvectordataprovider.h" +#include "qgssymbol.h" +#include "qgssinglesymbolrenderer.h" +#include "qgsfontutils.h" +#include "qgsprintlayout.h" +#include +#include +#include "qgstest.h" + +class TestQgsLayoutAtlas : public QObject +{ + Q_OBJECT + + public: + TestQgsLayoutAtlas() = default; + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init();// will be called before each testfunction is executed. + void cleanup();// will be called after every testfunction. + + // test filename pattern evaluation + void filename(); + // test rendering with an autoscale atlas + void autoscale_render(); + // test rendering with a fixed scale atlas + void fixedscale_render(); + // test rendering with predefined scales + void predefinedscales_render(); + // test rendering with two atlas-driven maps + void two_map_autoscale_render(); + // test rendering with a hidden coverage + void hiding_render(); + // test rendering with feature sorting + void sorting_render(); + // test rendering with feature filtering + void filtering_render(); + // test render signals + void test_signals(); + // test removing coverage layer while atlas is enabled + void test_remove_layer(); + + private: + QgsPrintLayout *mLayout = nullptr; + QgsLayoutItemLabel *mLabel1 = nullptr; + QgsLayoutItemLabel *mLabel2 = nullptr; + QgsLayoutItemMap *mAtlasMap = nullptr; + QgsLayoutItemMap *mOverview = nullptr; + QgsVectorLayer *mVectorLayer = nullptr; + QgsVectorLayer *mVectorLayer2 = nullptr; + QgsLayoutAtlas *mAtlas = nullptr; + QString mReport; +}; + +void TestQgsLayoutAtlas::initTestCase() +{ + QgsApplication::init(); + QgsApplication::initQgis(); + + //create maplayers from testdata and add to layer registry + QFileInfo vectorFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/france_parts.shp" ); + mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + mVectorLayer2 = new QgsVectorLayer( vectorFileInfo.filePath(), + vectorFileInfo.completeBaseName(), + QStringLiteral( "ogr" ) ); + + QgsVectorSimplifyMethod simplifyMethod; + simplifyMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); + mVectorLayer->setSimplifyMethod( simplifyMethod ); + + mReport = QStringLiteral( "

Composer Atlas Tests

\n" ); +} + +void TestQgsLayoutAtlas::cleanupTestCase() +{ + delete mLayout; + delete mVectorLayer; + QgsApplication::exitQgis(); + + QString myReportFile = QDir::tempPath() + "/qgistest.html"; + QFile myFile( myReportFile ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) + { + QTextStream myQTextStream( &myFile ); + myQTextStream << mReport; + myFile.close(); + } +} + +void TestQgsLayoutAtlas::init() +{ + //create composition with composer map + + // select epsg:2154 + QgsCoordinateReferenceSystem crs; + crs.createFromSrid( 2154 ); + QgsProject::instance()->setCrs( crs ); + mLayout = new QgsPrintLayout( QgsProject::instance() ); + mLayout->initializeDefaults(); + + // fix the renderer, fill with green + QgsStringMap props; + props.insert( QStringLiteral( "color" ), QStringLiteral( "0,127,0" ) ); + QgsFillSymbol *fillSymbol = QgsFillSymbol::createSimple( props ); + QgsSingleSymbolRenderer *renderer = new QgsSingleSymbolRenderer( fillSymbol ); + mVectorLayer->setRenderer( renderer ); + + // the atlas map + mAtlasMap = new QgsLayoutItemMap( mLayout ); + mAtlasMap->attemptSetSceneRect( QRectF( 20, 20, 130, 130 ) ); + mAtlasMap->setFrameEnabled( true ); + mLayout->addLayoutItem( mAtlasMap ); + mAtlasMap->setLayers( QList() << mVectorLayer ); + + mAtlas = mLayout->atlas(); + mAtlas->setCoverageLayer( mVectorLayer ); + mAtlas->setEnabled( true ); + + // an overview + mOverview = new QgsLayoutItemMap( mLayout ); + mOverview->attemptSetSceneRect( QRectF( 180, 20, 50, 50 ) ); + mOverview->setFrameEnabled( true ); + mOverview->overview()->setLinkedMap( mAtlasMap ); + mOverview->setLayers( QList() << mVectorLayer ); + mLayout->addLayoutItem( mOverview ); + mOverview->setExtent( QgsRectangle( 49670.718, 6415139.086, 699672.519, 7065140.887 ) ); + + // set the fill symbol of the overview map + QgsStringMap props2; + props2.insert( QStringLiteral( "color" ), QStringLiteral( "127,0,0,127" ) ); + QgsFillSymbol *fillSymbol2 = QgsFillSymbol::createSimple( props2 ); + mOverview->overview()->setFrameSymbol( fillSymbol2 ); + + // header label + mLabel1 = new QgsLayoutItemLabel( mLayout ); + mLayout->addLayoutItem( mLabel1 ); + mLabel1->setText( QStringLiteral( "[% \"NAME_1\" %] area" ) ); + mLabel1->setFont( QgsFontUtils::getStandardTestFont() ); + mLabel1->setMarginX( 1 ); + mLabel1->setMarginY( 1 ); + //need to explicitly set width, since expression hasn't been evaluated against + //an atlas feature yet and will be shorter than required + mLabel1->attemptSetSceneRect( QRectF( 150, 5, 60, 15 ) ); + + // feature number label + mLabel2 = new QgsLayoutItemLabel( mLayout ); + mLayout->addLayoutItem( mLabel2 ); + mLabel2->setText( QStringLiteral( "# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]" ) ); + mLabel2->setFont( QgsFontUtils::getStandardTestFont() ); + mLabel2->attemptSetSceneRect( QRectF( 150, 200, 60, 15 ) ); + mLabel2->setMarginX( 1 ); + mLabel2->setMarginY( 1 ); + + + qDebug() << "header label font: " << mLabel1->font().toString() << " exactMatch:" << mLabel1->font().exactMatch(); + qDebug() << "feature number label font: " << mLabel2->font().toString() << " exactMatch:" << mLabel2->font().exactMatch(); +} + +void TestQgsLayoutAtlas::cleanup() +{ + delete mLayout; + mLayout = nullptr; +} + +void TestQgsLayoutAtlas::filename() +{ + QString error; + mAtlas->setFilenameExpression( QStringLiteral( "'output_' || @atlas_featurenumber" ), error ); + mAtlas->beginRender(); + for ( int fi = 0; fi < mAtlas->count(); ++fi ) + { + mAtlas->seekTo( fi ); + QString expected = QStringLiteral( "output_%1" ).arg( ( int )( fi + 1 ) ); + QCOMPARE( mAtlas->currentFilename(), expected ); + } + mAtlas->endRender(); +} + + +void TestQgsLayoutAtlas::autoscale_render() +{ + mAtlasMap->setExtent( QgsRectangle( 332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Auto ); + mAtlasMap->setAtlasMargin( 0.10 ); + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_autoscale%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::fixedscale_render() +{ + //TODO QGIS3.0 - setting the extent AFTER setting atlas driven/fixed scaling mode should + //also update the set fixed scale + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_fixedscale%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::predefinedscales_render() +{ + //TODO QGIS3.0 - setting the extent AFTER setting atlas driven/predefined scaling mode should + //also update the atlas map scale + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Predefined ); + + QVector scales; + scales << 1800000.0; + scales << 5000000.0; + mLayout->reportContext().setPredefinedScales( scales ); + { + const QVector &setScales = mLayout->reportContext().predefinedScales(); + for ( int i = 0; i < setScales.size(); i++ ) + { + QVERIFY( setScales[i] == scales[i] ); + } + } + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_predefinedscales%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::two_map_autoscale_render() +{ + mAtlasMap->setExtent( QgsRectangle( 332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Auto ); + mAtlasMap->setAtlasMargin( 0.10 ); + mOverview->setAtlasDriven( true ); + mOverview->setAtlasScalingMode( QgsLayoutItemMap::Auto ); + mOverview->setAtlasMargin( 2.0 ); + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_two_maps%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::hiding_render() +{ + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); + mAtlas->setHideCoverage( true ); + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_hiding%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::sorting_render() +{ + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); + mAtlas->setHideCoverage( false ); + + mAtlas->setSortFeatures( true ); + mAtlas->setSortExpression( QStringLiteral( "NAME_1" ) ); // departement name + mAtlas->setSortAscending( false ); + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_sorting%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::filtering_render() +{ + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); + mAtlas->setHideCoverage( false ); + + mAtlas->setSortFeatures( false ); + + mAtlas->setFilterFeatures( true ); + QString error; + mAtlas->setFilterExpression( QStringLiteral( "substr(NAME_1,1,1)='P'" ), error ); // select only 'Pays de la Loire' + + mAtlas->beginRender(); + + for ( int fit = 0; fit < 1; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + + QgsLayoutChecker checker( QStringLiteral( "atlas_filtering%1" ).arg( ( ( int )fit ) + 1 ), mLayout ); + checker.setControlPathPrefix( QStringLiteral( "atlas" ) ); + QVERIFY( checker.testLayout( mReport, 0, 100 ) ); + } + mAtlas->endRender(); +} + +void TestQgsLayoutAtlas::test_signals() +{ + mAtlasMap->setExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ); + mAtlasMap->setAtlasDriven( true ); + mAtlasMap->setAtlasScalingMode( QgsLayoutItemMap::Fixed ); + mAtlas->setHideCoverage( false ); + mAtlas->setSortFeatures( false ); + mAtlas->setFilterFeatures( false ); + + QSignalSpy spyRenderBegun( mAtlas, &QgsLayoutAtlas::renderBegun ); + QSignalSpy spyRenderEnded( mAtlas, &QgsLayoutAtlas::renderEnded ); + QSignalSpy spyFeatureChanged( mAtlas, &QgsLayoutAtlas::featureChanged ); + QSignalSpy spyPreparedForAtlas( mAtlasMap, &QgsLayoutItemMap::preparedForAtlas ); + mAtlas->beginRender(); + + QCOMPARE( spyRenderBegun.count(), 1 ); + + for ( int fit = 0; fit < 2; ++fit ) + { + mAtlas->seekTo( fit ); + mLabel1->adjustSizeToText(); + } + QCOMPARE( spyPreparedForAtlas.count(), 2 ); + QCOMPARE( spyFeatureChanged.count(), 2 ); + mAtlas->endRender(); + QCOMPARE( spyRenderEnded.count(), 1 ); +} + +void TestQgsLayoutAtlas::test_remove_layer() +{ + QgsProject::instance()->addMapLayer( mVectorLayer2 ); + mAtlas->setCoverageLayer( mVectorLayer2 ); + mAtlas->setEnabled( true ); + + QSignalSpy spyToggled( mAtlas, SIGNAL( toggled( bool ) ) ); + + //remove coverage layer while atlas is enabled + QgsProject::instance()->removeMapLayer( mVectorLayer2->id() ); + mVectorLayer2 = nullptr; + + QVERIFY( !mAtlas->enabled() ); + QVERIFY( spyToggled.count() == 1 ); +} + +QGSTEST_MAIN( TestQgsLayoutAtlas ) +#include "testqgslayoutatlas.moc" diff --git a/tests/src/core/testqgslayoutcontext.cpp b/tests/src/core/testqgslayoutcontext.cpp index 986931a4fa5..0b10ac4cb8a 100644 --- a/tests/src/core/testqgslayoutcontext.cpp +++ b/tests/src/core/testqgslayoutcontext.cpp @@ -15,10 +15,14 @@ * * ***************************************************************************/ -#include "qgslayoutcontext.h" +#include "qgslayoutrendercontext.h" +#include "qgslayoutreportcontext.h" #include "qgis.h" #include "qgsfeature.h" #include "qgsvectorlayer.h" +#include "qgsgeometry.h" +#include "qgsproject.h" +#include "qgslayout.h" #include #include "qgstest.h" #include @@ -40,6 +44,8 @@ class TestQgsLayoutContext: public QObject void renderContextFlags(); void boundingBoxes(); void exportLayer(); + void geometry(); + void scales(); private: QString mReport; @@ -75,40 +81,40 @@ void TestQgsLayoutContext::cleanup() void TestQgsLayoutContext::creation() { - QgsLayoutContext *context = new QgsLayoutContext(); + QgsLayoutRenderContext *context = new QgsLayoutRenderContext( nullptr ); QVERIFY( context ); delete context; } void TestQgsLayoutContext::flags() { - QgsLayoutContext context; - QSignalSpy spyFlagsChanged( &context, &QgsLayoutContext::flagsChanged ); + QgsLayoutRenderContext context( nullptr ); + QSignalSpy spyFlagsChanged( &context, &QgsLayoutRenderContext::flagsChanged ); //test getting and setting flags - context.setFlags( QgsLayoutContext::Flags( QgsLayoutContext::FlagAntialiasing | QgsLayoutContext::FlagUseAdvancedEffects ) ); + context.setFlags( QgsLayoutRenderContext::Flags( QgsLayoutRenderContext::FlagAntialiasing | QgsLayoutRenderContext::FlagUseAdvancedEffects ) ); // default flags, so should be no signal QCOMPARE( spyFlagsChanged.count(), 0 ); - QVERIFY( context.flags() == ( QgsLayoutContext::FlagAntialiasing | QgsLayoutContext::FlagUseAdvancedEffects ) ); - QVERIFY( context.testFlag( QgsLayoutContext::FlagAntialiasing ) ); - QVERIFY( context.testFlag( QgsLayoutContext::FlagUseAdvancedEffects ) ); - QVERIFY( ! context.testFlag( QgsLayoutContext::FlagDebug ) ); - context.setFlag( QgsLayoutContext::FlagDebug ); + QVERIFY( context.flags() == ( QgsLayoutRenderContext::FlagAntialiasing | QgsLayoutRenderContext::FlagUseAdvancedEffects ) ); + QVERIFY( context.testFlag( QgsLayoutRenderContext::FlagAntialiasing ) ); + QVERIFY( context.testFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects ) ); + QVERIFY( ! context.testFlag( QgsLayoutRenderContext::FlagDebug ) ); + context.setFlag( QgsLayoutRenderContext::FlagDebug ); QCOMPARE( spyFlagsChanged.count(), 1 ); - QVERIFY( context.testFlag( QgsLayoutContext::FlagDebug ) ); - context.setFlag( QgsLayoutContext::FlagDebug, false ); + QVERIFY( context.testFlag( QgsLayoutRenderContext::FlagDebug ) ); + context.setFlag( QgsLayoutRenderContext::FlagDebug, false ); QCOMPARE( spyFlagsChanged.count(), 2 ); - QVERIFY( ! context.testFlag( QgsLayoutContext::FlagDebug ) ); - context.setFlag( QgsLayoutContext::FlagDebug, false ); //no change + QVERIFY( ! context.testFlag( QgsLayoutRenderContext::FlagDebug ) ); + context.setFlag( QgsLayoutRenderContext::FlagDebug, false ); //no change QCOMPARE( spyFlagsChanged.count(), 2 ); - context.setFlags( QgsLayoutContext::FlagDebug ); + context.setFlags( QgsLayoutRenderContext::FlagDebug ); QCOMPARE( spyFlagsChanged.count(), 3 ); } void TestQgsLayoutContext::feature() { - QgsLayoutContext context; + QgsLayoutReportContext context( nullptr ); //test removing feature context.setFeature( QgsFeature() ); @@ -124,7 +130,7 @@ void TestQgsLayoutContext::feature() void TestQgsLayoutContext::layer() { - QgsLayoutContext context; + QgsLayoutReportContext context( nullptr ); //test clearing layer context.setLayer( nullptr ); @@ -144,28 +150,36 @@ void TestQgsLayoutContext::layer() void TestQgsLayoutContext::dpi() { - QgsLayoutContext context; + QgsLayoutRenderContext context( nullptr ); + + QSignalSpy spyDpiChanged( &context, &QgsLayoutRenderContext::dpiChanged ); context.setDpi( 600 ); QCOMPARE( context.dpi(), 600.0 ); QCOMPARE( context.measurementConverter().dpi(), 600.0 ); + QCOMPARE( spyDpiChanged.count(), 1 ); + + context.setDpi( 600 ); + QCOMPARE( spyDpiChanged.count(), 1 ); + context.setDpi( 6000 ); + QCOMPARE( spyDpiChanged.count(), 2 ); } void TestQgsLayoutContext::renderContextFlags() { - QgsLayoutContext context; + QgsLayoutRenderContext context( nullptr ); context.setFlags( 0 ); QgsRenderContext::Flags flags = context.renderContextFlags(); QVERIFY( !( flags & QgsRenderContext::Antialiasing ) ); QVERIFY( !( flags & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( flags & QgsRenderContext::ForceVectorOutput ) ); - context.setFlag( QgsLayoutContext::FlagAntialiasing ); + context.setFlag( QgsLayoutRenderContext::FlagAntialiasing ); flags = context.renderContextFlags(); QVERIFY( ( flags & QgsRenderContext::Antialiasing ) ); QVERIFY( !( flags & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( flags & QgsRenderContext::ForceVectorOutput ) ); - context.setFlag( QgsLayoutContext::FlagUseAdvancedEffects ); + context.setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects ); flags = context.renderContextFlags(); QVERIFY( ( flags & QgsRenderContext::Antialiasing ) ); QVERIFY( ( flags & QgsRenderContext::UseAdvancedEffects ) ); @@ -174,7 +188,7 @@ void TestQgsLayoutContext::renderContextFlags() void TestQgsLayoutContext::boundingBoxes() { - QgsLayoutContext context; + QgsLayoutRenderContext context( nullptr ); context.setBoundingBoxesVisible( false ); QVERIFY( !context.boundingBoxesVisible() ); context.setBoundingBoxesVisible( true ); @@ -183,12 +197,64 @@ void TestQgsLayoutContext::boundingBoxes() void TestQgsLayoutContext::exportLayer() { - QgsLayoutContext context; + QgsLayoutRenderContext context( nullptr ); // must default to -1 QCOMPARE( context.currentExportLayer(), -1 ); context.setCurrentExportLayer( 1 ); QCOMPARE( context.currentExportLayer(), 1 ); } +void TestQgsLayoutContext::geometry() +{ + QgsProject p; + QgsLayout l( &p ); + QgsLayoutReportContext context( &l ); + + // no feature set + QVERIFY( context.currentGeometry().isNull() ); + QVERIFY( context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).isNull() ); + + // no layer set + QgsFeature f; + f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 144 -38, 145 -39 )" ) ) ); + context.setFeature( f ); + QCOMPARE( context.currentGeometry().asWkt(), f.geometry().asWkt() ); + QVERIFY( context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).isNull() ); + + //with layer + QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326&field=id_a:integer" ), QStringLiteral( "A" ), QStringLiteral( "memory" ) ); + context.setLayer( layer ); + + QCOMPARE( context.currentGeometry().asWkt(), f.geometry().asWkt() ); + QVERIFY( !context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).isNull() ); + QCOMPARE( context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).asWkt( 0 ), QStringLiteral( "LineString (2412169 2388563, 2500000 2277996)" ) ); + + // should be cached + QCOMPARE( context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).asWkt( 0 ), QStringLiteral( "LineString (2412169 2388563, 2500000 2277996)" ) ); + + // layer crs + QCOMPARE( context.currentGeometry( layer->crs() ).asWkt(), f.geometry().asWkt() ); + + // clear cache + QgsFeature f2; + context.setFeature( f2 ); + QVERIFY( context.currentGeometry().isNull() ); + QVERIFY( context.currentGeometry( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ).isNull() ); + + delete layer; +} + +void TestQgsLayoutContext::scales() +{ + QVector< qreal > scales; + scales << 1 << 15 << 5 << 10; + + QgsLayoutReportContext context( nullptr ); + context.setPredefinedScales( scales ); + + // should be sorted + QCOMPARE( context.predefinedScales(), QVector< qreal >() << 1 << 5 << 10 << 15 ); +} + QGSTEST_MAIN( TestQgsLayoutContext ) #include "testqgslayoutcontext.moc" diff --git a/tests/src/core/testqgslayouthtml.cpp b/tests/src/core/testqgslayouthtml.cpp index ea5e88dff93..b9768b07403 100644 --- a/tests/src/core/testqgslayouthtml.cpp +++ b/tests/src/core/testqgslayouthtml.cpp @@ -268,10 +268,10 @@ void TestQgsLayoutHtml::javascriptSetFeature() QgsProject::instance()->addMapLayers( QList() << childLayer << parentLayer ); -#if 0 //TODO //atlas - mComposition->atlasComposition().setCoverageLayer( parentLayer ); - mComposition->atlasComposition().setEnabled( true ); + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + l.reportContext().setLayer( parentLayer ); QgsRelation rel; rel.setId( QStringLiteral( "rel1" ) ); @@ -281,8 +281,6 @@ void TestQgsLayoutHtml::javascriptSetFeature() rel.addFieldPair( QStringLiteral( "y" ), QStringLiteral( "foreignkey" ) ); QgsProject::instance()->relationManager()->addRelation( rel ); - QgsLayout l( QgsProject::instance() ); - l.initializeDefaults(); QgsLayoutItemHtml *htmlItem = new QgsLayoutItemHtml( &l ); QgsLayoutFrame *htmlFrame = new QgsLayoutFrame( &l, htmlItem ); htmlFrame->attemptSetSceneRect( QRectF( 0, 0, 100, 200 ) ); @@ -298,21 +296,19 @@ void TestQgsLayoutHtml::javascriptSetFeature() " feature.properties['relation one'][0].z + ',' + feature.properties['relation one'][1].z;}" "" ) ); - mComposition->setAtlasMode( QgsComposition::ExportAtlas ); - QVERIFY( mComposition->atlasComposition().beginRender() ); - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); + QgsFeature f; + QgsFeatureIterator it = parentLayer->getFeatures(); + it.nextFeature( f ); + l.reportContext().setFeature( f ); htmlItem->loadHtml(); - QgsLayoutChecker checker( QStringLiteral( "composerhtml_setfeature" ), mComposition ); + QgsLayoutChecker checker( QStringLiteral( "composerhtml_setfeature" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_html" ) ); bool result = checker.testLayout( mReport ); - mComposition->removeMultiFrame( htmlItem ); - delete htmlItem; QVERIFY( result ); QgsProject::instance()->removeMapLayers( QList() << childLayer << parentLayer ); -#endif } diff --git a/tests/src/core/testqgslayoutitem.cpp b/tests/src/core/testqgslayoutitem.cpp index 5ce9d773165..35a32663a1e 100644 --- a/tests/src/core/testqgslayoutitem.cpp +++ b/tests/src/core/testqgslayoutitem.cpp @@ -27,6 +27,8 @@ #include "qgslayoutitemshape.h" #include "qgslayouteffect.h" #include "qgsfillsymbollayer.h" +#include "qgslayoutpagecollection.h" +#include "qgslayoutundostack.h" #include #include #include @@ -48,7 +50,6 @@ class TestItem : public QgsLayoutItem //implement pure virtual methods int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; } - QString stringType() const override { return QStringLiteral( "TestItemType" ); } bool forceResize = false; @@ -167,6 +168,8 @@ class TestQgsLayoutItem: public QObject void excludeFromExports(); void setSceneRect(); void page(); + void itemVariablesFunction(); + void variables(); private: @@ -174,7 +177,7 @@ class TestQgsLayoutItem: public QObject bool renderCheck( QString testName, QImage &image, int mismatchCount ); - QgsLayoutItem *createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original ); + std::unique_ptr< QgsLayoutItem > createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original ); }; @@ -245,7 +248,7 @@ void TestQgsLayoutItem::registry() QVERIFY( registry.itemTypes().isEmpty() ); QVERIFY( !registry.createItem( 1, nullptr ) ); - auto create = []( QgsLayout * layout )->QgsLayoutItem* + auto create = []( QgsLayout * layout )->QgsLayoutItem * { return new TestItem( layout ); }; @@ -256,7 +259,7 @@ void TestQgsLayoutItem::registry() QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemRegistry::typeAdded ); - QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), QIcon(), create, resolve ); + QgsLayoutItemMetadata *metadata = new QgsLayoutItemMetadata( 2, QStringLiteral( "my type" ), create, resolve ); QVERIFY( registry.addLayoutItemType( metadata ) ); QCOMPARE( spyTypeAdded.count(), 1 ); QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), 2 ); @@ -294,9 +297,9 @@ void TestQgsLayoutItem::shouldDrawDebug() QgsProject p; QgsLayout l( &p ); TestItem *item = new TestItem( &l ); - l.context().setFlag( QgsLayoutContext::FlagDebug, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true ); QVERIFY( item->shouldDrawDebugRect() ); - l.context().setFlag( QgsLayoutContext::FlagDebug, false ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, false ); QVERIFY( !item->shouldDrawDebugRect() ); delete item; } @@ -306,9 +309,9 @@ void TestQgsLayoutItem::shouldDrawAntialiased() QgsProject p; QgsLayout l( &p ); TestItem *item = new TestItem( &l ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing, false ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false ); QVERIFY( !item->shouldDrawAntialiased() ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true ); QVERIFY( item->shouldDrawAntialiased() ); delete item; } @@ -325,10 +328,10 @@ void TestQgsLayoutItem::preparePainter() QImage image( QSize( 100, 100 ), QImage::Format_ARGB32 ); QPainter painter; painter.begin( &image ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing, false ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false ); item->preparePainter( &painter ); QVERIFY( !( painter.renderHints() & QPainter::Antialiasing ) ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, true ); item->preparePainter( &painter ); QVERIFY( painter.renderHints() & QPainter::Antialiasing ); delete item; @@ -343,7 +346,7 @@ void TestQgsLayoutItem::debugRect() item->setPos( 100, 100 ); item->setRect( 0, 0, 200, 200 ); l.setSceneRect( 0, 0, 400, 400 ); - l.context().setFlag( QgsLayoutContext::FlagDebug, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true ); QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 ); image.fill( 0 ); QPainter painter( &image ); @@ -363,7 +366,7 @@ void TestQgsLayoutItem::draw() item->setPos( 100, 100 ); item->setRect( 0, 0, 200, 200 ); l.setSceneRect( 0, 0, 400, 400 ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing, false ); //disable antialiasing to limit cross platform differences + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing, false ); //disable antialiasing to limit cross platform differences QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 ); image.fill( 0 ); QPainter painter( &image ); @@ -606,9 +609,26 @@ void TestQgsLayoutItem::dataDefinedSize() QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters ); QCOMPARE( item->rect().width(), 130.0 ); //mm QCOMPARE( item->rect().height(), 30.0 ); //mm + // data defined orientation + item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "portrait" ) ); + item->attemptResize( QgsLayoutSize( 7.0, 1.50, QgsUnitTypes::LayoutCentimeters ) ); + QCOMPARE( item->sizeWithUnits().width(), 3.0 ); + QCOMPARE( item->sizeWithUnits().height(), 13.0 ); + QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters ); + QCOMPARE( item->rect().width(), 30.0 ); //mm + QCOMPARE( item->rect().height(), 130.0 ); //mm + item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty() ); item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemHeight, QgsProperty() ); item->dataDefinedProperties().setProperty( QgsLayoutObject::PresetPaperSize, QgsProperty() ); + item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty::fromValue( "landscape" ) ); + item->attemptResize( QgsLayoutSize( 1.0, 1.50, QgsUnitTypes::LayoutCentimeters ) ); + QCOMPARE( item->sizeWithUnits().width(), 1.5 ); + QCOMPARE( item->sizeWithUnits().height(), 1.0 ); + QCOMPARE( item->sizeWithUnits().units(), QgsUnitTypes::LayoutCentimeters ); + QCOMPARE( item->rect().width(), 15.0 ); //mm + QCOMPARE( item->rect().height(), 10.0 ); //mm + item->dataDefinedProperties().setProperty( QgsLayoutObject::PaperOrientation, QgsProperty() ); //check change of units should apply to data defined size item->dataDefinedProperties().setProperty( QgsLayoutObject::ItemWidth, QgsProperty::fromExpression( QStringLiteral( "4+8" ) ) ); @@ -731,7 +751,7 @@ void TestQgsLayoutItem::resize() //test pixel -> page conversion l.setUnits( QgsUnitTypes::LayoutInches ); - l.context().setDpi( 100.0 ); + l.renderContext().setDpi( 100.0 ); item->refresh(); QCOMPARE( spySizeChanged.count(), 6 ); item->setRect( 0, 0, 1, 2 ); @@ -740,7 +760,7 @@ void TestQgsLayoutItem::resize() QCOMPARE( item->rect().height(), 2.8 ); QCOMPARE( spySizeChanged.count(), 7 ); //changing the dpi should resize the item - l.context().setDpi( 200.0 ); + l.renderContext().setDpi( 200.0 ); item->refresh(); QCOMPARE( item->rect().width(), 0.7 ); QCOMPARE( item->rect().height(), 1.4 ); @@ -748,7 +768,7 @@ void TestQgsLayoutItem::resize() //test page -> pixel conversion l.setUnits( QgsUnitTypes::LayoutPixels ); - l.context().setDpi( 100.0 ); + l.renderContext().setDpi( 100.0 ); item->refresh(); item->setRect( 0, 0, 2, 2 ); QCOMPARE( spySizeChanged.count(), 10 ); @@ -757,7 +777,7 @@ void TestQgsLayoutItem::resize() QCOMPARE( item->rect().height(), 300.0 ); QCOMPARE( spySizeChanged.count(), 11 ); //changing dpi results in item resize - l.context().setDpi( 200.0 ); + l.renderContext().setDpi( 200.0 ); item->refresh(); QCOMPARE( item->rect().width(), 200.0 ); QCOMPARE( item->rect().height(), 600.0 ); @@ -1153,28 +1173,28 @@ void TestQgsLayoutItem::move() //test pixel -> page conversion l.setUnits( QgsUnitTypes::LayoutInches ); - l.context().setDpi( 100.0 ); + l.renderContext().setDpi( 100.0 ); item->refresh(); item->setPos( 1, 2 ); item->attemptMove( QgsLayoutPoint( 140, 280, QgsUnitTypes::LayoutPixels ) ); QCOMPARE( item->scenePos().x(), 1.4 ); QCOMPARE( item->scenePos().y(), 2.8 ); //changing the dpi should move the item - l.context().setDpi( 200.0 ); + l.renderContext().setDpi( 200.0 ); item->refresh(); QCOMPARE( item->scenePos().x(), 0.7 ); QCOMPARE( item->scenePos().y(), 1.4 ); //test page -> pixel conversion l.setUnits( QgsUnitTypes::LayoutPixels ); - l.context().setDpi( 100.0 ); + l.renderContext().setDpi( 100.0 ); item->refresh(); item->setPos( 2, 2 ); item->attemptMove( QgsLayoutPoint( 1, 3, QgsUnitTypes::LayoutInches ) ); QCOMPARE( item->scenePos().x(), 100.0 ); QCOMPARE( item->scenePos().y(), 300.0 ); //changing dpi results in item move - l.context().setDpi( 200.0 ); + l.renderContext().setDpi( 200.0 ); item->refresh(); QCOMPARE( item->scenePos().x(), 200.0 ); QCOMPARE( item->scenePos().y(), 600.0 ); @@ -1200,6 +1220,21 @@ void TestQgsLayoutItem::move() QCOMPARE( item->scenePos().x(), 10.0 ); QCOMPARE( item->scenePos().y(), 12.0 ); + //moveBy + item.reset( new TestItem( &l ) ); + item->attemptMove( QgsLayoutPoint( 5, 9, QgsUnitTypes::LayoutCentimeters ) ); + item->attemptResize( QgsLayoutSize( 4, 6 ) ); + QCOMPARE( item->positionWithUnits().x(), 5.0 ); + QCOMPARE( item->positionWithUnits().y(), 9.0 ); + QCOMPARE( item->scenePos().x(), 50.0 ); + QCOMPARE( item->scenePos().y(), 90.0 ); + item->attemptMoveBy( 5, -6 ); + QCOMPARE( item->positionWithUnits().x(), 5.5 ); + QCOMPARE( item->positionWithUnits().y(), 8.4 ); + QCOMPARE( item->positionWithUnits().units(), QgsUnitTypes::LayoutCentimeters ); + QCOMPARE( item->scenePos().x(), 55.0 ); + QCOMPARE( item->scenePos().y(), 84.0 ); + //item with frame item.reset( new TestItem( &l ) ); item->attemptResize( QgsLayoutSize( 2, 4 ) ); @@ -1353,6 +1388,63 @@ void TestQgsLayoutItem::page() QCOMPARE( item->positionWithUnits(), QgsLayoutPoint( 5, 38, QgsUnitTypes::LayoutCentimeters ) ); } +void TestQgsLayoutItem::itemVariablesFunction() +{ + QgsRectangle extent( 2000, 2800, 2500, 2900 ); + QgsLayout l( QgsProject::instance() ); + + QgsExpression e( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_scale' )" ) ); + // no map + QgsExpressionContext c = l.createExpressionContext(); + QVariant r = e.evaluate( &c ); + QVERIFY( !r.isValid() ); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); + map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setExtent( extent ); + l.addLayoutItem( map ); + map->setId( QStringLiteral( "map_id" ) ); + + c = l.createExpressionContext(); + r = e.evaluate( &c ); + QGSCOMPARENEAR( r.toDouble(), 184764103, 100 ); + + QgsExpression e2( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs' )" ) ); + r = e2.evaluate( &c ); + QCOMPARE( r.toString(), QString( "EPSG:4326" ) ); + + QgsExpression e3( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_crs_definition' )" ) ); + r = e3.evaluate( &c ); + QCOMPARE( r.toString(), QString( "+proj=longlat +datum=WGS84 +no_defs" ) ); + + QgsExpression e4( QStringLiteral( "map_get( item_variables( 'map_id' ), 'map_units' )" ) ); + r = e4.evaluate( &c ); + QCOMPARE( r.toString(), QString( "degrees" ) ); +} + +void TestQgsLayoutItem::variables() +{ + QgsLayout l( QgsProject::instance() ); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::layoutItemScope( map ) ); + int before = scope->variableCount(); + + QgsExpressionContextUtils::setLayoutItemVariable( map, QStringLiteral( "var" ), 5 ); + scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) ); + QCOMPARE( scope->variableCount(), before + 1 ); + QCOMPARE( scope->variable( QStringLiteral( "var" ) ).toInt(), 5 ); + + QVariantMap vars; + vars.insert( QStringLiteral( "var2" ), 7 ); + QgsExpressionContextUtils::setLayoutItemVariables( map, vars ); + scope.reset( QgsExpressionContextUtils::layoutItemScope( map ) ); + QCOMPARE( scope->variableCount(), before + 1 ); + QVERIFY( !scope->hasVariable( QStringLiteral( "var" ) ) ); + QCOMPARE( scope->variable( QStringLiteral( "var2" ) ).toInt(), 7 ); +} + void TestQgsLayoutItem::rotation() { QgsProject proj; @@ -1479,7 +1571,7 @@ void TestQgsLayoutItem::rotation() l.addItem( item ); item->setItemRotation( 45 ); l.setSceneRect( 0, 0, 400, 400 ); - l.context().setFlag( QgsLayoutContext::FlagDebug, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagDebug, true ); QImage image( l.sceneRect().size().toSize(), QImage::Format_ARGB32 ); image.fill( 0 ); QPainter painter( &image ); @@ -1513,7 +1605,7 @@ void TestQgsLayoutItem::writeXml() QDomElement element = rootNode.firstChildElement(); QCOMPARE( element.nodeName(), QString( "LayoutItem" ) ); - QCOMPARE( element.attribute( "type", "" ), item->stringType() ); + QCOMPARE( element.attribute( "type", "" ).toInt(), item->type() ); //check that element has an object node QDomNodeList objectNodeList = element.elementsByTagName( QStringLiteral( "LayoutObject" ) ); @@ -1540,11 +1632,6 @@ void TestQgsLayoutItem::readXml() QVERIFY( !item->readXml( badElement, doc, QgsReadWriteContext() ) ); QVERIFY( !item->readXml( noNode, doc, QgsReadWriteContext() ) ); - //element with wrong type - QDomElement wrongType = doc.createElement( QStringLiteral( "LayoutItem" ) ); - wrongType.setAttribute( QStringLiteral( "type" ), QStringLiteral( "bad" ) ); - QVERIFY( !item->readXml( wrongType, doc, QgsReadWriteContext() ) ); - //try good element QDomElement goodElement = doc.createElement( QStringLiteral( "LayoutItem" ) ); goodElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "TestItemType" ) ); @@ -1560,7 +1647,7 @@ void TestQgsLayoutItem::writeReadXmlProperties() original->dataDefinedProperties().setProperty( QgsLayoutObject::TestProperty, QgsProperty::fromExpression( QStringLiteral( "10 + 40" ) ) ); - original->setReferencePoint( QgsLayoutItem::Middle ); + original->setReferencePoint( QgsLayoutItem::MiddleRight ); original->attemptResize( QgsLayoutSize( 6, 8, QgsUnitTypes::LayoutCentimeters ) ); original->attemptMove( QgsLayoutPoint( 0.05, 0.09, QgsUnitTypes::LayoutMeters ) ); original->setItemRotation( 45.0 ); @@ -1578,7 +1665,7 @@ void TestQgsLayoutItem::writeReadXmlProperties() original->setExcludeFromExports( true ); original->setItemOpacity( 0.75 ); - QgsLayoutItem *copy = createCopyViaXml( &l, original ); + std::unique_ptr< QgsLayoutItem > copy = createCopyViaXml( &l, original ); QCOMPARE( copy->uuid(), original->uuid() ); QCOMPARE( copy->id(), original->id() ); @@ -1588,12 +1675,16 @@ void TestQgsLayoutItem::writeReadXmlProperties() QCOMPARE( dd.propertyType(), QgsProperty::ExpressionBasedProperty ); QCOMPARE( copy->referencePoint(), original->referencePoint() ); QCOMPARE( copy->sizeWithUnits(), original->sizeWithUnits() ); - QCOMPARE( copy->positionWithUnits(), original->positionWithUnits() ); + QGSCOMPARENEAR( copy->positionWithUnits().x(), original->positionWithUnits().x(), 0.001 ); + QGSCOMPARENEAR( copy->positionWithUnits().y(), original->positionWithUnits().y(), 0.001 ); + QCOMPARE( copy->positionWithUnits().units(), original->positionWithUnits().units() ); QCOMPARE( copy->itemRotation(), original->itemRotation() ); + QGSCOMPARENEAR( copy->pos().x(), original->pos().x(), 0.001 ); + QGSCOMPARENEAR( copy->pos().y(), original->pos().y(), 0.001 ); QVERIFY( copy->isLocked() ); QCOMPARE( copy->zValue(), 55.0 ); QVERIFY( !copy->isVisible() ); - QVERIFY( copy->hasFrame() ); + QVERIFY( copy->frameEnabled() ); QCOMPARE( copy->frameStrokeColor(), QColor( 100, 150, 200 ) ); QCOMPARE( copy->frameStrokeWidth(), QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) ); QCOMPARE( copy->frameJoinStyle(), Qt::MiterJoin ); @@ -1602,8 +1693,6 @@ void TestQgsLayoutItem::writeReadXmlProperties() QCOMPARE( copy->blendMode(), QPainter::CompositionMode_Darken ); QVERIFY( copy->excludeFromExports( ) ); QCOMPARE( copy->itemOpacity(), 0.75 ); - - delete copy; delete original; } @@ -1769,15 +1858,45 @@ void TestQgsLayoutItem::blendMode() QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken ); QVERIFY( item->mEffect->isEnabled() ); - l.context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects, false ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, false ); QVERIFY( !item->mEffect->isEnabled() ); - l.context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects, true ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, true ); QVERIFY( item->mEffect->isEnabled() ); item->dataDefinedProperties().setProperty( QgsLayoutObject::BlendMode, QgsProperty::fromExpression( "'lighten'" ) ); item->refreshDataDefinedProperty(); QCOMPARE( item->blendMode(), QPainter::CompositionMode_Darken ); // should not change QCOMPARE( item->mEffect->compositionMode(), QPainter::CompositionMode_Lighten ); + + QgsLayout l2( QgsProject::instance() ); + l2.initializeDefaults(); + QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 ); + mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) ); + mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle ); + QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); + QgsFillSymbol *fillSymbol = new QgsFillSymbol(); + fillSymbol->changeSymbolLayer( 0, simpleFill ); + simpleFill->setColor( QColor( 255, 150, 0 ) ); + mComposerRect1->setSymbol( fillSymbol ); + delete fillSymbol; + + l2.addLayoutItem( mComposerRect1 ); + QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 ); + mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) ); + mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle ); + l2.addLayoutItem( mComposerRect2 ); + QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer(); + QgsFillSymbol *fillSymbol2 = new QgsFillSymbol(); + fillSymbol2->changeSymbolLayer( 0, simpleFill2 ); + simpleFill2->setColor( QColor( 0, 100, 150 ) ); + mComposerRect2->setSymbol( fillSymbol2 ); + delete fillSymbol2; + + mComposerRect2->setBlendMode( QPainter::CompositionMode_Multiply ); + + QgsLayoutChecker checker( QStringLiteral( "composereffects_blend" ), &l2 ); + checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) ); + QVERIFY( checker.testLayout( mReport ) ); } void TestQgsLayoutItem::opacity() @@ -1796,6 +1915,37 @@ void TestQgsLayoutItem::opacity() item->refreshDataDefinedProperty(); QCOMPARE( item->itemOpacity(), 0.75 ); // should not change QCOMPARE( item->opacity(), 0.35 ); + + + QgsLayout l2( QgsProject::instance() ); + l2.initializeDefaults(); + QgsLayoutItemShape *mComposerRect1 = new QgsLayoutItemShape( &l2 ); + mComposerRect1->attemptSetSceneRect( QRectF( 20, 20, 150, 100 ) ); + mComposerRect1->setShapeType( QgsLayoutItemShape::Rectangle ); + QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); + QgsFillSymbol *fillSymbol = new QgsFillSymbol(); + fillSymbol->changeSymbolLayer( 0, simpleFill ); + simpleFill->setColor( QColor( 255, 150, 0 ) ); + mComposerRect1->setSymbol( fillSymbol ); + delete fillSymbol; + + l2.addLayoutItem( mComposerRect1 ); + QgsLayoutItemShape *mComposerRect2 = new QgsLayoutItemShape( &l2 ); + mComposerRect2->attemptSetSceneRect( QRectF( 50, 50, 150, 100 ) ); + mComposerRect2->setShapeType( QgsLayoutItemShape::Rectangle ); + l2.addLayoutItem( mComposerRect2 ); + QgsSimpleFillSymbolLayer *simpleFill2 = new QgsSimpleFillSymbolLayer(); + QgsFillSymbol *fillSymbol2 = new QgsFillSymbol(); + fillSymbol2->changeSymbolLayer( 0, simpleFill2 ); + simpleFill2->setColor( QColor( 0, 100, 150 ) ); + mComposerRect2->setSymbol( fillSymbol2 ); + delete fillSymbol2; + + mComposerRect2->setItemOpacity( 0.5 ); + + QgsLayoutChecker checker( QStringLiteral( "composereffects_transparency" ), &l2 ); + checker.setControlPathPrefix( QStringLiteral( "composer_effects" ) ); + QVERIFY( checker.testLayout( mReport ) ); } void TestQgsLayoutItem::excludeFromExports() @@ -1837,7 +1987,7 @@ void TestQgsLayoutItem::excludeFromExports() QVERIFY( checker.testLayout( mReport ) ); } -QgsLayoutItem *TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original ) +std::unique_ptr TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original ) { //save original item to xml QDomImplementation DomImplementation; @@ -1850,10 +2000,10 @@ QgsLayoutItem *TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayout original->writeXml( rootNode, doc, QgsReadWriteContext() ); //create new item and restore settings from xml - TestItem *copy = new TestItem( layout ); + std::unique_ptr< TestItem > copy = qgis::make_unique< TestItem >( layout ); copy->readXml( rootNode.firstChildElement(), doc, QgsReadWriteContext() ); - return copy; + return std::move( copy ); } QGSTEST_MAIN( TestQgsLayoutItem ) diff --git a/tests/src/core/testqgslayoutitemgroup.cpp b/tests/src/core/testqgslayoutitemgroup.cpp index 21bb72018b7..266123b2856 100644 --- a/tests/src/core/testqgslayoutitemgroup.cpp +++ b/tests/src/core/testqgslayoutitemgroup.cpp @@ -18,14 +18,12 @@ #include "qgslayoutitemgroup.h" #include "qgslayout.h" #include "qgslayoutitemshape.h" -#include "qgscomposerlabel.h" -#include "qgscomposerpolygon.h" -#include "qgscomposition.h" #include "qgsmultirenderchecker.h" #include "qgsapplication.h" #include "qgslogger.h" #include "qgsproject.h" #include "qgsfillsymbollayer.h" +#include "qgslayoutundostack.h" #include #include @@ -80,6 +78,7 @@ void TestQgsLayoutItemGroup::initTestCase() void TestQgsLayoutItemGroup::cleanupTestCase() { + QgsApplication::exitQgis(); } void TestQgsLayoutItemGroup::init() @@ -185,6 +184,9 @@ void TestQgsLayoutItemGroup::createGroup() QVERIFY( item->isGroupMember() ); QCOMPARE( item->parentGroup(), group ); QCOMPARE( item2->parentGroup(), group ); + + delete item; + delete item2; } void TestQgsLayoutItemGroup::ungroup() diff --git a/tests/src/core/testqgslayoutlabel.cpp b/tests/src/core/testqgslayoutlabel.cpp index 53aa95f7f79..06dd7961ec1 100644 --- a/tests/src/core/testqgslayoutlabel.cpp +++ b/tests/src/core/testqgslayoutlabel.cpp @@ -23,6 +23,9 @@ #include "qgsmultirenderchecker.h" #include "qgsfontutils.h" #include "qgsproject.h" +#include "qgsprintlayout.h" +#include "qgslayoutatlas.h" +#include "qgslayoutpagecollection.h" #include #include "qgstest.h" @@ -43,13 +46,15 @@ class TestQgsLayoutLabel : public QObject // test simple expression evaluation void evaluation(); // test expression evaluation when a feature is set - void feature_evaluation(); + void featureEvaluationUsingAtlas(); + void featureEvaluationUsingContext(); // test page expressions - void page_evaluation(); + void pageEvaluation(); void marginMethods(); //tests getting/setting margins void render(); void renderAsHtml(); void renderAsHtmlRelative(); + void labelRotation(); private: QgsVectorLayer *mVectorLayer = nullptr; @@ -98,9 +103,7 @@ void TestQgsLayoutLabel::evaluation() QgsLayout l( QgsProject::instance() ); l.initializeDefaults(); -#if 0 //TODO - l.atlasComposition().setCoverageLayer( mVectorLayer ); -#endif + l.reportContext().setLayer( mVectorLayer ); QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l ); label->setMargin( 1 ); @@ -143,66 +146,93 @@ void TestQgsLayoutLabel::evaluation() } } -void TestQgsLayoutLabel::feature_evaluation() +void TestQgsLayoutLabel::featureEvaluationUsingAtlas() { - QgsLayout l( QgsProject::instance() ); + QgsPrintLayout l( QgsProject::instance() ); l.initializeDefaults(); -#if 0 //TODO - l.atlasComposition().setCoverageLayer( mVectorLayer ); -#endif QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l ); label->setMargin( 1 ); l.addLayoutItem( label ); -#if 0 //TODO - l.atlasComposition().setEnabled( true ); - l.setAtlasMode( QgsComposition::ExportAtlas ); - l.atlasComposition().updateFeatures(); - l.atlasComposition().prepareForFeature( 0 ); - + l.atlas()->setEnabled( true ); + l.atlas()->setCoverageLayer( mVectorLayer ); + QVERIFY( l.atlas()->beginRender() ); + l.atlas()->seekTo( 0 ); { // evaluation with a feature label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); - QString evaluated = label->displayText(); + QString evaluated = label->currentText(); QString expected = QStringLiteral( "Basse-Normandie_ok" ); QCOMPARE( evaluated, expected ); } - mComposition->atlasComposition().prepareForFeature( 1 ); + l.atlas()->seekTo( 1 ); { // evaluation with a feature label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); - QString evaluated = label->displayText(); + QString evaluated = label->currentText(); QString expected = QStringLiteral( "Bretagne_ok" ); QCOMPARE( evaluated, expected ); } - mComposition->atlasComposition().setEnabled( false ); -#endif } -void TestQgsLayoutLabel::page_evaluation() +void TestQgsLayoutLabel::featureEvaluationUsingContext() { -#if 0 //TODO + // just using context, no atlas QgsLayout l( QgsProject::instance() ); l.initializeDefaults(); - mComposition->atlasComposition().setCoverageLayer( mVectorLayer ); QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l ); label->setMargin( 1 ); l.addLayoutItem( label ); - mComposition->setNumPages( 2 ); + QgsFeature f; + QgsFeatureIterator it = mVectorLayer->getFeatures(); + + l.reportContext().setLayer( mVectorLayer ); + it.nextFeature( f ); + l.reportContext().setFeature( f ); + { + // evaluation with a feature + label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); + QString evaluated = label->currentText(); + QString expected = QStringLiteral( "Basse-Normandie_ok" ); + QCOMPARE( evaluated, expected ); + } + it.nextFeature( f ); + l.reportContext().setFeature( f ); + { + // evaluation with a feature + label->setText( QStringLiteral( "[%\"NAME_1\"||'_ok'%]" ) ); + QString evaluated = label->currentText(); + QString expected = QStringLiteral( "Bretagne_ok" ); + QCOMPARE( evaluated, expected ); + } +} + +void TestQgsLayoutLabel::pageEvaluation() +{ + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4", QgsLayoutItemPage::Landscape ); + l.pageCollection()->addPage( page2 ); + l.reportContext().setLayer( mVectorLayer ); + + QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l ); + label->setMargin( 1 ); + l.addLayoutItem( label ); + { label->setText( QStringLiteral( "[%@layout_page||'/'||@layout_numpages%]" ) ); - QString evaluated = label->displayText(); + QString evaluated = label->currentText(); QString expected = QStringLiteral( "1/2" ); QCOMPARE( evaluated, expected ); // move to the second page and re-evaluate - label->setItemPosition( 0, 320 ); - QCOMPARE( label->displayText(), QString( "2/2" ) ); + label->attemptMove( QgsLayoutPoint( 0, 320 ) ); + QCOMPARE( label->currentText(), QString( "2/2" ) ); } -#endif } void TestQgsLayoutLabel::marginMethods() @@ -287,5 +317,25 @@ void TestQgsLayoutLabel::renderAsHtmlRelative() QVERIFY( checker.testLayout( mReport, 0, 0 ) ); } +void TestQgsLayoutLabel::labelRotation() +{ + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l ); + label->setMargin( 1 ); + l.addLayoutItem( label ); + label->setText( QStringLiteral( "test label" ) ); + label->setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ), 30 ) ); + label->attemptMove( QgsLayoutPoint( 70, 70 ) ); + label->adjustSizeToText(); + label->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) ); + label->setBackgroundEnabled( true ); + label->setItemRotation( 135 ); + + QgsLayoutChecker checker( QStringLiteral( "layoutrotation_label" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); + QVERIFY( checker.testLayout( mReport, 0, 0 ) ); +} + QGSTEST_MAIN( TestQgsLayoutLabel ) #include "testqgslayoutlabel.moc" diff --git a/tests/src/core/testqgslayoutmap.cpp b/tests/src/core/testqgslayoutmap.cpp index 2bfed585b05..b8229d62116 100644 --- a/tests/src/core/testqgslayoutmap.cpp +++ b/tests/src/core/testqgslayoutmap.cpp @@ -27,6 +27,9 @@ #include "qgsproject.h" #include "qgsmapthemecollection.h" #include "qgsproperty.h" +#include "qgslayoutpagecollection.h" +#include "qgslayoutitempolyline.h" +#include "qgsreadwritecontext.h" #include #include "qgstest.h" @@ -44,13 +47,16 @@ class TestQgsLayoutMap : public QObject void cleanup();// will be called after every testfunction. void id(); void render(); -#if 0 void uniqueId(); //test if map id is adapted when doing copy paste void worldFileGeneration(); // test world file generation -#endif + void mapPolygonVertices(); // test mapPolygon function with no map rotation void dataDefinedLayers(); //test data defined layer string void dataDefinedStyles(); //test data defined styles + void rasterized(); + void layersToRender(); + void mapRotation(); + void mapItemRotation(); private: QgsRasterLayer *mRasterLayer = nullptr; @@ -159,21 +165,26 @@ void TestQgsLayoutMap::render() QVERIFY( checker.testLayout( mReport, 0, 0 ) ); } -#if 0 + void TestQgsLayoutMap::uniqueId() { + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + l.addLayoutItem( map ); + QDomDocument doc; QDomElement documentElement = doc.createElement( QStringLiteral( "ComposerItemClipboard" ) ); - mComposerMap->writeXml( documentElement, doc ); - mComposition->addItemsFromXml( documentElement, doc, false ); + map->writeXml( documentElement, doc, QgsReadWriteContext() ); + l.addItemsFromXml( documentElement, doc, QgsReadWriteContext() ); //test if both composer maps have different ids - const QgsComposerMap *newMap = 0; - QList mapList = mComposition->composerMapItems(); - QList::const_iterator mapIt = mapList.constBegin(); - for ( ; mapIt != mapList.constEnd(); ++mapIt ) + QgsLayoutItemMap *newMap = 0; + QList mapList; + l.layoutItems( mapList ); + for ( auto mapIt = mapList.constBegin() ; mapIt != mapList.constEnd(); ++mapIt ) { - if ( *mapIt != mComposerMap ) + if ( *mapIt != map ) { newMap = *mapIt; break; @@ -182,24 +193,35 @@ void TestQgsLayoutMap::uniqueId() QVERIFY( newMap ); - int oldId = mComposerMap->id(); - int newId = newMap->id(); - - mComposition->removeComposerItem( const_cast( newMap ) ); + QString oldId = map->displayName(); + QString newId = newMap->displayName(); QVERIFY( oldId != newId ); } void TestQgsLayoutMap::worldFileGeneration() { - mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); - mComposerMap->setMapRotation( 30.0 ); + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4", QgsLayoutItemPage::Landscape ); + l.pageCollection()->addPage( page2 ); - mComposition->setGenerateWorldFile( true ); - mComposition->setReferenceMap( mComposerMap ); + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) ); + map->setFrameEnabled( true ); + map->setLayers( QList() << mRasterLayer ); + l.addLayoutItem( map ); + + map->setExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) ); + map->setMapRotation( 30.0 ); + + l.setReferenceMap( map ); + + QgsLayoutExporter exporter( &l ); double a, b, c, d, e, f; - mComposition->computeWorldFileParameters( a, b, c, d, e, f ); + exporter.computeWorldFileParameters( a, b, c, d, e, f ); QGSCOMPARENEAR( a, 4.18048, 0.001 ); QGSCOMPARENEAR( b, 2.41331, 0.001 ); @@ -209,8 +231,8 @@ void TestQgsLayoutMap::worldFileGeneration() QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); //test with map on second page. Parameters should be the same - mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); - mComposition->computeWorldFileParameters( a, b, c, d, e, f ); + map->attemptMove( QgsLayoutPoint( 20, 20 ), true, false, 1 ); + exporter.computeWorldFileParameters( a, b, c, d, e, f ); QGSCOMPARENEAR( a, 4.18048, 0.001 ); QGSCOMPARENEAR( b, 2.41331, 0.001 ); @@ -220,8 +242,8 @@ void TestQgsLayoutMap::worldFileGeneration() QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 ); //test computing parameters for specific region - mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 ); - mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f ); + map->attemptMove( QgsLayoutPoint( 20, 20 ), true, false, 1 ); + exporter.computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f ); QGSCOMPARENEAR( a, 4.18061, 0.001 ); QGSCOMPARENEAR( b, 2.41321, 0.001 ); @@ -229,12 +251,7 @@ void TestQgsLayoutMap::worldFileGeneration() QGSCOMPARENEAR( d, 2.4137, 0.001 ); QGSCOMPARENEAR( e, -4.1798, 0.001 ); QGSCOMPARENEAR( f, 3.35331e+06, 1e+03 ); - - mComposition->setGenerateWorldFile( false ); - mComposerMap->setMapRotation( 0.0 ); - } -#endif void TestQgsLayoutMap::mapPolygonVertices() @@ -326,7 +343,6 @@ void TestQgsLayoutMap::dataDefinedLayers() result = map->layersToRender(); QVERIFY( result.isEmpty() ); - //test with atlas feature evaluation QgsVectorLayer *atlasLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string" ), QStringLiteral( "atlas" ), QStringLiteral( "memory" ) ); QVERIFY( atlasLayer->isValid() ); @@ -335,25 +351,26 @@ void TestQgsLayoutMap::dataDefinedLayers() QgsFeature f2( atlasLayer->dataProvider()->fields(), 1 ); f2.setAttribute( QStringLiteral( "col1" ), mPointsLayer->name() ); atlasLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 ); -#if 0 //TODO - mComposition->atlasComposition().setCoverageLayer( atlasLayer ); - mComposition->atlasComposition().setEnabled( true ); - mComposition->setAtlasMode( QgsComposition::ExportAtlas ); - mComposition->atlasComposition().beginRender(); - mComposition->atlasComposition().prepareForFeature( 0 ); + + l.reportContext().setLayer( atlasLayer ); + QgsFeature f; + QgsFeatureIterator it = atlasLayer->getFeatures(); + it.nextFeature( f ); + l.reportContext().setFeature( f ); map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromField( QStringLiteral( "col1" ) ) ); result = map->layersToRender(); QCOMPARE( result.count(), 1 ); QCOMPARE( result.at( 0 ), mLinesLayer ); - mComposition->atlasComposition().prepareForFeature( 1 ); + it.nextFeature( f ); + l.reportContext().setFeature( f ); result = map->layersToRender(); QCOMPARE( result.count(), 1 ); QCOMPARE( result.at( 0 ), mPointsLayer ); - mComposition->atlasComposition().setEnabled( false ); - delete atlasLayer; + it.nextFeature( f ); + l.reportContext().setFeature( f ); -#endif + delete atlasLayer; //render test map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression( @@ -429,5 +446,142 @@ void TestQgsLayoutMap::dataDefinedStyles() QVERIFY( checker.testLayout( mReport, 0, 0 ) ); } +void TestQgsLayoutMap::rasterized() +{ + // test a map which must be rasterized + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptMove( QgsLayoutPoint( 20, 30 ) ); + map->attemptResize( QgsLayoutSize( 200, 100 ) ); + map->setFrameEnabled( true ); + map->setExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) ); + QList layers = QList() << mLinesLayer; + map->setLayers( layers ); + map->setBackgroundColor( Qt::yellow ); + l.addLayoutItem( map ); + + // add some guide lines, just for reference + QPolygonF points; + points << QPointF( 0, 30 ) << QPointF( 10, 30 ); + QgsLayoutItemPolyline *line1 = new QgsLayoutItemPolyline( points, &l ); + l.addLayoutItem( line1 ); + points.clear(); + points << QPointF( 0, 30 + map->rect().height() ) << QPointF( 10, 30 + map->rect().height() ); + QgsLayoutItemPolyline *line2 = new QgsLayoutItemPolyline( points, &l ); + l.addLayoutItem( line2 ); + points.clear(); + points << QPointF( 20, 0 ) << QPointF( 20, 20 ); + QgsLayoutItemPolyline *line3 = new QgsLayoutItemPolyline( points, &l ); + l.addLayoutItem( line3 ); + points.clear(); + points << QPointF( 220, 0 ) << QPointF( 220, 20 ); + QgsLayoutItemPolyline *line4 = new QgsLayoutItemPolyline( points, &l ); + l.addLayoutItem( line4 ); + + // force rasterization + QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( "test", map ); + grid->setIntervalX( 10 ); + grid->setIntervalY( 10 ); + grid->setBlendMode( QPainter::CompositionMode_Darken ); + grid->setAnnotationEnabled( true ); + grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Left ); + grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Top ); + grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Right ); + grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Bottom ); + map->grids()->addGrid( grid ); + map->updateBoundingRect(); + + QVERIFY( map->containsAdvancedEffects() ); + + QgsLayoutChecker checker( QStringLiteral( "layoutmap_rasterized" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_map" ) ); + QVERIFY( checker.testLayout( mReport, 0, 0 ) ); + + // try rendering again, without requiring rasterization, for comparison + // (we can use the same test image, because CompositionMode_Darken doesn't actually have any noticeable + // rendering differences for the black grid!) + grid->setBlendMode( QPainter::CompositionMode_SourceOver ); + QVERIFY( !map->containsAdvancedEffects() ); + QVERIFY( checker.testLayout( mReport, 0, 0 ) ); +} + +void TestQgsLayoutMap::layersToRender() +{ + QList layers = QList() << mRasterLayer << mPolysLayer << mPointsLayer << mLinesLayer; + QList layers2 = QList() << mRasterLayer << mPolysLayer << mLinesLayer; + + QgsLayout l( QgsProject::instance() ); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->setLayers( layers ); + l.addLayoutItem( map ); + + QCOMPARE( map->layersToRender(), layers ); + + // hide coverage layer + l.reportContext().setLayer( mPointsLayer ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagHideCoverageLayer, true ); + QCOMPARE( map->layersToRender(), layers2 ); + + l.renderContext().setFlag( QgsLayoutRenderContext::FlagHideCoverageLayer, false ); + QCOMPARE( map->layersToRender(), layers ); +} + +void TestQgsLayoutMap::mapRotation() +{ + QgsProject p; + QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/rgb256x256.png" ); + QgsRasterLayer *layer = new QgsRasterLayer( rasterFileInfo.filePath(), + rasterFileInfo.completeBaseName() ); + QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 1, 2, 3 ); + layer->setRenderer( rasterRenderer ); + p.addMapLayer( layer ); + + QgsLayout l( &p ); + l.initializeDefaults(); + + //test map rotation + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 20, 20, 100, 50 ) ); + map->setFrameEnabled( true ); + l.addLayoutItem( map ); + map->setExtent( QgsRectangle( 0, -192, 256, -64 ) ); + map->setMapRotation( 90 ); + map->setLayers( QList() << layer ); + + QgsLayoutChecker checker( QStringLiteral( "composerrotation_maprotation" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); + QVERIFY( checker.testLayout( mReport, 0, 200 ) ); +} + +void TestQgsLayoutMap::mapItemRotation() +{ + QgsProject p; + QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/rgb256x256.png" ); + QgsRasterLayer *layer = new QgsRasterLayer( rasterFileInfo.filePath(), + rasterFileInfo.completeBaseName() ); + QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 1, 2, 3 ); + layer->setRenderer( rasterRenderer ); + p.addMapLayer( layer ); + + QgsLayout l( &p ); + l.initializeDefaults(); + + //test map rotation + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 20, 50, 100, 50 ) ); + map->setFrameEnabled( true ); + l.addLayoutItem( map ); + map->setExtent( QgsRectangle( 0, -192, 256, -64 ) ); + map->setItemRotation( 90 ); + map->setLayers( QList() << layer ); + + QgsLayoutChecker checker( QStringLiteral( "composerrotation_mapitemrotation" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); + QVERIFY( checker.testLayout( mReport, 0, 200 ) ); +} + QGSTEST_MAIN( TestQgsLayoutMap ) #include "testqgslayoutmap.moc" diff --git a/tests/src/core/testqgslayoutmapoverview.cpp b/tests/src/core/testqgslayoutmapoverview.cpp index 67b3167215f..d6ed6e9a394 100644 --- a/tests/src/core/testqgslayoutmapoverview.cpp +++ b/tests/src/core/testqgslayoutmapoverview.cpp @@ -108,22 +108,18 @@ void TestQgsLayoutMapOverview::overviewMap() l.addLayoutItem( overviewMap ); map->setExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in overviewMap->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( map ); + overviewMap->overview()->setLinkedMap( map ); - QCOMPARE( overviewMap->overview()->frameMap(), map ); - QCOMPARE( overviewMap->overview()->frameMapUuid(), map->uuid() ); - overviewMap->overview()->setFrameMap( nullptr ); - QVERIFY( !overviewMap->overview()->frameMap() ); - QVERIFY( overviewMap->overview()->frameMapUuid().isEmpty() ); - overviewMap->overview()->setFrameMapUuid( map->uuid() ); - QCOMPARE( overviewMap->overview()->frameMap(), map ); - QCOMPARE( overviewMap->overview()->frameMapUuid(), map->uuid() ); - overviewMap->overview()->setFrameMapUuid( QString() ); - QVERIFY( !overviewMap->overview()->frameMap() ); - QVERIFY( overviewMap->overview()->frameMapUuid().isEmpty() ); + QCOMPARE( overviewMap->overview()->linkedMap(), map ); + overviewMap->overview()->setLinkedMap( nullptr ); + QVERIFY( !overviewMap->overview()->linkedMap() ); + overviewMap->overview()->setLinkedMap( map ); + QCOMPARE( overviewMap->overview()->linkedMap(), map ); + overviewMap->overview()->setLinkedMap( nullptr ); + QVERIFY( !overviewMap->overview()->linkedMap() ); //render - overviewMap->overview()->setFrameMap( map ); + overviewMap->overview()->setLinkedMap( map ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); @@ -149,7 +145,7 @@ void TestQgsLayoutMapOverview::overviewMapRotated() map->setExtent( QgsRectangle( 96, -144, 160, -112 ) ); //zoom in map->setMapRotation( 30 ); overviewMap->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( map ); + overviewMap->overview()->setLinkedMap( map ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_rotated" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); @@ -175,7 +171,7 @@ void TestQgsLayoutMapOverview::overviewMapRotated2() map->setExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in overviewMap->setMapRotation( 30 ); overviewMap->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMap->overview()->setFrameMap( map ); + overviewMap->overview()->setLinkedMap( map ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_rotated2" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); @@ -200,7 +196,7 @@ void TestQgsLayoutMapOverview::overviewMapBlending() l.addLayoutItem( overviewMapBlend ); map->setExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in overviewMapBlend->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapBlend->overview()->setFrameMap( map ); + overviewMapBlend->overview()->setLinkedMap( map ); overviewMapBlend->overview()->setBlendMode( QPainter::CompositionMode_Multiply ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_blending" ), &l ); @@ -227,7 +223,7 @@ void TestQgsLayoutMapOverview::overviewMapInvert() l.addLayoutItem( overviewMapInvert ); map->setExtent( QgsRectangle( 96, -152, 160, -120 ) ); //zoom in overviewMapInvert->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapInvert->overview()->setFrameMap( map ); + overviewMapInvert->overview()->setLinkedMap( map ); overviewMapInvert->overview()->setInverted( true ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_invert" ), &l ); @@ -254,7 +250,7 @@ void TestQgsLayoutMapOverview::overviewMapCenter() l.addLayoutItem( overviewMapCenter ); map->setExtent( QgsRectangle( 192, -288, 320, -224 ) ); overviewMapCenter->setExtent( QgsRectangle( 0, -256, 256, 0 ) ); - overviewMapCenter->overview()->setFrameMap( map ); + overviewMapCenter->overview()->setLinkedMap( map ); overviewMapCenter->overview()->setCentered( true ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_center" ), &l ); @@ -285,7 +281,7 @@ void TestQgsLayoutMapOverview::overviewReprojected() map->setExtent( QgsRectangle( 93, -64.245, 120.6, -45 ) ); overviewMap->setExtent( QgsRectangle( 4712502, -7620278, 10872777, -2531356 ) ); - overviewMap->overview()->setFrameMap( map ); + overviewMap->overview()->setLinkedMap( map ); QgsLayoutChecker checker( QStringLiteral( "composermap_overview_reprojected" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_mapoverview" ) ); diff --git a/tests/src/core/testqgslayoutmultiframe.cpp b/tests/src/core/testqgslayoutmultiframe.cpp index 87b4b715be0..e3724262fbe 100644 --- a/tests/src/core/testqgslayoutmultiframe.cpp +++ b/tests/src/core/testqgslayoutmultiframe.cpp @@ -23,6 +23,9 @@ #include "qgsapplication.h" #include "qgsproject.h" #include "qgslayoutitemhtml.h" +#include "qgslayoutpagecollection.h" +#include "qgslayoutundostack.h" +#include "qgsreadwritecontext.h" #include #include "qgstest.h" @@ -45,6 +48,8 @@ class TestQgsLayoutMultiFrame : public QObject void undoRedoRemovedFrame(); //test that undo doesn't crash with removed frames void undoRedoRemovedFrame2(); void registry(); + void deleteFrame(); + void writeReadXml(); private: QgsLayout *mLayout = nullptr; @@ -69,11 +74,6 @@ class TestMultiFrame : public QgsLayoutMultiFrame return QgsLayoutItemRegistry::PluginItem + 1; } - QString stringType() const override - { - return QStringLiteral( "TestMultiFrame" ); - } - void render( QgsRenderContext &, const QRectF &, int, const QStyleOptionGraphicsItem * ) override { @@ -533,7 +533,7 @@ void TestQgsLayoutMultiFrame::registry() QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemRegistry::multiFrameTypeAdded ); - QgsLayoutMultiFrameMetadata *metadata = new QgsLayoutMultiFrameMetadata( QgsLayoutItemRegistry::PluginItem + 1, QStringLiteral( "TestMultiFrame" ), QIcon(), create, resolve ); + QgsLayoutMultiFrameMetadata *metadata = new QgsLayoutMultiFrameMetadata( QgsLayoutItemRegistry::PluginItem + 1, QStringLiteral( "TestMultiFrame" ), create, resolve ); QVERIFY( registry.addLayoutMultiFrameType( metadata ) ); QCOMPARE( spyTypeAdded.count(), 1 ); QCOMPARE( spyTypeAdded.value( 0 ).at( 0 ).toInt(), QgsLayoutItemRegistry::PluginItem + 1 ); @@ -559,5 +559,67 @@ void TestQgsLayoutMultiFrame::registry() QVERIFY( props.isEmpty() ); } +void TestQgsLayoutMultiFrame::deleteFrame() +{ + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + + QgsLayoutItemHtml *htmlItem = new QgsLayoutItemHtml( &l ); + QgsLayoutFrame *frame1 = new QgsLayoutFrame( &l, htmlItem ); + frame1->attemptSetSceneRect( QRectF( 0, 0, 100, 200 ) ); + htmlItem->addFrame( frame1 ); + QgsLayoutFrame *frame2 = new QgsLayoutFrame( &l, htmlItem ); + frame2->attemptSetSceneRect( QRectF( 0, 0, 100, 200 ) ); + htmlItem->addFrame( frame2 ); + + QCOMPARE( htmlItem->frameCount(), 2 ); + QCOMPARE( htmlItem->frames(), QList< QgsLayoutFrame * >() << frame1 << frame2 ); + l.removeLayoutItem( frame1 ); + QCOMPARE( htmlItem->frameCount(), 1 ); + QCOMPARE( htmlItem->frames(), QList< QgsLayoutFrame * >() << frame2 ); + l.removeLayoutItem( frame2 ); + QCOMPARE( htmlItem->frameCount(), 0 ); + QVERIFY( htmlItem->frames().empty() ); +} + +void TestQgsLayoutMultiFrame::writeReadXml() +{ + QgsProject p; + + // create layout + QgsLayout c( &p ); + // add an multiframe + QgsLayoutItemHtml *html = new QgsLayoutItemHtml( &c ); + c.addMultiFrame( html ); + html->setHtml( QStringLiteral( "hi" ) ); + QgsLayoutFrame *frame = new QgsLayoutFrame( &c, html ); + frame->attemptSetSceneRect( QRectF( 1, 1, 10, 10 ) ); + c.addLayoutItem( frame ); + html->addFrame( frame ); + + QCOMPARE( frame->multiFrame(), html ); + QCOMPARE( html->frameCount(), 1 ); + QCOMPARE( html->frames(), QList< QgsLayoutFrame * >() << frame ); + + // save layout to xml + QDomDocument doc; + doc.appendChild( c.writeXml( doc, QgsReadWriteContext() ) ); + + // make a new layout from xml + QgsLayout c2( &p ); + c2.readXml( doc.childNodes().at( 0 ).toElement(), doc, QgsReadWriteContext() ); + // get table from new layout + QList< QgsLayoutFrame * > frames2; + c2.layoutItems( frames2 ); + QCOMPARE( frames2.count(), 1 ); + QgsLayoutFrame *frame2 = frames2.at( 0 ); + + QgsLayoutItemHtml *html2 = static_cast< QgsLayoutItemHtml *>( frame2->multiFrame() ); + QVERIFY( html2 ); + QCOMPARE( html2->html(), QStringLiteral( "hi" ) ); + QCOMPARE( html2->frameCount(), 1 ); + QCOMPARE( html2->frames(), QList< QgsLayoutFrame * >() << frame2 ); +} + QGSTEST_MAIN( TestQgsLayoutMultiFrame ) #include "testqgslayoutmultiframe.moc" diff --git a/tests/src/core/testqgslayoutobject.cpp b/tests/src/core/testqgslayoutobject.cpp index a76c4de2dad..1f55e17fd81 100644 --- a/tests/src/core/testqgslayoutobject.cpp +++ b/tests/src/core/testqgslayoutobject.cpp @@ -20,6 +20,7 @@ #include "qgstest.h" #include "qgsproject.h" #include "qgsreadwritecontext.h" +#include "qgsprintlayout.h" class TestQgsLayoutObject: public QObject { @@ -126,7 +127,7 @@ void TestQgsLayoutObject::context() { QgsProject p; p.setTitle( QStringLiteral( "my title" ) ); - QgsLayout l( &p ); + QgsPrintLayout l( &p ); l.setName( QStringLiteral( "my layout" ) ); QgsLayoutObject *object = new QgsLayoutObject( nullptr ); diff --git a/tests/src/core/testqgslayoutpage.cpp b/tests/src/core/testqgslayoutpage.cpp index 09bcbff51cd..8105fe987d2 100644 --- a/tests/src/core/testqgslayoutpage.cpp +++ b/tests/src/core/testqgslayoutpage.cpp @@ -25,6 +25,7 @@ #include "qgsfillsymbollayer.h" #include "qgslinesymbollayer.h" #include "qgsmultirenderchecker.h" +#include "qgslayoutpagecollection.h" #include #include "qgstest.h" @@ -41,6 +42,7 @@ class TestQgsLayoutPage : public QObject void pageSize(); void decodePageOrientation(); void grid(); + void defaultPaper(); void transparentPaper(); //test totally transparent paper style void borderedPaper(); //test page with border void markerLinePaper(); //test page with marker line borde @@ -169,6 +171,19 @@ void TestQgsLayoutPage::grid() } +void TestQgsLayoutPage::defaultPaper() +{ + QgsProject p; + QgsLayout l( &p ); + std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( &l ) ); + page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) ); + l.pageCollection()->addPage( page.release() ); + + QgsLayoutChecker checker( QStringLiteral( "composerpaper_default" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); + QVERIFY( checker.testLayout( mReport ) ); +} + void TestQgsLayoutPage::transparentPaper() { QgsProject p; @@ -243,7 +258,7 @@ void TestQgsLayoutPage::hiddenPages() simpleFill->setStrokeColor( Qt::transparent ); l.pageCollection()->setPageStyleSymbol( fillSymbol.get() ); - l.context().setPagesVisible( false ); + l.renderContext().setPagesVisible( false ); QgsLayoutChecker checker( QStringLiteral( "composerpaper_hidden" ), &l ); checker.setControlPathPrefix( QStringLiteral( "composer_paper" ) ); diff --git a/tests/src/core/testqgslayoutpolyline.cpp b/tests/src/core/testqgslayoutpolyline.cpp new file mode 100644 index 00000000000..aa8423e86fc --- /dev/null +++ b/tests/src/core/testqgslayoutpolyline.cpp @@ -0,0 +1,113 @@ + +/*************************************************************************** + testqgslayoutpolyline.cpp + --------------------------- + begin : January 2018 + copyright : (C) 2018 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsapplication.h" +#include "qgslayout.h" +#include "qgsmultirenderchecker.h" +#include "qgslayoutitempolyline.h" +#include "qgsproject.h" + +#include +#include +#include "qgstest.h" + +class TestQgsLayoutPolyline : public QObject +{ + Q_OBJECT + + public: + TestQgsLayoutPolyline() = default; + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init();// will be called before each testfunction is executed. + void cleanup();// will be called after every testfunction. + void drawArrowHead(); + + private: + bool renderCheck( const QString &testName, QImage &image, int mismatchCount = 0 ); + + QString mReport; +}; + +void TestQgsLayoutPolyline::initTestCase() +{ + QgsApplication::init(); + QgsApplication::initQgis(); + + mReport = QStringLiteral( "

Layout Polyline Tests

\n" ); +} + +void TestQgsLayoutPolyline::cleanupTestCase() +{ + QString myReportFile = QDir::tempPath() + "/qgistest.html"; + QFile myFile( myReportFile ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) + { + QTextStream myQTextStream( &myFile ); + myQTextStream << mReport; + myFile.close(); + } + + QgsApplication::exitQgis(); +} + +void TestQgsLayoutPolyline::init() +{ + +} + +void TestQgsLayoutPolyline::cleanup() +{ + +} + +void TestQgsLayoutPolyline::drawArrowHead() +{ + //test drawing with no painter + QgsLayoutItemPolyline::drawArrowHead( nullptr, 100, 100, 90, 30 ); + + //test painting on to image + QImage testImage = QImage( 250, 250, QImage::Format_RGB32 ); + testImage.fill( qRgb( 152, 219, 249 ) ); + QPainter testPainter; + testPainter.begin( &testImage ); + QgsLayoutItemPolyline::drawArrowHead( &testPainter, 100, 100, 45, 30 ); + testPainter.end(); + QVERIFY( renderCheck( "composerutils_drawarrowhead", testImage, 40 ) ); +} + + +bool TestQgsLayoutPolyline::renderCheck( const QString &testName, QImage &image, int mismatchCount ) +{ + mReport += "

" + testName + "

\n"; + QString myTmpDir = QDir::tempPath() + '/'; + QString myFileName = myTmpDir + testName + ".png"; + image.save( myFileName, "PNG" ); + QgsRenderChecker myChecker; + myChecker.setControlPathPrefix( QStringLiteral( "composer_utils" ) ); + myChecker.setControlName( "expected_" + testName ); + myChecker.setRenderedImage( myFileName ); + bool myResultFlag = myChecker.compareImages( testName, mismatchCount ); + mReport += myChecker.report(); + return myResultFlag; +} + +QGSTEST_MAIN( TestQgsLayoutPolyline ) +#include "testqgslayoutpolyline.moc" diff --git a/tests/src/core/testqgslayoutscalebar.cpp b/tests/src/core/testqgslayoutscalebar.cpp index f3fd4ea82aa..296ac310fef 100644 --- a/tests/src/core/testqgslayoutscalebar.cpp +++ b/tests/src/core/testqgslayoutscalebar.cpp @@ -112,7 +112,7 @@ void TestQgsLayoutScaleBar::singleBox() QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l ); scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) ); l.addLayoutItem( scalebar ); - scalebar->setMap( map ); + scalebar->setLinkedMap( map ); scalebar->setFont( QgsFontUtils::getStandardTestFont() ); scalebar->setUnits( QgsUnitTypes::DistanceMeters ); scalebar->setUnitsPerSegment( 2000 ); @@ -140,7 +140,7 @@ void TestQgsLayoutScaleBar::singleBoxAlpha() QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l ); scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) ); l.addLayoutItem( scalebar ); - scalebar->setMap( map ); + scalebar->setLinkedMap( map ); scalebar->setFont( QgsFontUtils::getStandardTestFont() ); scalebar->setUnits( QgsUnitTypes::DistanceMeters ); scalebar->setUnitsPerSegment( 2000 ); @@ -173,7 +173,7 @@ void TestQgsLayoutScaleBar::doubleBox() QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l ); scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) ); l.addLayoutItem( scalebar ); - scalebar->setMap( map ); + scalebar->setLinkedMap( map ); scalebar->setFont( QgsFontUtils::getStandardTestFont() ); scalebar->setUnits( QgsUnitTypes::DistanceMeters ); scalebar->setUnitsPerSegment( 2000 ); @@ -207,7 +207,7 @@ void TestQgsLayoutScaleBar::numeric() QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l ); scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) ); l.addLayoutItem( scalebar ); - scalebar->setMap( map ); + scalebar->setLinkedMap( map ); scalebar->setFont( QgsFontUtils::getStandardTestFont() ); scalebar->setUnits( QgsUnitTypes::DistanceMeters ); scalebar->setUnitsPerSegment( 2000 ); @@ -240,7 +240,7 @@ void TestQgsLayoutScaleBar::tick() QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l ); scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) ); l.addLayoutItem( scalebar ); - scalebar->setMap( map ); + scalebar->setLinkedMap( map ); scalebar->setFont( QgsFontUtils::getStandardTestFont() ); scalebar->setUnits( QgsUnitTypes::DistanceMeters ); scalebar->setUnitsPerSegment( 2000 ); diff --git a/tests/src/core/testqgslayoutshapes.cpp b/tests/src/core/testqgslayoutshapes.cpp index 4ae266db0fb..c77e08a3ed9 100644 --- a/tests/src/core/testqgslayoutshapes.cpp +++ b/tests/src/core/testqgslayoutshapes.cpp @@ -49,6 +49,7 @@ class TestQgsLayoutShapes : public QObject void symbol(); //test is styling shapes via symbol is working void readWriteXml(); void bounds(); + void shapeRotation(); private: @@ -226,7 +227,7 @@ void TestQgsLayoutShapes::readWriteXml() { QgsProject p; QgsLayout l( &p ); - QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l ); + std::unique_ptr< QgsLayoutItemShape > shape = qgis::make_unique< QgsLayoutItemShape >( &l ); shape->setShapeType( QgsLayoutItemShape::Triangle ); QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); QgsFillSymbol *fillSymbol = new QgsFillSymbol(); @@ -235,6 +236,7 @@ void TestQgsLayoutShapes::readWriteXml() simpleFill->setStrokeColor( Qt::yellow ); simpleFill->setStrokeWidth( 6 ); shape->setSymbol( fillSymbol ); + delete fillSymbol; //save original item to xml QDomImplementation DomImplementation; @@ -247,7 +249,7 @@ void TestQgsLayoutShapes::readWriteXml() shape->writeXml( rootNode, doc, QgsReadWriteContext() ); //create new item and restore settings from xml - QgsLayoutItemShape *copy = new QgsLayoutItemShape( &l ); + std::unique_ptr< QgsLayoutItemShape > copy = qgis::make_unique< QgsLayoutItemShape >( &l ); QVERIFY( copy->readXml( rootNode.firstChildElement(), doc, QgsReadWriteContext() ) ); QCOMPARE( copy->shapeType(), QgsLayoutItemShape::Triangle ); QCOMPARE( copy->symbol()->symbolLayer( 0 )->color().name(), QStringLiteral( "#00ff00" ) ); @@ -258,7 +260,7 @@ void TestQgsLayoutShapes::bounds() { QgsProject p; QgsLayout l( &p ); - QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l ); + std::unique_ptr< QgsLayoutItemShape > shape = qgis::make_unique< QgsLayoutItemShape >( &l ); shape->attemptMove( QgsLayoutPoint( 20, 20 ) ); shape->attemptResize( QgsLayoutSize( 150, 100 ) ); @@ -269,6 +271,7 @@ void TestQgsLayoutShapes::bounds() simpleFill->setStrokeColor( Qt::yellow ); simpleFill->setStrokeWidth( 6 ); shape->setSymbol( fillSymbol ); + delete fillSymbol; // scene bounding rect should include symbol outline QRectF bounds = shape->sceneBoundingRect(); @@ -285,5 +288,31 @@ void TestQgsLayoutShapes::bounds() QCOMPARE( bounds.bottom(), 103.0 ); } +void TestQgsLayoutShapes::shapeRotation() +{ + QgsProject p; + QgsLayout l( &p ); + l.initializeDefaults(); + + QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l ); + shape->attemptSetSceneRect( QRectF( 70, 70, 150, 100 ) ); + shape->setItemRotation( 45 ); + + //setup simple fill + QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer(); + QgsFillSymbol *fillSymbol = new QgsFillSymbol(); + fillSymbol->changeSymbolLayer( 0, simpleFill ); + simpleFill->setColor( QColor( 255, 150, 0 ) ); + //simpleFill->setStrokeColor( Qt::yellow ); + //simpleFill->setStrokeWidth( 6 ); + shape->setSymbol( fillSymbol ); + delete fillSymbol; + + l.addLayoutItem( shape ); + QgsLayoutChecker checker( QStringLiteral( "composerrotation_shape" ), &l ); + checker.setControlPathPrefix( QStringLiteral( "composer_items" ) ); + QVERIFY( checker.testLayout( mReport ) ); +} + QGSTEST_MAIN( TestQgsLayoutShapes ) #include "testqgslayoutshapes.moc" diff --git a/tests/src/core/testqgslayouttable.cpp b/tests/src/core/testqgslayouttable.cpp index 3fb8ef8e1cd..af5b188b71f 100644 --- a/tests/src/core/testqgslayouttable.cpp +++ b/tests/src/core/testqgslayouttable.cpp @@ -521,9 +521,8 @@ void TestQgsLayoutTable::attributeTableRepeat() void TestQgsLayoutTable::attributeTableAtlasSource() { -#if 0 //TODO - QgsLayoutItemAttributeTable *table = new QgsLayoutItemAttributeTable( mComposition, false ); - + QgsLayout l( QgsProject::instance() ); + QgsLayoutItemAttributeTable *table = new QgsLayoutItemAttributeTable( &l ); table->setSource( QgsLayoutItemAttributeTable::AtlasFeature ); @@ -534,13 +533,15 @@ void TestQgsLayoutTable::attributeTableAtlasSource() vectorFileInfo.completeBaseName(), QStringLiteral( "ogr" ) ); QgsProject::instance()->addMapLayer( vectorLayer ); - mComposition->atlasComposition().setCoverageLayer( vectorLayer ); - mComposition->atlasComposition().setEnabled( true ); - QVERIFY( mComposition->atlasComposition().beginRender() ); + l.reportContext().setLayer( vectorLayer ); - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); - QCOMPARE( table->contents()->length(), 1 ); - QgsComposerTableRow row = table->contents()->at( 0 ); + QgsFeature f; + QgsFeatureIterator it = vectorLayer->getFeatures(); + it.nextFeature( f ); + l.reportContext().setFeature( f ); + + QCOMPARE( table->contents().length(), 1 ); + QgsLayoutTableRow row = table->contents().at( 0 ); //check a couple of results QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); @@ -551,9 +552,11 @@ void TestQgsLayoutTable::attributeTableAtlasSource() QCOMPARE( row.at( 5 ), QVariant( 2 ) ); //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 1 ) ); - QCOMPARE( table->contents()->length(), 1 ); - row = table->contents()->at( 0 ); + it.nextFeature( f ); + l.reportContext().setFeature( f ); + + QCOMPARE( table->contents().length(), 1 ); + row = table->contents().at( 0 ); QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); QCOMPARE( row.at( 1 ), QVariant( 0 ) ); QCOMPARE( row.at( 2 ), QVariant( 1 ) ); @@ -562,9 +565,11 @@ void TestQgsLayoutTable::attributeTableAtlasSource() QCOMPARE( row.at( 5 ), QVariant( 6 ) ); //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 2 ) ); - QCOMPARE( table->contents()->length(), 1 ); - row = table->contents()->at( 0 ); + it.nextFeature( f ); + l.reportContext().setFeature( f ); + + QCOMPARE( table->contents().length(), 1 ); + row = table->contents().at( 0 ); QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); QCOMPARE( row.at( 1 ), QVariant( 85 ) ); QCOMPARE( row.at( 2 ), QVariant( 3 ) ); @@ -572,15 +577,10 @@ void TestQgsLayoutTable::attributeTableAtlasSource() QCOMPARE( row.at( 4 ), QVariant( 1 ) ); QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - mComposition->atlasComposition().endRender(); - //try for a crash when removing current atlas layer QgsProject::instance()->removeMapLayer( vectorLayer->id() ); table->refreshAttributes(); - mComposition->removeMultiFrame( table ); - delete table; -#endif } @@ -611,10 +611,13 @@ void TestQgsLayoutTable::attributeTableRelationSource() QgsProject::instance()->addMapLayer( atlasLayer ); -#if 0 //TODO //setup atlas - mComposition->atlasComposition().setCoverageLayer( atlasLayer ); - mComposition->atlasComposition().setEnabled( true ); + l.reportContext().setLayer( atlasLayer ); + + QgsFeature f; + QgsFeatureIterator it = atlasLayer->getFeatures(); + it.nextFeature( f ); + l.reportContext().setFeature( f ); //create a relation QgsRelation relation; @@ -624,18 +627,15 @@ void TestQgsLayoutTable::attributeTableRelationSource() relation.addFieldPair( QStringLiteral( "Class" ), QStringLiteral( "Class" ) ); QgsProject::instance()->relationManager()->addRelation( relation ); - QgsLayoutItemAttributeTable *table = new QgsLayoutItemAttributeTable( mComposition, false ); + table = new QgsLayoutItemAttributeTable( &l ); table->setMaximumNumberOfFeatures( 50 ); table->setSource( QgsLayoutItemAttributeTable::RelationChildren ); table->setRelationId( relation.id() ); - QVERIFY( mComposition->atlasComposition().beginRender() ); - QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) ); + QCOMPARE( f.attribute( "Class" ).toString(), QString( "Jet" ) ); + QCOMPARE( table->contents().length(), 8 ); - QCOMPARE( mComposition->atlasComposition().feature().attribute( "Class" ).toString(), QString( "Jet" ) ); - QCOMPARE( table->contents()->length(), 8 ); - - QgsComposerTableRow row = table->contents()->at( 0 ); + QgsLayoutTableRow row = table->contents().at( 0 ); //check a couple of results QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); @@ -644,14 +644,14 @@ void TestQgsLayoutTable::attributeTableRelationSource() QCOMPARE( row.at( 3 ), QVariant( 2 ) ); QCOMPARE( row.at( 4 ), QVariant( 0 ) ); QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - row = table->contents()->at( 1 ); + row = table->contents().at( 1 ); QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); QCOMPARE( row.at( 1 ), QVariant( 85 ) ); QCOMPARE( row.at( 2 ), QVariant( 3 ) ); QCOMPARE( row.at( 3 ), QVariant( 1 ) ); QCOMPARE( row.at( 4 ), QVariant( 1 ) ); QCOMPARE( row.at( 5 ), QVariant( 2 ) ); - row = table->contents()->at( 2 ); + row = table->contents().at( 2 ); QCOMPARE( row.at( 0 ), QVariant( "Jet" ) ); QCOMPARE( row.at( 1 ), QVariant( 95 ) ); QCOMPARE( row.at( 2 ), QVariant( 3 ) ); @@ -660,17 +660,18 @@ void TestQgsLayoutTable::attributeTableRelationSource() QCOMPARE( row.at( 5 ), QVariant( 2 ) ); //next atlas feature - QVERIFY( mComposition->atlasComposition().prepareForFeature( 1 ) ); - QCOMPARE( mComposition->atlasComposition().feature().attribute( "Class" ).toString(), QString( "Biplane" ) ); - QCOMPARE( table->contents()->length(), 5 ); - row = table->contents()->at( 0 ); + it.nextFeature( f ); + l.reportContext().setFeature( f ); + QCOMPARE( f.attribute( "Class" ).toString(), QString( "Biplane" ) ); + QCOMPARE( table->contents().length(), 5 ); + row = table->contents().at( 0 ); QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); QCOMPARE( row.at( 1 ), QVariant( 0 ) ); QCOMPARE( row.at( 2 ), QVariant( 1 ) ); QCOMPARE( row.at( 3 ), QVariant( 3 ) ); QCOMPARE( row.at( 4 ), QVariant( 3 ) ); QCOMPARE( row.at( 5 ), QVariant( 6 ) ); - row = table->contents()->at( 1 ); + row = table->contents().at( 1 ); QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) ); QCOMPARE( row.at( 1 ), QVariant( 340 ) ); QCOMPARE( row.at( 2 ), QVariant( 1 ) ); @@ -678,16 +679,10 @@ void TestQgsLayoutTable::attributeTableRelationSource() QCOMPARE( row.at( 4 ), QVariant( 3 ) ); QCOMPARE( row.at( 5 ), QVariant( 6 ) ); - mComposition->atlasComposition().endRender(); - //try for a crash when removing current atlas layer QgsProject::instance()->removeMapLayer( atlasLayer->id() ); table->refreshAttributes(); - - mComposition->removeMultiFrame( table ); - delete table; -#endif } void TestQgsLayoutTable::contentsContainsRow() @@ -1003,7 +998,7 @@ void TestQgsLayoutTable::wrapChar() table->setHeaderFont( QgsFontUtils::getStandardTestFont() ); table->setBackgroundColor( Qt::yellow ); - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); + std::unique_ptr< QgsVectorLayer > multiLineLayer = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); QVERIFY( multiLineLayer->isValid() ); QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); f1.setAttribute( QStringLiteral( "col1" ), "multiline\nstring" ); @@ -1012,7 +1007,7 @@ void TestQgsLayoutTable::wrapChar() multiLineLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 ); table->setMaximumNumberOfFeatures( 1 ); - table->setVectorLayer( multiLineLayer ); + table->setVectorLayer( multiLineLayer.get() ); table->setWrapString( QStringLiteral( "in" ) ); QVector expectedRows; @@ -1044,7 +1039,7 @@ void TestQgsLayoutTable::autoWrap() table->setHeaderFont( QgsFontUtils::getStandardTestFont() ); table->setBackgroundColor( Qt::yellow ); - QgsVectorLayer *multiLineLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); + std::unique_ptr< QgsVectorLayer > multiLineLayer = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "Point?field=col1:string&field=col2:string&field=col3:string" ), QStringLiteral( "multiline" ), QStringLiteral( "memory" ) ); QVERIFY( multiLineLayer->isValid() ); QgsFeature f1( multiLineLayer->dataProvider()->fields(), 1 ); f1.setAttribute( QStringLiteral( "col1" ), "long multiline\nstring" ); @@ -1067,7 +1062,7 @@ void TestQgsLayoutTable::autoWrap() frame2->attemptSetSceneRect( QRectF( 5, 40, 100, 90 ) ); table->setMaximumNumberOfFeatures( 20 ); - table->setVectorLayer( multiLineLayer ); + table->setVectorLayer( multiLineLayer.get() ); table->setWrapBehavior( QgsLayoutTable::WrapText ); table->columns().at( 0 )->setWidth( 25 ); diff --git a/tests/src/core/testqgslayoututils.cpp b/tests/src/core/testqgslayoututils.cpp index fa1d1c23207..fd0e81e29d7 100644 --- a/tests/src/core/testqgslayoututils.cpp +++ b/tests/src/core/testqgslayoututils.cpp @@ -51,6 +51,7 @@ class TestQgsLayoutUtils: public QObject void drawTextPos(); //test drawing text at a pos void drawTextRect(); //test drawing text in a rect void largestRotatedRect(); //test largest rotated rect helper function + void decodePaperOrientation(); private: @@ -244,11 +245,9 @@ void TestQgsLayoutUtils::createRenderContextFromLayout() // add a reference map QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); -#if 0 // TODO - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - composition->addComposerMap( map ); -#endif + map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setExtent( extent ); + l.addLayoutItem( map ); l.setReferenceMap( map ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, &p ); @@ -263,19 +262,19 @@ void TestQgsLayoutUtils::createRenderContextFromLayout() QVERIFY( !rc.painter() ); // check render context flags are correctly set - l.context().setFlags( 0 ); + l.renderContext().setFlags( nullptr ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( !( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( !( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( rc.flags() & QgsRenderContext::ForceVectorOutput ) ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( ( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( !( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( rc.flags() & QgsRenderContext::ForceVectorOutput ) ); - l.context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( ( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( ( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); @@ -306,16 +305,12 @@ void TestQgsLayoutUtils::createRenderContextFromMap() QgsProject project; QgsLayout l( &project ); -#if 0 // TODO // add a map QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 30, 60, 200, 100 ) ); + map->setExtent( extent ); + l.addLayoutItem( map ); - map->setNewExtent( extent ); - map->setSceneRect( QRectF( 30, 60, 200, 100 ) ); - l.addComposerMap( map ); -#endif - -#if 0 //TODO rc = QgsLayoutUtils::createRenderContextForMap( map, &p ); QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); QGSCOMPARENEAR( rc.rendererScale(), map->scale(), 1000000 ); @@ -329,10 +324,9 @@ void TestQgsLayoutUtils::createRenderContextFromMap() // secondary map QgsLayoutItemMap *map2 = new QgsLayoutItemMap( &l ); - - map2->setNewExtent( extent ); - map2->setSceneRect( QRectF( 30, 60, 100, 50 ) ); - composition->addComposerMap( map2 ); + map2->attemptSetSceneRect( QRectF( 30, 60, 100, 50 ) ); + map2->setExtent( extent ); + l.addLayoutItem( map2 ); rc = QgsLayoutUtils::createRenderContextForMap( map2, &p ); QGSCOMPARENEAR( rc.scaleFactor(), 150 / 25.4, 0.001 ); @@ -340,24 +334,24 @@ void TestQgsLayoutUtils::createRenderContextFromMap() QVERIFY( rc.painter() ); // check render context flags are correctly set - l.context().setFlags( 0 ); + l.renderContext().setFlags( 0 ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( !( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( !( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( rc.flags() & QgsRenderContext::ForceVectorOutput ) ); - l.context().setFlag( QgsLayoutContext::FlagAntialiasing ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagAntialiasing ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( ( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( !( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( rc.flags() & QgsRenderContext::ForceVectorOutput ) ); - l.context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects ); + l.renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects ); rc = QgsLayoutUtils::createRenderContextForLayout( &l, nullptr ); QVERIFY( ( rc.flags() & QgsRenderContext::Antialiasing ) ); QVERIFY( ( rc.flags() & QgsRenderContext::UseAdvancedEffects ) ); QVERIFY( ( rc.flags() & QgsRenderContext::ForceVectorOutput ) ); -#endif + p.end(); } @@ -490,7 +484,7 @@ void TestQgsLayoutUtils::textHeightMM() void TestQgsLayoutUtils::drawTextPos() { //test drawing with no painter - QgsLayoutUtils::drawText( 0, QPointF( 5, 15 ), QStringLiteral( "Abc123" ), mTestFont ); + QgsLayoutUtils::drawText( nullptr, QPointF( 5, 15 ), QStringLiteral( "Abc123" ), mTestFont ); //test drawing text on to image mTestFont.setPointSize( 48 ); @@ -515,7 +509,7 @@ void TestQgsLayoutUtils::drawTextPos() void TestQgsLayoutUtils::drawTextRect() { //test drawing with no painter - QgsLayoutUtils::drawText( 0, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont ); + QgsLayoutUtils::drawText( nullptr, QRectF( 5, 15, 200, 50 ), QStringLiteral( "Abc123" ), mTestFont ); //test drawing text on to image mTestFont.setPointSize( 48 ); @@ -616,6 +610,23 @@ void TestQgsLayoutUtils::largestRotatedRect() } } +void TestQgsLayoutUtils::decodePaperOrientation() +{ + QgsLayoutItemPage::Orientation orientation; + bool ok = false; + orientation = QgsLayoutUtils::decodePaperOrientation( QStringLiteral( "bad string" ), ok ); + QVERIFY( !ok ); + QCOMPARE( orientation, QgsLayoutItemPage::Landscape ); //should default to landscape + ok = false; + orientation = QgsLayoutUtils::decodePaperOrientation( QStringLiteral( "portrait" ), ok ); + QVERIFY( ok ); + QCOMPARE( orientation, QgsLayoutItemPage::Portrait ); + ok = false; + orientation = QgsLayoutUtils::decodePaperOrientation( QStringLiteral( " LANDSCAPE " ), ok ); + QVERIFY( ok ); + QCOMPARE( orientation, QgsLayoutItemPage::Landscape ); +} + bool TestQgsLayoutUtils::renderCheck( const QString &testName, QImage &image, int mismatchCount ) { mReport += "

" + testName + "

\n"; diff --git a/tests/src/core/testqgslegendrenderer.cpp b/tests/src/core/testqgslegendrenderer.cpp index 7849e36c6d6..68e55c742b7 100644 --- a/tests/src/core/testqgslegendrenderer.cpp +++ b/tests/src/core/testqgslegendrenderer.cpp @@ -125,9 +125,9 @@ class TestQgsLegendRenderer : public QObject private: QgsLayerTree *mRoot = nullptr; - QgsVectorLayer *mVL1 = 0 ; // line - QgsVectorLayer *mVL2 = 0 ; // polygon - QgsVectorLayer *mVL3 = 0 ; // point + QgsVectorLayer *mVL1 = nullptr ; // line + QgsVectorLayer *mVL2 = nullptr ; // polygon + QgsVectorLayer *mVL3 = nullptr ; // point QgsRasterLayer *mRL = nullptr; QString mReport; bool _testLegendColumns( int itemCount, int columnCount, const QString &testName ); @@ -230,7 +230,7 @@ void TestQgsLegendRenderer::init() void TestQgsLegendRenderer::cleanup() { delete mRoot; - mRoot = 0; + mRoot = nullptr; QgsProject::instance()->removeAllMapLayers(); } @@ -632,7 +632,7 @@ void TestQgsLegendRenderer::testFilterByExpression() QVERIFY( _verifyImage( testName, mReport ) ); // test again with setLegendFilter and only expressions - legendModel.setLegendFilterByMap( 0 ); + legendModel.setLegendFilterByMap( nullptr ); legendModel.setLegendFilter( &mapSettings, /*useExtent*/ false ); QString testName2 = testName + "2"; diff --git a/tests/src/core/testqgsmaprendererjob.cpp b/tests/src/core/testqgsmaprendererjob.cpp index 8158738b007..74db54b3d3b 100644 --- a/tests/src/core/testqgsmaprendererjob.cpp +++ b/tests/src/core/testqgsmaprendererjob.cpp @@ -55,7 +55,7 @@ class TestQgsMapRendererJob : public QObject public: TestQgsMapRendererJob() = default; - ~TestQgsMapRendererJob() + ~TestQgsMapRendererJob() override { delete mMapSettings; } diff --git a/tests/src/core/testqgsmaprotation.cpp b/tests/src/core/testqgsmaprotation.cpp index cc4c68a806f..83dd2212af9 100644 --- a/tests/src/core/testqgsmaprotation.cpp +++ b/tests/src/core/testqgsmaprotation.cpp @@ -47,7 +47,7 @@ class TestQgsMapRotation : public QObject mTestDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; } - ~TestQgsMapRotation(); + ~TestQgsMapRotation() override; private slots: void initTestCase();// will be called before the first testfunction is executed. @@ -207,6 +207,7 @@ void TestQgsMapRotation::linesLayer() format.setSize( 16 ); palSettings.setFormat( format ); mLinesLayer->setLabeling( new QgsVectorLayerSimpleLabeling( palSettings ) ); + mLinesLayer->setLabelsEnabled( true ); QVERIFY( success ); mMapSettings->setExtent( mLinesLayer->extent() ); //QgsRectangle(-150,-150,150,150) ); diff --git a/tests/src/core/testqgsmarkerlinesymbol.cpp b/tests/src/core/testqgsmarkerlinesymbol.cpp index 0e3c9d9552f..f92738bc2d5 100644 --- a/tests/src/core/testqgsmarkerlinesymbol.cpp +++ b/tests/src/core/testqgsmarkerlinesymbol.cpp @@ -49,7 +49,7 @@ class TestQgsMarkerLineSymbol : public QObject mTestDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; } - ~TestQgsMarkerLineSymbol(); + ~TestQgsMarkerLineSymbol() override; private slots: void initTestCase();// will be called before the first testfunction is executed. diff --git a/tests/src/core/testqgspagesizeregistry.cpp b/tests/src/core/testqgspagesizeregistry.cpp index a3742f68696..5e2a38cc81f 100644 --- a/tests/src/core/testqgspagesizeregistry.cpp +++ b/tests/src/core/testqgspagesizeregistry.cpp @@ -145,6 +145,20 @@ void TestQgsPageSizeRegistry::decodePageSize() QCOMPARE( result.name, QString( "A3" ) ); QCOMPARE( result.size, QgsLayoutSize( 297.0, 420.0 ) ); + //good strings + QVERIFY( registry->decodePageSize( QStringLiteral( "a4" ), result ) ); + QCOMPARE( result.size.width(), 210.0 ); + QCOMPARE( result.size.height(), 297.0 ); + QVERIFY( registry->decodePageSize( QStringLiteral( "B0" ), result ) ); + QCOMPARE( result.size.width(), 1000.0 ); + QCOMPARE( result.size.height(), 1414.0 ); + QVERIFY( registry->decodePageSize( QStringLiteral( "letter" ), result ) ); + QCOMPARE( result.size.width(), 215.9 ); + QCOMPARE( result.size.height(), 279.4 ); + QVERIFY( registry->decodePageSize( QStringLiteral( "LEGAL" ), result ) ); + QCOMPARE( result.size.width(), 215.9 ); + QCOMPARE( result.size.height(), 355.6 ); + //test with bad string QgsPageSize result2( QStringLiteral( "nomatch" ), QgsLayoutSize( 10.0, 20.0 ) ); QgsPageSize expected( result2 ); //for a bad match, expect page size to be unchanged diff --git a/tests/src/core/testqgspainteffect.cpp b/tests/src/core/testqgspainteffect.cpp index 108d51aabe5..430fe3464f3 100644 --- a/tests/src/core/testqgspainteffect.cpp +++ b/tests/src/core/testqgspainteffect.cpp @@ -39,8 +39,10 @@ #include "qgsfillsymbollayer.h" #include "qgslinesymbollayer.h" #include "qgsmarkersymbollayer.h" -#include "qgscomposition.h" -#include "qgscomposermap.h" +#include "qgslayout.h" +#include "qgslayoutitempage.h" +#include "qgslayoutitemmap.h" +#include "qgslayoutpagecollection.h" //qgis test includes #include "qgsmultirenderchecker.h" @@ -53,17 +55,17 @@ class DummyPaintEffect : public QgsPaintEffect : mProp1( prop1 ) , mProp2( prop2 ) {} - virtual QString type() const override { return QStringLiteral( "Dummy" ); } - virtual QgsPaintEffect *clone() const override { return new DummyPaintEffect( mProp1, mProp2 ); } + QString type() const override { return QStringLiteral( "Dummy" ); } + QgsPaintEffect *clone() const override { return new DummyPaintEffect( mProp1, mProp2 ); } static QgsPaintEffect *create( const QgsStringMap &props ) { return new DummyPaintEffect( props[QStringLiteral( "testProp" )], props[QStringLiteral( "testProp2" )] ); } - virtual QgsStringMap properties() const override + QgsStringMap properties() const override { QgsStringMap props; props[QStringLiteral( "testProp" )] = mProp1; props[QStringLiteral( "testProp2" )] = mProp2; return props; } - virtual void readProperties( const QgsStringMap &props ) override + void readProperties( const QgsStringMap &props ) override { mProp1 = props[QStringLiteral( "testProp" )]; mProp2 = props[QStringLiteral( "testProp2" )]; @@ -74,7 +76,7 @@ class DummyPaintEffect : public QgsPaintEffect protected: - virtual void draw( QgsRenderContext &context ) override { Q_UNUSED( context ); } + void draw( QgsRenderContext &context ) override { Q_UNUSED( context ); } private: QString mProp1; @@ -117,7 +119,7 @@ class TestQgsPaintEffect: public QObject void layerEffectMarker(); void vectorLayerEffect(); void mapUnits(); - void composer(); + void layout(); private: bool imageCheck( const QString &testName, QImage &image, int mismatchCount = 0 ); @@ -138,7 +140,7 @@ void TestQgsPaintEffect::initTestCase() QgsApplication::initQgis(); mReport += QLatin1String( "

Paint Effect Tests

\n" ); - mPicture = 0; + mPicture = nullptr; QgsPaintEffectRegistry *registry = QgsApplication::paintEffectRegistry(); registry->addEffectType( new QgsPaintEffectMetadata( QStringLiteral( "Dummy" ), QStringLiteral( "Dummy effect" ), DummyPaintEffect::create ) ); @@ -944,9 +946,9 @@ void TestQgsPaintEffect::mapUnits() delete lineLayer; } -void TestQgsPaintEffect::composer() +void TestQgsPaintEffect::layout() { - //test rendering an effect inside a composer (tests DPI scaling of effects) + //test rendering an effect inside a layout (tests DPI scaling of effects) QString linesFileName = mTestDataDir + "lines.shp"; QFileInfo lineFileInfo( linesFileName ); @@ -970,25 +972,28 @@ void TestQgsPaintEffect::composer() lineLayer->setRenderer( renderer ); - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - composition->setPaperSize( 50, 50 ); - QgsComposerMap *composerMap = new QgsComposerMap( composition, 1, 1, 48, 48 ); - composerMap->setFrameEnabled( true ); - composition->addComposerMap( composerMap ); - composerMap->setNewExtent( lineLayer->extent() ); - composerMap->setLayers( QList() << lineLayer ); + QgsLayout l( QgsProject::instance() ); + std::unique_ptr< QgsLayoutItemPage > page = qgis::make_unique< QgsLayoutItemPage >( &l ); + page->setPageSize( QgsLayoutSize( 50, 50 ) ); + l.pageCollection()->addPage( page.release() ); + + QgsLayoutItemMap *map = new QgsLayoutItemMap( &l ); + map->attemptSetSceneRect( QRectF( 1, 1, 48, 48 ) ); + map->setFrameEnabled( true ); + l.addLayoutItem( map ); + map->setExtent( lineLayer->extent() ); + map->setLayers( QList() << lineLayer ); QImage outputImage( 591, 591, QImage::Format_RGB32 ); - composition->setPlotStyle( QgsComposition::Print ); outputImage.setDotsPerMeterX( 300 / 25.4 * 1000 ); outputImage.setDotsPerMeterY( 300 / 25.4 * 1000 ); QgsMultiRenderChecker::drawBackground( &outputImage ); QPainter p( &outputImage ); - composition->renderPage( &p, 0 ); + QgsLayoutExporter exporter( &l ); + exporter.renderPage( &p, 0 ); p.end(); bool result = imageCheck( QStringLiteral( "painteffect_composer" ), outputImage ); - delete composition; QVERIFY( result ); delete lineLayer; } diff --git a/tests/src/core/testqgspainteffectregistry.cpp b/tests/src/core/testqgspainteffectregistry.cpp index 2a96379dde6..a962dccd55b 100644 --- a/tests/src/core/testqgspainteffectregistry.cpp +++ b/tests/src/core/testqgspainteffectregistry.cpp @@ -28,13 +28,13 @@ class DummyPaintEffect : public QgsPaintEffect { public: DummyPaintEffect() = default; - virtual QString type() const override { return QStringLiteral( "Dummy" ); } - virtual QgsPaintEffect *clone() const override { return new DummyPaintEffect(); } + QString type() const override { return QStringLiteral( "Dummy" ); } + QgsPaintEffect *clone() const override { return new DummyPaintEffect(); } static QgsPaintEffect *create( const QgsStringMap & ) { return new DummyPaintEffect(); } - virtual QgsStringMap properties() const override { return QgsStringMap(); } - virtual void readProperties( const QgsStringMap &props ) override { Q_UNUSED( props ); } + QgsStringMap properties() const override { return QgsStringMap(); } + void readProperties( const QgsStringMap &props ) override { Q_UNUSED( props ); } protected: - virtual void draw( QgsRenderContext &context ) override { Q_UNUSED( context ); } + void draw( QgsRenderContext &context ) override { Q_UNUSED( context ); } }; class TestQgsPaintEffectRegistry : public QObject diff --git a/tests/src/core/testqgspointlocator.cpp b/tests/src/core/testqgspointlocator.cpp index 60d1243b4bb..db074fb0ddd 100644 --- a/tests/src/core/testqgspointlocator.cpp +++ b/tests/src/core/testqgspointlocator.cpp @@ -30,7 +30,7 @@ struct FilterExcludePoint : public QgsPointLocator::MatchFilter { explicit FilterExcludePoint( const QgsPointXY &p ) : mPoint( p ) {} - bool acceptMatch( const QgsPointLocator::Match &match ) { return match.point() != mPoint; } + bool acceptMatch( const QgsPointLocator::Match &match ) override { return match.point() != mPoint; } QgsPointXY mPoint; }; @@ -42,7 +42,7 @@ struct FilterExcludeEdge : public QgsPointLocator::MatchFilter , mP2( p2 ) {} - bool acceptMatch( const QgsPointLocator::Match &match ) + bool acceptMatch( const QgsPointLocator::Match &match ) override { QgsPointXY p1, p2; match.edgePoints( p1, p2 ); @@ -248,13 +248,13 @@ class TestQgsPointLocator : public QObject void testExtent() { QgsRectangle bbox1( 10, 10, 11, 11 ); // out of layer's bounds - QgsPointLocator loc1( mVL, QgsCoordinateReferenceSystem(), &bbox1 ); + QgsPointLocator loc1( mVL, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), &bbox1 ); QgsPointLocator::Match m1 = loc1.nearestVertex( QgsPointXY( 2, 2 ), 999 ); QVERIFY( !m1.isValid() ); QgsRectangle bbox2( 0, 0, 1, 1 ); // in layer's bounds - QgsPointLocator loc2( mVL, QgsCoordinateReferenceSystem(), &bbox2 ); + QgsPointLocator loc2( mVL, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), &bbox2 ); QgsPointLocator::Match m2 = loc2.nearestVertex( QgsPointXY( 2, 2 ), 999 ); QVERIFY( m2.isValid() ); @@ -270,7 +270,7 @@ class TestQgsPointLocator : public QObject flist << ff; vlNullGeom->dataProvider()->addFeatures( flist ); - QgsPointLocator loc( vlNullGeom, QgsCoordinateReferenceSystem(), nullptr ); + QgsPointLocator loc( vlNullGeom, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr ); QgsPointLocator::Match m1 = loc.nearestVertex( QgsPointXY( 2, 2 ), std::numeric_limits::max() ); QVERIFY( !m1.isValid() ); @@ -292,7 +292,7 @@ class TestQgsPointLocator : public QObject flist << ff; vlEmptyGeom->dataProvider()->addFeatures( flist ); - QgsPointLocator loc( vlEmptyGeom, QgsCoordinateReferenceSystem(), nullptr ); + QgsPointLocator loc( vlEmptyGeom, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr ); QgsPointLocator::Match m1 = loc.nearestVertex( QgsPointXY( 2, 2 ), std::numeric_limits::max() ); QVERIFY( !m1.isValid() ); diff --git a/tests/src/core/testqgsproject.cpp b/tests/src/core/testqgsproject.cpp index be2d0d4f07a..220532ce152 100644 --- a/tests/src/core/testqgsproject.cpp +++ b/tests/src/core/testqgsproject.cpp @@ -108,6 +108,19 @@ void TestQgsProject::testPathResolver() QCOMPARE( resolverRel.readPath( "../file1.txt" ), QString( "/home/file1.txt" ) ); QCOMPARE( resolverRel.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) ); + // test older style relative path - file must exist for this to work + QTemporaryFile tmpFile; + tmpFile.open(); // fileName is not available until we open the file + QString tmpName = tmpFile.fileName(); + tmpFile.close(); + QgsPathResolver tempRel( tmpName ); + QFileInfo fi( tmpName ); + QFile testFile( fi.path() + QStringLiteral( "/file1.txt" ) ); + testFile.open( QIODevice::WriteOnly | QIODevice::Text ); + testFile.close(); + QVERIFY( QFile::exists( fi.path() + QStringLiteral( "/file1.txt" ) ) ); + QCOMPARE( tempRel.readPath( "file1.txt" ), fi.path() + QStringLiteral( "/file1.txt" ) ); + QgsPathResolver resolverAbs; QCOMPARE( resolverAbs.writePath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) ); QCOMPARE( resolverAbs.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) ); diff --git a/tests/src/core/testqgsproperty.cpp b/tests/src/core/testqgsproperty.cpp index d9ca4cae125..73e8adee993 100644 --- a/tests/src/core/testqgsproperty.cpp +++ b/tests/src/core/testqgsproperty.cpp @@ -42,16 +42,16 @@ class TestTransformer : public QgsPropertyTransformer } - virtual Type transformerType() const override { return SizeScaleTransformer; } - virtual TestTransformer *clone() const override + Type transformerType() const override { return SizeScaleTransformer; } + TestTransformer *clone() const override { return new TestTransformer( mMinValue, mMaxValue ); } - virtual QString toExpression( const QString & ) const override { return QString(); } + QString toExpression( const QString & ) const override { return QString(); } private: - virtual QVariant transform( const QgsExpressionContext &context, const QVariant &value ) const override + QVariant transform( const QgsExpressionContext &context, const QVariant &value ) const override { Q_UNUSED( context ); diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp index be17fe511dd..c9ffdf5ac8b 100644 --- a/tests/src/core/testqgsrasterlayer.cpp +++ b/tests/src/core/testqgsrasterlayer.cpp @@ -55,7 +55,7 @@ class TestQgsRasterLayer : public QObject Q_OBJECT public: TestQgsRasterLayer() = default; - ~TestQgsRasterLayer() + ~TestQgsRasterLayer() override { delete mMapSettings; } @@ -649,7 +649,7 @@ void TestQgsRasterLayer::transparency() rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList ); QgsRasterRenderer *rasterRenderer = mpFloat32RasterLayer->renderer(); - QVERIFY( rasterRenderer != 0 ); + QVERIFY( rasterRenderer != nullptr ); rasterRenderer->setRasterTransparency( rasterTransparency ); mMapSettings->setLayers( QList() << mpFloat32RasterLayer ); diff --git a/tests/src/core/testqgsrastersublayer.cpp b/tests/src/core/testqgsrastersublayer.cpp index 77ae5d6be16..c3ce794277f 100644 --- a/tests/src/core/testqgsrastersublayer.cpp +++ b/tests/src/core/testqgsrastersublayer.cpp @@ -84,7 +84,7 @@ void TestQgsRasterSubLayer::initTestCase() GDALAllRegister(); QString format = QStringLiteral( "netCDF" ); GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() ); - mHasNetCDF = myGdalDriver != 0; + mHasNetCDF = myGdalDriver != nullptr; mFileName = mTestDataDir + "landsat2.nc"; diff --git a/tests/src/core/testqgsrenderers.cpp b/tests/src/core/testqgsrenderers.cpp index a37f446c187..7e095133e41 100644 --- a/tests/src/core/testqgsrenderers.cpp +++ b/tests/src/core/testqgsrenderers.cpp @@ -45,7 +45,7 @@ class TestQgsRenderers : public QObject public: TestQgsRenderers() = default; - ~TestQgsRenderers() + ~TestQgsRenderers() override { delete mMapSettings; } diff --git a/tests/src/core/testqgsrulebasedrenderer.cpp b/tests/src/core/testqgsrulebasedrenderer.cpp index e510a81b80c..fe727496373 100644 --- a/tests/src/core/testqgsrulebasedrenderer.cpp +++ b/tests/src/core/testqgsrulebasedrenderer.cpp @@ -120,10 +120,10 @@ class TestQgsRuleBasedRenderer: public QObject void test_clone_ruleKey() { - RRule *rootRule = new RRule( 0 ); - RRule *sub1Rule = new RRule( 0, 0, 0, QStringLiteral( "fld > 1" ) ); - RRule *sub2Rule = new RRule( 0, 0, 0, QStringLiteral( "fld > 2" ) ); - RRule *sub3Rule = new RRule( 0, 0, 0, QStringLiteral( "fld > 3" ) ); + RRule *rootRule = new RRule( nullptr ); + RRule *sub1Rule = new RRule( nullptr, 0, 0, QStringLiteral( "fld > 1" ) ); + RRule *sub2Rule = new RRule( nullptr, 0, 0, QStringLiteral( "fld > 2" ) ); + RRule *sub3Rule = new RRule( nullptr, 0, 0, QStringLiteral( "fld > 3" ) ); rootRule->appendChild( sub1Rule ); sub1Rule->appendChild( sub2Rule ); sub2Rule->appendChild( sub3Rule ); diff --git a/tests/src/core/testqgssnappingutils.cpp b/tests/src/core/testqgssnappingutils.cpp index 0582a7a26d4..f676e41251b 100644 --- a/tests/src/core/testqgssnappingutils.cpp +++ b/tests/src/core/testqgssnappingutils.cpp @@ -30,7 +30,7 @@ struct FilterExcludePoint : public QgsPointLocator::MatchFilter { explicit FilterExcludePoint( const QgsPointXY &p ) : mPoint( p ) {} - bool acceptMatch( const QgsPointLocator::Match &match ) { return match.point() != mPoint; } + bool acceptMatch( const QgsPointLocator::Match &match ) override { return match.point() != mPoint; } QgsPointXY mPoint; }; diff --git a/tests/src/core/testqgsstyle.cpp b/tests/src/core/testqgsstyle.cpp index 826ecfe7614..87493ac942b 100644 --- a/tests/src/core/testqgsstyle.cpp +++ b/tests/src/core/testqgsstyle.cpp @@ -208,7 +208,7 @@ void TestStyle::testLoadColorRamps() QgsDebugMsg( "colorRamp " + name ); QVERIFY( colorRamps.contains( name ) ); QgsColorRamp *ramp = mStyle->colorRamp( name ); - QVERIFY( ramp != 0 ); + QVERIFY( ramp != nullptr ); // test colors if ( colorTests.contains( name ) ) { @@ -236,7 +236,7 @@ void TestStyle::testSaveLoad() QgsDebugMsg( "colorRamp " + name ); QVERIFY( colorRamps.contains( name ) ); QgsColorRamp *ramp = mStyle->colorRamp( name ); - QVERIFY( ramp != 0 ); + QVERIFY( ramp != nullptr ); if ( ramp ) delete ramp; } diff --git a/tests/src/core/testqgstaskmanager.cpp b/tests/src/core/testqgstaskmanager.cpp index f7762541999..d9b987c23b2 100644 --- a/tests/src/core/testqgstaskmanager.cpp +++ b/tests/src/core/testqgstaskmanager.cpp @@ -80,10 +80,10 @@ class TestTerminationTask : public TestTask public: - ~TestTerminationTask() + ~TestTerminationTask() override { //make sure task has been terminated by manager prior to deletion - Q_ASSERT( status() == QgsTask::Terminated ); + QVERIFY( status() == QgsTask::Terminated ); } protected: @@ -102,7 +102,7 @@ class CancelableTask : public QgsTask public: - ~CancelableTask() + ~CancelableTask() override { int i = 1; i++; @@ -167,7 +167,7 @@ class FinishTask : public QgsTask void finished( bool result ) override { - Q_ASSERT( QApplication::instance()->thread() == QThread::currentThread() ); + QVERIFY( QApplication::instance()->thread() == QThread::currentThread() ); *resultObtained = result; } }; diff --git a/tests/src/core/testqgstracer.cpp b/tests/src/core/testqgstracer.cpp index a2446aad010..fb457f59286 100644 --- a/tests/src/core/testqgstracer.cpp +++ b/tests/src/core/testqgstracer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "qgsproject.h" class TestQgsTracer : public QObject { @@ -306,13 +307,14 @@ void TestQgsTracer::testReprojection() QgsVectorLayer *vl = make_layer( wkts ); QgsCoordinateReferenceSystem dstCrs( QStringLiteral( "EPSG:3857" ) ); - QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), dstCrs ); + QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), dstCrs, QgsProject::instance() ); QgsPointXY p1 = ct.transform( QgsPointXY( 1, 0 ) ); QgsPointXY p2 = ct.transform( QgsPointXY( 2, 0 ) ); QgsTracer tracer; tracer.setLayers( QList() << vl ); - tracer.setDestinationCrs( dstCrs ); + QgsCoordinateTransformContext context; + tracer.setDestinationCrs( dstCrs, context ); tracer.init(); QgsPolylineXY points1 = tracer.findShortestPath( p1, p2 ); @@ -337,7 +339,7 @@ void TestQgsTracer::testCurved() QgsPolylineXY points1 = tracer.findShortestPath( QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) ); - QVERIFY( points1.count() != 0 ); + QVERIFY( !points1.isEmpty() ); QgsGeometry tmpG1 = QgsGeometry::fromPolylineXY( points1 ); double l = tmpG1.length(); diff --git a/tests/src/core/testqgsvectorlayer.cpp b/tests/src/core/testqgsvectorlayer.cpp index cececff8500..3b25dd01ea1 100644 --- a/tests/src/core/testqgsvectorlayer.cpp +++ b/tests/src/core/testqgsvectorlayer.cpp @@ -41,7 +41,7 @@ class TestSignalReceiver : public QObject public: TestSignalReceiver() - : QObject( 0 ) + : QObject( nullptr ) , featureBlendMode( QPainter::CompositionMode( 0 ) ) {} bool rendererChanged = false ; diff --git a/tests/src/core/testqgsvectorlayercache.cpp b/tests/src/core/testqgsvectorlayercache.cpp index b6e137933ed..745709603bb 100644 --- a/tests/src/core/testqgsvectorlayercache.cpp +++ b/tests/src/core/testqgsvectorlayercache.cpp @@ -31,7 +31,7 @@ * @ingroup UnitTests * This is a unit test for the vector layer cache * - * @see QgsVectorLayerCache + * \see QgsVectorLayerCache */ class TestVectorLayerCache : public QObject { diff --git a/tests/src/core/testqgsvectorlayerjoinbuffer.cpp b/tests/src/core/testqgsvectorlayerjoinbuffer.cpp index f056cdcc2ac..24fc8a46104 100644 --- a/tests/src/core/testqgsvectorlayerjoinbuffer.cpp +++ b/tests/src/core/testqgsvectorlayerjoinbuffer.cpp @@ -33,7 +33,7 @@ * @ingroup UnitTests * This is a unit test for the vector layer join buffer * - * @see QgsVectorLayerJoinBuffer + * \see QgsVectorLayerJoinBuffer */ class TestVectorLayerJoinBuffer : public QObject { diff --git a/tests/src/geometry_checker/testqgsgeometrychecks.cpp b/tests/src/geometry_checker/testqgsgeometrychecks.cpp index 89281b8108d..1ac4ea974f4 100644 --- a/tests/src/geometry_checker/testqgsgeometrychecks.cpp +++ b/tests/src/geometry_checker/testqgsgeometrychecks.cpp @@ -14,7 +14,6 @@ ***************************************************************************/ #include "qgstest.h" -#include "qgscrscache.h" #include "qgsfeature.h" #include "qgsfeaturepool.h" #include "qgsvectorlayer.h" @@ -39,6 +38,7 @@ #include "qgsgeometryselfcontactcheck.h" #include "qgsgeometryselfintersectioncheck.h" #include "qgsgeometrysliverpolygoncheck.h" +#include "qgsproject.h" #include "qgsgeometrytypecheck.h" @@ -55,9 +55,9 @@ class TestQgsGeometryChecks: public QObject QgsGeometryCheck::ChangeType type; QgsVertexId vidx; }; - double layerToMapUnits( const QgsMapLayer *layer, const QString &mapCrs ) const; - QgsFeaturePool *createFeaturePool( QgsVectorLayer *layer, const QString &mapCrs, bool selectedOnly = false ) const; - QgsGeometryCheckerContext *createTestContext( QTemporaryDir &tempDir, QMap &layers, const QString &mapCrs = "EPSG:4326", double prec = 8 ) const; + double layerToMapUnits( const QgsMapLayer *layer, const QgsCoordinateReferenceSystem &mapCrs ) const; + QgsFeaturePool *createFeaturePool( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &mapCrs, bool selectedOnly = false ) const; + QgsGeometryCheckerContext *createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs = QgsCoordinateReferenceSystem( "EPSG:4326" ), double prec = 8 ) const; void cleanupTestContext( QgsGeometryCheckerContext *ctx ) const; void listErrors( const QList &checkErrors, const QStringList &messages ) const; QList searchCheckErrors( const QList &checkErrors, const QString &layerId, const QgsFeatureId &featureId = -1, const QgsPointXY &pos = QgsPointXY(), const QgsVertexId &vid = QgsVertexId(), const QVariant &value = QVariant(), double tol = 1E-4 ) const; @@ -958,9 +958,9 @@ void TestQgsGeometryChecks::testSliverPolygonCheck() /////////////////////////////////////////////////////////////////////////////// -double TestQgsGeometryChecks::layerToMapUnits( const QgsMapLayer *layer, const QString &mapCrs ) const +double TestQgsGeometryChecks::layerToMapUnits( const QgsMapLayer *layer, const QgsCoordinateReferenceSystem &mapCrs ) const { - QgsCoordinateTransform crst = QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mapCrs ); + QgsCoordinateTransform crst = QgsCoordinateTransform( layer->crs(), mapCrs, QgsProject::instance() ); QgsRectangle extent = layer->extent(); QgsPointXY l1( extent.xMinimum(), extent.yMinimum() ); QgsPointXY l2( extent.xMaximum(), extent.yMaximum() ); @@ -971,14 +971,14 @@ double TestQgsGeometryChecks::layerToMapUnits( const QgsMapLayer *layer, const Q return distMapUnits / distLayerUnits; } -QgsFeaturePool *TestQgsGeometryChecks::createFeaturePool( QgsVectorLayer *layer, const QString &mapCrs, bool selectedOnly ) const +QgsFeaturePool *TestQgsGeometryChecks::createFeaturePool( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &mapCrs, bool selectedOnly ) const { double layerToMapUntis = layerToMapUnits( layer, mapCrs ); - QgsCoordinateTransform layerToMapTransform = QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mapCrs ); + QgsCoordinateTransform layerToMapTransform = QgsCoordinateTransform( layer->crs(), mapCrs, QgsProject::instance() ); return new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ); } -QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap &layers, const QString &mapCrs, double prec ) const +QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap &layers, const QgsCoordinateReferenceSystem &mapCrs, double prec ) const { QDir testDataDir( QDir( TEST_DATA_DIR ).absoluteFilePath( "geometry_checker" ) ); QDir tmpDir( tempDir.path() ); diff --git a/tests/src/gui/CMakeLists.txt b/tests/src/gui/CMakeLists.txt index df67d953f11..2c1a4bd21f6 100644 --- a/tests/src/gui/CMakeLists.txt +++ b/tests/src/gui/CMakeLists.txt @@ -16,7 +16,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/core/expression ${CMAKE_SOURCE_DIR}/src/core/auth - ${CMAKE_SOURCE_DIR}/src/core/composer ${CMAKE_SOURCE_DIR}/src/core/geometry ${CMAKE_SOURCE_DIR}/src/core/layout ${CMAKE_SOURCE_DIR}/src/core/metadata @@ -133,8 +132,8 @@ ADD_QGIS_TEST(editorwidgetregistrytest testqgseditorwidgetregistry.cpp) ADD_QGIS_TEST(keyvaluewidgettest testqgskeyvaluewidget.cpp) ADD_QGIS_TEST(listwidgettest testqgslistwidget.cpp) ADD_QGIS_TEST(filedownloader testqgsfiledownloader.cpp) -ADD_QGIS_TEST(composergui testqgscomposergui.cpp) ADD_QGIS_TEST(layoutgui testqgslayoutgui.cpp) ADD_QGIS_TEST(layoutview testqgslayoutview.cpp) +ADD_QGIS_TEST(valuemapwidgetwrapper testqgsvaluemapwidgetwrapper.cpp) ADD_QGIS_TEST(valuerelationwidgetwrapper testqgsvaluerelationwidgetwrapper.cpp) ADD_QGIS_TEST(relationreferencewidget testqgsrelationreferencewidget.cpp) diff --git a/tests/src/gui/testqgsattributeform.cpp b/tests/src/gui/testqgsattributeform.cpp index 7bd9ff27414..d4629cb0762 100644 --- a/tests/src/gui/testqgsattributeform.cpp +++ b/tests/src/gui/testqgsattributeform.cpp @@ -188,10 +188,10 @@ void TestQgsAttributeForm::testFieldMultiConstraints() ww3 = qobject_cast( form.mWidgets[3] ); // no constraint so we expect an empty label - Q_ASSERT( constraintsLabel( &form, ww0 )->text().isEmpty() ); - Q_ASSERT( constraintsLabel( &form, ww1 )->text().isEmpty() ); - Q_ASSERT( constraintsLabel( &form, ww2 )->text().isEmpty() ); - Q_ASSERT( constraintsLabel( &form, ww3 )->text().isEmpty() ); + QVERIFY( constraintsLabel( &form, ww0 )->text().isEmpty() ); + QVERIFY( constraintsLabel( &form, ww1 )->text().isEmpty() ); + QVERIFY( constraintsLabel( &form, ww2 )->text().isEmpty() ); + QVERIFY( constraintsLabel( &form, ww3 )->text().isEmpty() ); // update constraint layer->setConstraintExpression( 0, QStringLiteral( "col0 < (col1 * col2)" ) ); diff --git a/tests/src/gui/testqgscomposergui.cpp b/tests/src/gui/testqgscomposergui.cpp deleted file mode 100644 index 1ffde2016a0..00000000000 --- a/tests/src/gui/testqgscomposergui.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/*************************************************************************** - testqgscomposergui.cpp - ---------------------- - Date : May 2017 - Copyright : (C) 2017 Nyall Dawson - Email : nyall dot dawson at gmail dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - - - -#include - -#include "qgsmapsettings.h" -#include "qgscomposition.h" -#include "qgscomposerlabel.h" -#include "qgscomposermap.h" -#include "qgscomposermodel.h" -#include "qgscomposeritemcombobox.h" - -#include -#include - -class TestQgsComposerGui: public QObject -{ - Q_OBJECT - private slots: - void initTestCase(); // will be called before the first testfunction is executed. - void cleanupTestCase(); // will be called after the last testfunction was executed. - void init(); // will be called before each testfunction is executed. - void cleanup(); // will be called after every testfunction. - - void testProxyCrash(); - - private: - -}; - -void TestQgsComposerGui::initTestCase() -{ -} - -void TestQgsComposerGui::cleanupTestCase() -{ -} - -void TestQgsComposerGui::init() -{ -} - -void TestQgsComposerGui::cleanup() -{ -} - -void TestQgsComposerGui::testProxyCrash() -{ - // test for a possible crash when using QgsComposerProxyModel and reordering items - QgsComposition *composition = new QgsComposition( QgsProject::instance() ); - - // create a composer item combobox - QgsComposerItemComboBox *cb = new QgsComposerItemComboBox( nullptr, composition ); - QgsComposerItemComboBox *cb2 = new QgsComposerItemComboBox( nullptr, composition ); - QgsComposerItemComboBox *cb3 = new QgsComposerItemComboBox( nullptr, composition ); - QgsComposerItemComboBox *cb4 = new QgsComposerItemComboBox( nullptr, composition ); - - // add some items to composition - QgsComposerMap *item1 = new QgsComposerMap( composition ); - composition->addItem( item1 ); - QgsComposerLabel *item2 = new QgsComposerLabel( composition ); - composition->addItem( item2 ); - QgsComposerMap *item3 = new QgsComposerMap( composition ); - composition->addItem( item3 ); - - QCOMPARE( cb->count(), 3 ); - cb->setItemType( QgsComposerItem::ComposerMap ); - QCOMPARE( cb->count(), 2 ); - - cb4->setItemType( QgsComposerItem::ComposerLabel ); - - cb->setItem( item1 ); - QCOMPARE( cb->currentItem(), item1 ); - cb2->setItem( item3 ); - QCOMPARE( cb2->currentItem(), item3 ); - cb3->setItem( item2 ); - QCOMPARE( cb3->currentItem(), item2 ); - - // reorder items - expect no crash! - // we do this by calling the private members, in order to simulate what - // happens when a drag and drop reorder occurs - composition->itemsModel()->mItemZList.removeOne( item1 ); - composition->itemsModel()->mItemZList.insert( 1, item1 ); - composition->itemsModel()->rebuildSceneItemList(); - - QCOMPARE( cb->currentItem(), item1 ); - QCOMPARE( cb2->currentItem(), item3 ); - - composition->itemsModel()->mItemZList.removeOne( item1 ); - composition->itemsModel()->mItemZList.insert( 0, item1 ); - composition->itemsModel()->rebuildSceneItemList(); - - delete composition; -} - - -QTEST_MAIN( TestQgsComposerGui ) -#include "testqgscomposergui.moc" diff --git a/tests/src/gui/testqgsfeaturelistcombobox.cpp b/tests/src/gui/testqgsfeaturelistcombobox.cpp index a0b9e2f46fc..2080b17d9a1 100644 --- a/tests/src/gui/testqgsfeaturelistcombobox.cpp +++ b/tests/src/gui/testqgsfeaturelistcombobox.cpp @@ -99,7 +99,7 @@ void TestQgsFeatureListComboBox::testSetGetLayer() { std::unique_ptr cb( new QgsFeatureListComboBox() ); - Q_ASSERT( cb->sourceLayer() == nullptr ); + QVERIFY( cb->sourceLayer() == nullptr ); cb->setSourceLayer( mLayer.get() ); QCOMPARE( cb->sourceLayer(), mLayer.get() ); } @@ -109,17 +109,17 @@ void TestQgsFeatureListComboBox::testSetGetForeignKey() QgsFeatureListComboBox *cb = new QgsFeatureListComboBox(); // std::unique_ptr cb( new QgsFeatureListComboBox() ); - Q_ASSERT( cb->identifierValue().isNull() ); + QVERIFY( cb->identifierValue().isNull() ); cb->setSourceLayer( mLayer.get() ); cb->setDisplayExpression( "\"material\"" ); cb->lineEdit()->setText( "ro" ); emit cb->lineEdit()->textChanged( "ro" ); - Q_ASSERT( cb->identifierValue().isNull() ); + QVERIFY( cb->identifierValue().isNull() ); waitForLoaded( cb ); - Q_ASSERT( cb->identifierValue().isNull() ); + QVERIFY( cb->identifierValue().isNull() ); cb->setIdentifierValue( 20 ); QCOMPARE( cb->identifierValue(), QVariant( 20 ) ); @@ -127,7 +127,7 @@ void TestQgsFeatureListComboBox::testSetGetForeignKey() void TestQgsFeatureListComboBox::testAllowNull() { - Q_ASSERT( false ); + QVERIFY( false ); // Note to self: implement this! } diff --git a/tests/src/gui/testqgsfieldexpressionwidget.cpp b/tests/src/gui/testqgsfieldexpressionwidget.cpp index 4612ae82ee4..5d29f53c0fc 100644 --- a/tests/src/gui/testqgsfieldexpressionwidget.cpp +++ b/tests/src/gui/testqgsfieldexpressionwidget.cpp @@ -28,7 +28,7 @@ * @ingroup UnitTests * This is a unit test for the field expression widget * - * @see QgsFieldExpressionWidget + * \see QgsFieldExpressionWidget */ class TestQgsFieldExpressionWidget : public QObject { diff --git a/tests/src/gui/testqgsfiledownloader.cpp b/tests/src/gui/testqgsfiledownloader.cpp index 0511daaba5a..777560ea39c 100644 --- a/tests/src/gui/testqgsfiledownloader.cpp +++ b/tests/src/gui/testqgsfiledownloader.cpp @@ -133,7 +133,7 @@ void TestQgsFileDownloader::init() mCompleted = false; mExited = false; mTempFile = new QTemporaryFile(); - Q_ASSERT( mTempFile->open() ); + QVERIFY( mTempFile->open() ); mTempFile->close(); } diff --git a/tests/src/gui/testqgslayoutgui.cpp b/tests/src/gui/testqgslayoutgui.cpp index b8e7bbbcaaa..532bc96ae54 100644 --- a/tests/src/gui/testqgslayoutgui.cpp +++ b/tests/src/gui/testqgslayoutgui.cpp @@ -67,7 +67,7 @@ void TestQgsLayoutGui::itemTypeComboBox() // test QgsLayoutItemComboBox QgsLayout *layout = new QgsLayout( QgsProject::instance() ); - // create a composer item combobox + // create a layout item combobox QgsLayoutItemComboBox *cb = new QgsLayoutItemComboBox( nullptr, layout ); QgsLayoutItemComboBox *cb2 = new QgsLayoutItemComboBox( nullptr, layout ); cb2->setItemType( QgsLayoutItemRegistry::LayoutShape ); @@ -76,7 +76,7 @@ void TestQgsLayoutGui::itemTypeComboBox() QSignalSpy spy1( cb, &QgsLayoutItemComboBox::itemChanged ); QSignalSpy spy2( cb2, &QgsLayoutItemComboBox::itemChanged ); - // add some items to composition + // add some items to layout QgsLayoutItemMap *item1 = new QgsLayoutItemMap( layout ); item1->setId( "item 1" ); layout->addLayoutItem( item1 ); @@ -183,13 +183,13 @@ void TestQgsLayoutGui::testProxyCrash() // test for a possible crash when using QgsLayoutProxyModel and reordering items QgsLayout *layout = new QgsLayout( QgsProject::instance() ); - // create a composer item combobox + // create a layout item combobox QgsLayoutItemComboBox *cb = new QgsLayoutItemComboBox( nullptr, layout ); QgsLayoutItemComboBox *cb2 = new QgsLayoutItemComboBox( nullptr, layout ); QgsLayoutItemComboBox *cb3 = new QgsLayoutItemComboBox( nullptr, layout ); QgsLayoutItemComboBox *cb4 = new QgsLayoutItemComboBox( nullptr, layout ); - // add some items to composition + // add some items to layout QgsLayoutItemMap *item1 = new QgsLayoutItemMap( layout ); layout->addLayoutItem( item1 ); QCOMPARE( cb->count(), 1 ); diff --git a/tests/src/gui/testqgslayoutview.cpp b/tests/src/gui/testqgslayoutview.cpp index c2526fa88a2..046b7d34c09 100644 --- a/tests/src/gui/testqgslayoutview.cpp +++ b/tests/src/gui/testqgslayoutview.cpp @@ -242,13 +242,11 @@ class TestItem : public QgsLayoutItem public: TestItem( QgsLayout *layout ) : QgsLayoutItem( layout ) {} - ~TestItem() = default; int mFlag = 0; //implement pure virtual methods int type() const override { return QgsLayoutItemRegistry::LayoutItem + 101; } - QString stringType() const override { return QStringLiteral( "testitem" ); } void draw( QgsRenderContext &, const QStyleOptionGraphicsItem * = nullptr ) override { } }; @@ -268,8 +266,8 @@ void TestQgsLayoutView::guiRegistry() QVERIFY( registry.itemMetadataIds().isEmpty() ); QVERIFY( !registry.createItemWidget( nullptr ) ); QVERIFY( !registry.createItemWidget( nullptr ) ); - TestItem *testItem = new TestItem( nullptr ); - QVERIFY( !registry.createItemWidget( testItem ) ); // not in registry + std::unique_ptr< TestItem > testItem = qgis::make_unique< TestItem >( nullptr ); + QVERIFY( !registry.createItemWidget( testItem.get() ) ); // not in registry QSignalSpy spyTypeAdded( ®istry, &QgsLayoutItemGuiRegistry::typeAdded ); @@ -301,7 +299,7 @@ void TestQgsLayoutView::guiRegistry() QVERIFY( registry.itemMetadata( uuid ) ); QCOMPARE( registry.itemMetadata( uuid )->visibleName(), QStringLiteral( "mytype" ) ); - QWidget *widget = registry.createItemWidget( testItem ); + QWidget *widget = registry.createItemWidget( testItem.get() ); QVERIFY( widget ); delete widget; @@ -322,7 +320,7 @@ void TestQgsLayoutView::guiRegistry() //creating item QgsLayoutItem *item = registry.createItem( uuid, nullptr ); QVERIFY( !item ); - QgsApplication::layoutItemRegistry()->addLayoutItemType( new QgsLayoutItemMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "my type" ), QIcon(), []( QgsLayout * layout )->QgsLayoutItem* + QgsApplication::layoutItemRegistry()->addLayoutItemType( new QgsLayoutItemMetadata( QgsLayoutItemRegistry::LayoutItem + 101, QStringLiteral( "my type" ), []( QgsLayout * layout )->QgsLayoutItem* { return new TestItem( layout ); } ) ); diff --git a/tests/src/gui/testqgsscalerangewidget.cpp b/tests/src/gui/testqgsscalerangewidget.cpp index ef082c168b4..31e70f0e945 100644 --- a/tests/src/gui/testqgsscalerangewidget.cpp +++ b/tests/src/gui/testqgsscalerangewidget.cpp @@ -32,7 +32,7 @@ * @ingroup UnitTests * This is a unit test for the scale range widget * - * @see QgsScaleRangeWidget + * \see QgsScaleRangeWidget */ class TestQgsScaleRangeWidget : public QObject { diff --git a/tests/src/gui/testqgsvaluemapwidgetwrapper.cpp b/tests/src/gui/testqgsvaluemapwidgetwrapper.cpp new file mode 100644 index 00000000000..5bcedb31157 --- /dev/null +++ b/tests/src/gui/testqgsvaluemapwidgetwrapper.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + testqgsvaluemapwidgetwrapper.cpp + -------------------------------------- + Date : January 2018 + Copyright : (C) 2018 Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#include "qgstest.h" + +#include "editorwidgets/core/qgseditorwidgetregistry.h" +#include "qgsapplication.h" +#include "qgseditorwidgetwrapper.h" +#include "editorwidgets/qgsvaluemapwidgetwrapper.h" +#include "qgsvaluemapfieldformatter.h" +#include "editorwidgets/qgsvaluemapconfigdlg.h" +#include "qgsgui.h" + +class TestQgsValueMapWidgetWrapper : public QObject +{ + Q_OBJECT + public: + TestQgsValueMapWidgetWrapper() = default; + + private slots: + void initTestCase(); // will be called before the first testfunction is executed. + void cleanupTestCase(); // will be called after the last testfunction was executed. + void init(); // will be called before each testfunction is executed. + void cleanup(); // will be called after every testfunction. + void testPopulateComboBox(); + +}; + +void TestQgsValueMapWidgetWrapper::initTestCase() +{ + QgsApplication::init(); + QgsApplication::initQgis(); + QgsGui::editorWidgetRegistry()->initEditors(); +} + +void TestQgsValueMapWidgetWrapper::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +void TestQgsValueMapWidgetWrapper::init() +{ +} + +void TestQgsValueMapWidgetWrapper::cleanup() +{ +} + +void TestQgsValueMapWidgetWrapper::testPopulateComboBox() +{ + // new style config + QVariantMap config; + QList valueList; + QVariantMap nullValue; + nullValue.insert( QgsApplication::nullRepresentation(), QgsValueMapFieldFormatter::NULL_VALUE ); + valueList.append( nullValue ); + QVariantMap value1; + value1.insert( QStringLiteral( "desc 1" ), QStringLiteral( "val 1" ) ); + valueList.append( value1 ); + QVariantMap value2; + value2.insert( QStringLiteral( "desc 2" ), QStringLiteral( "val 2" ) ); + valueList.append( value2 ); + + config.insert( QStringLiteral( "map" ), valueList ); + + + std::unique_ptr< QComboBox > combo = qgis::make_unique< QComboBox >(); + + // with nulls + QgsValueMapConfigDlg::populateComboBox( combo.get(), config, false ); + + QCOMPARE( combo->count(), 3 ); + QCOMPARE( combo->itemText( 0 ), QgsApplication::nullRepresentation() ); + QCOMPARE( combo->itemData( 0 ).toString(), QgsValueMapFieldFormatter::NULL_VALUE ); + QCOMPARE( combo->itemText( 1 ), QStringLiteral( "desc 1" ) ); + QCOMPARE( combo->itemData( 1 ).toString(), QStringLiteral( "val 1" ) ); + QCOMPARE( combo->itemText( 2 ), QStringLiteral( "desc 2" ) ); + QCOMPARE( combo->itemData( 2 ).toString(), QStringLiteral( "val 2" ) ); + + // no nulls + combo->clear(); + QgsValueMapConfigDlg::populateComboBox( combo.get(), config, true ); + + QCOMPARE( combo->count(), 2 ); + QCOMPARE( combo->itemText( 0 ), QStringLiteral( "desc 1" ) ); + QCOMPARE( combo->itemData( 0 ).toString(), QStringLiteral( "val 1" ) ); + QCOMPARE( combo->itemText( 1 ), QStringLiteral( "desc 2" ) ); + QCOMPARE( combo->itemData( 1 ).toString(), QStringLiteral( "val 2" ) ); + + // old style config map (2.x) + config.clear(); + QVariantMap mapValue; + mapValue.insert( QgsApplication::nullRepresentation(), QgsValueMapFieldFormatter::NULL_VALUE ); + mapValue.insert( QStringLiteral( "desc 1" ), QStringLiteral( "val 1" ) ); + mapValue.insert( QStringLiteral( "desc 2" ), QStringLiteral( "val 2" ) ); + config.insert( QStringLiteral( "map" ), mapValue ); + + // with nulls + combo->clear(); + QgsValueMapConfigDlg::populateComboBox( combo.get(), config, false ); + + QCOMPARE( combo->count(), 3 ); + QCOMPARE( combo->itemText( 0 ), QgsApplication::nullRepresentation() ); + QCOMPARE( combo->itemData( 0 ).toString(), QgsValueMapFieldFormatter::NULL_VALUE ); + QCOMPARE( combo->itemText( 1 ), QStringLiteral( "desc 1" ) ); + QCOMPARE( combo->itemData( 1 ).toString(), QStringLiteral( "val 1" ) ); + QCOMPARE( combo->itemText( 2 ), QStringLiteral( "desc 2" ) ); + QCOMPARE( combo->itemData( 2 ).toString(), QStringLiteral( "val 2" ) ); + + // no nulls + combo->clear(); + QgsValueMapConfigDlg::populateComboBox( combo.get(), config, true ); + + QCOMPARE( combo->count(), 2 ); + QCOMPARE( combo->itemText( 0 ), QStringLiteral( "desc 1" ) ); + QCOMPARE( combo->itemData( 0 ).toString(), QStringLiteral( "val 1" ) ); + QCOMPARE( combo->itemText( 1 ), QStringLiteral( "desc 2" ) ); + QCOMPARE( combo->itemData( 1 ).toString(), QStringLiteral( "val 2" ) ); + +} + +QGSTEST_MAIN( TestQgsValueMapWidgetWrapper ) +#include "testqgsvaluemapwidgetwrapper.moc" diff --git a/tests/src/gui/testqgsvaluerelationwidgetwrapper.cpp b/tests/src/gui/testqgsvaluerelationwidgetwrapper.cpp index 5362fe64c4c..640652a05d3 100644 --- a/tests/src/gui/testqgsvaluerelationwidgetwrapper.cpp +++ b/tests/src/gui/testqgsvaluerelationwidgetwrapper.cpp @@ -67,11 +67,13 @@ void TestQgsValueRelationWidgetWrapper::testScrollBarUnlocked() QgsProject::instance()->addMapLayer( &vl1, false, false ); // build a value relation widget wrapper - QListWidget lw; - QWidget editor; - QgsValueRelationWidgetWrapper w( &vl1, 0, &editor, nullptr ); + QgsValueRelationWidgetWrapper w( &vl1, 0, nullptr, nullptr ); + + QVariantMap config; + config.insert( QStringLiteral( "AllowMulti" ), true ); + w.setConfig( config ); + w.widget(); w.setEnabled( true ); - w.initWidget( &lw ); // add an item virtually QListWidgetItem item; diff --git a/tests/src/gui/testrenderergui.cpp b/tests/src/gui/testrenderergui.cpp index db634054dcc..b9ccbb0d994 100644 --- a/tests/src/gui/testrenderergui.cpp +++ b/tests/src/gui/testrenderergui.cpp @@ -55,8 +55,8 @@ void TestRendererGUI::loadLayers() void TestRendererGUI::setRenderer() { QgsMapLayer *layer = mMapCanvas->layer( 0 ); - Q_ASSERT( layer ); - Q_ASSERT( layer->type() == QgsMapLayer::VectorLayer ); + QVERIFY( layer ); + QVERIFY( layer->type() == QgsMapLayer::VectorLayer ); QgsVectorLayer *vlayer = static_cast( layer ); QgsRendererPropertiesDialog dlg( vlayer, QgsStyle::defaultStyle() ); diff --git a/tests/src/gui/testrenderergui.h b/tests/src/gui/testrenderergui.h index c0db0bf7baa..d26c938eb48 100644 --- a/tests/src/gui/testrenderergui.h +++ b/tests/src/gui/testrenderergui.h @@ -23,7 +23,7 @@ class TestRendererGUI : public QMainWindow { Q_OBJECT public: - explicit TestRendererGUI( QWidget *parent = 0 ); + explicit TestRendererGUI( QWidget *parent = nullptr ); void loadLayers(); signals: diff --git a/tests/src/providers/CMakeLists.txt b/tests/src/providers/CMakeLists.txt index ef414abca1d..6953f97e158 100644 --- a/tests/src/providers/CMakeLists.txt +++ b/tests/src/providers/CMakeLists.txt @@ -104,9 +104,6 @@ IF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/raster ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/providers/wcs ) - INCLUDE_DIRECTORIES(SYSTEM - ${QT_QTSCRIPT_INCLUDE_DIR} - ) TARGET_LINK_LIBRARIES(qgis_wcstest ${QT_QTCORE_LIBRARY} @@ -114,7 +111,6 @@ IF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) ${QT_QTSVG_LIBRARY} ${QT_QTXML_LIBRARY} ${OPTIONAL_QTWEBKIT} - ${QT_QTSCRIPT_LIBRARY} qgis_core ) diff --git a/tests/src/providers/grass/testqgsgrassprovider.cpp b/tests/src/providers/grass/testqgsgrassprovider.cpp index 9bd5cfcbfea..a6403ae4bfb 100644 --- a/tests/src/providers/grass/testqgsgrassprovider.cpp +++ b/tests/src/providers/grass/testqgsgrassprovider.cpp @@ -1209,7 +1209,7 @@ void TestQgsGrassProvider::edit() grassLayer->startEditing(); grassProvider->startEditing( grassLayer ); - Q_ASSERT( expectedLayer ); + QVERIFY( expectedLayer ); expectedLayer->startEditing(); } diff --git a/tests/src/providers/testqgswcspublicservers.cpp b/tests/src/providers/testqgswcspublicservers.cpp index dab442fd4bc..d9c602c4aca 100644 --- a/tests/src/providers/testqgswcspublicservers.cpp +++ b/tests/src/providers/testqgswcspublicservers.cpp @@ -21,9 +21,6 @@ #include #include #include -#include -#include -#include #include "qgsapplication.h" #include "qgsdatasourceuri.h" @@ -121,56 +118,52 @@ void TestQgsWcsPublicServers::init() QString data = file.readAll(); //QgsDebugMsg("servers: \n" + str ); file.close(); - QScriptEngine engine; - QScriptValue result = engine.evaluate( data ); + QJsonDocument doc = QJsonDocument::fromJson( data.toUtf8() ); + const QJsonObject result = doc.object(); - QScriptValueIterator serverIt( result ); - while ( serverIt.hasNext() ) + QJsonObject::ConstIterator serverIt = result.constBegin(); + for ( ; serverIt != result.constEnd(); serverIt++ ) { - serverIt.next(); - QScriptValue serverValue = serverIt.value(); + const QJsonObject serverObject = serverIt.value().toObject(); + const QString serverUrl = serverObject.value( QLatin1String( "url" ) ).toString(); - QString serverUrl = serverValue.property( QStringLiteral( "url" ) ).toString(); QgsDebugMsg( "serverUrl: " + serverUrl ); Server server( serverUrl ); - server.description = serverValue.property( QStringLiteral( "description" ) ).toString(); + server.description = serverObject.value( QLatin1String( "description" ) ).toString(); - QScriptValueIterator paramsIt( serverValue.property( QStringLiteral( "params" ) ) ); - while ( paramsIt.hasNext() ) + const QJsonObject serverParams = serverObject.value( QLatin1String( "params" ) ).toObject(); + QJsonObject::ConstIterator paramsIt = serverParams.constBegin(); + for ( ; paramsIt != serverParams.constEnd(); paramsIt++ ) { - paramsIt.next(); QgsDebugMsg( QString( "params value: %1" ).arg( paramsIt.value().toString() ) ); - server.params.insert( paramsIt.name(), paramsIt.value().toString() ); + server.params.insert( paramsIt.key(), paramsIt.value().toString() ); } - QScriptValue issuesValue = serverValue.property( QStringLiteral( "issues" ) ); + QJsonObject issuesObject = serverObject.value( QLatin1String( "issues" ) ).toObject(); - QScriptValueIterator issuesIt( issuesValue ); - while ( issuesIt.hasNext() ) + QJsonObject::ConstIterator issuesIt = issuesObject.constBegin(); + for ( ; issuesIt != issuesObject.constEnd(); ++issuesIt ) { - issuesIt.next(); - QScriptValue issueValue = issuesIt.value(); + QJsonObject issueObject = issuesIt.value().toObject(); - QString description = issueValue.property( QStringLiteral( "description" ) ).toString(); + QString description = issueObject.value( QLatin1String( "description" ) ).toString(); QgsDebugMsg( "description: " + description ); Issue issue( description ); - issue.offender = issueValue.property( QStringLiteral( "offender" ) ).toString(); + issue.offender = issueObject.value( QLatin1String( "offender" ) ).toString(); - QScriptValue coveragesValue = issueValue.property( QStringLiteral( "coverages" ) ); - QScriptValueIterator coveragesIt( coveragesValue ); - while ( coveragesIt.hasNext() ) + QJsonObject coveragesObject = issueObject.value( QLatin1String( "coverages" ) ).toObject(); + QJsonObject::ConstIterator coverageIt = coveragesObject.constBegin(); + for ( ; coverageIt != coveragesObject.constEnd(); ++coverageIt ) { - coveragesIt.next(); - issue.coverages << coveragesIt.value().toString(); + issue.coverages << coverageIt.value().toString(); } - QScriptValue versionsValue = issueValue.property( QStringLiteral( "versions" ) ); - QScriptValueIterator versionsIt( versionsValue ); - while ( versionsIt.hasNext() ) + QJsonObject versionsObject = issueObject.value( QLatin1String( "versions" ) ).toObject(); + QJsonObject::ConstIterator versionsIt = versionsObject.constBegin(); + for ( ; versionsIt != versionsObject.constEnd(); ++versionsIt ) { - versionsIt.next(); issue.versions << versionsIt.value().toString(); } diff --git a/tests/src/providers/testqgswcspublicservers.h b/tests/src/providers/testqgswcspublicservers.h index ca9bf8eaedd..bc3e74aba23 100644 --- a/tests/src/providers/testqgswcspublicservers.h +++ b/tests/src/providers/testqgswcspublicservers.h @@ -61,7 +61,7 @@ class TestQgsWcsPublicServers: public QObject TestQgsWcsPublicServers( const QString &cacheDirPath, int maxCoverages, const QString &server = QString(), const QString &coverage = QString(), const QString &version = QString(), bool force = false ); - ~TestQgsWcsPublicServers(); + ~TestQgsWcsPublicServers() override; void init(); void test(); diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 4bbd6e6464e..8ab4626d5f4 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -12,7 +12,6 @@ ADD_PYTHON_TEST(PyQgsActionManager test_qgsactionmanager.py) ADD_PYTHON_TEST(PyQgsAggregateCalculator test_qgsaggregatecalculator.py) ADD_PYTHON_TEST(PyQgsAnnotation test_qgsannotation.py) ADD_PYTHON_TEST(PyQgsApplication test_qgsapplication.py) -ADD_PYTHON_TEST(PyQgsAtlasComposition test_qgsatlascomposition.py) ADD_PYTHON_TEST(PyQgsAttributeFormEditorWidget test_qgsattributeformeditorwidget.py) ADD_PYTHON_TEST(PyQgsAttributeTableConfig test_qgsattributetableconfig.py) ADD_PYTHON_TEST(PyQgsAttributeTableModel test_qgsattributetablemodel.py) @@ -26,26 +25,16 @@ ADD_PYTHON_TEST(PyQgsColorButton test_qgscolorbutton.py) ADD_PYTHON_TEST(PyQgsColorScheme test_qgscolorscheme.py) ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py) ADD_PYTHON_TEST(PyQgsCoordinateFormatter test_qgscoordinateformatter.py) -ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py) -ADD_PYTHON_TEST(PyQgsComposerHtml test_qgscomposerhtml.py) -ADD_PYTHON_TEST(PyQgsComposerItem test_qgscomposeritem.py) -ADD_PYTHON_TEST(PyQgsComposerLabel test_qgscomposerlabel.py) -ADD_PYTHON_TEST(PyQgsComposerLegend test_qgscomposerlegend.py) -ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py) -ADD_PYTHON_TEST(PyQgsComposerMapGrid test_qgscomposermapgrid.py) -ADD_PYTHON_TEST(PyQgsComposerPicture test_qgscomposerpicture.py) -ADD_PYTHON_TEST(PyQgsComposerShapes test_qgscomposershapes.py) -ADD_PYTHON_TEST(PyQgsComposerPolygon test_qgscomposerpolygon.py) -ADD_PYTHON_TEST(PyQgsComposerPolyline test_qgscomposerpolyline.py) -ADD_PYTHON_TEST(PyQgsComposerView test_qgscomposerview.py) -ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py) ADD_PYTHON_TEST(PyQgsConditionalStyle test_qgsconditionalstyle.py) +ADD_PYTHON_TEST(PyQgsCoordinateTransformContext test_qgscoordinatetransformcontext.py) ADD_PYTHON_TEST(PyQgsDefaultValue test_qgsdefaultvalue.py) ADD_PYTHON_TEST(PyQgsXmlUtils test_qgsxmlutils.py) ADD_PYTHON_TEST(PyQgsCoordinateTransform test_qgscoordinatetransform.py) +ADD_PYTHON_TEST(PyQgsDateTimeEdit test_qgsdatetimeedit.py) ADD_PYTHON_TEST(PyQgsDateTimeStatisticalSummary test_qgsdatetimestatisticalsummary.py) ADD_PYTHON_TEST(PyQgsDelimitedTextProvider test_qgsdelimitedtextprovider.py) ADD_PYTHON_TEST(PyQgsDistanceArea test_qgsdistancearea.py) +ADD_PYTHON_TEST(PyQgsEditFormConfig test_qgseditformconfig.py) ADD_PYTHON_TEST(PyQgsEditWidgets test_qgseditwidgets.py) ADD_PYTHON_TEST(PyQgsEllipsoidUtils test_qgsellipsoidutils.py) ADD_PYTHON_TEST(PyQgsEncodingSelectionDialog test_qgsencodingselectiondialog.py) @@ -64,6 +53,7 @@ ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py) ADD_PYTHON_TEST(PyQgsFeedback test_qgsfeedback.py) ADD_PYTHON_TEST(PyQgsFields test_qgsfields.py) ADD_PYTHON_TEST(PyQgsFieldModel test_qgsfieldmodel.py) +ADD_PYTHON_TEST(PyQgsFileUtils test_qgsfileutils.py) ADD_PYTHON_TEST(PyQgsFilterLineEdit test_qgsfilterlineedit.py) ADD_PYTHON_TEST(PyQgsFloatingWidget test_qgsfloatingwidget.py) ADD_PYTHON_TEST(PyQgsFontButton test_qgsfontbutton.py) @@ -82,6 +72,9 @@ ADD_PYTHON_TEST(PyQgsLayerTreeMapCanvasBridge test_qgslayertreemapcanvasbridge.p ADD_PYTHON_TEST(PyQgsLayerTree test_qgslayertree.py) ADD_PYTHON_TEST(PyQgsLayout test_qgslayout.py) ADD_PYTHON_TEST(PyQgsLayoutAlign test_qgslayoutaligner.py) +ADD_PYTHON_TEST(PyQgsLayoutAtlas test_qgslayoutatlas.py) +ADD_PYTHON_TEST(PyQgsLayoutExporter test_qgslayoutexporter.py) +ADD_PYTHON_TEST(PyQgsLayoutFrame test_qgslayoutframe.py) ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py) ADD_PYTHON_TEST(PyQgsLayoutPageCollection test_qgslayoutpagecollection.py) ADD_PYTHON_TEST(PyQgsLayoutView test_qgslayoutview.py) @@ -94,9 +87,12 @@ ADD_PYTHON_TEST(PyQgsLayoutLabel test_qgslayoutlabel.py) ADD_PYTHON_TEST(PyQgsLayoutLegend test_qgslayoutlegend.py) ADD_PYTHON_TEST(PyQgsLayoutMap test_qgslayoutmap.py) ADD_PYTHON_TEST(PyQgsLayoutMapGrid test_qgslayoutmapgrid.py) +ADD_PYTHON_TEST(PyQgsLayoutPage test_qgslayoutpage.py) ADD_PYTHON_TEST(PyQgsLayoutPicture test_qgslayoutpicture.py) ADD_PYTHON_TEST(PyQgsLayoutPolygon test_qgslayoutpolygon.py) ADD_PYTHON_TEST(PyQgsLayoutPolyline test_qgslayoutpolyline.py) +ADD_PYTHON_TEST(PyQgsLayoutScaleBar test_qgslayoutscalebar.py) +ADD_PYTHON_TEST(PyQgsLayoutShape test_qgslayoutshape.py) ADD_PYTHON_TEST(PyQgsLayoutSnapper test_qgslayoutsnapper.py) ADD_PYTHON_TEST(PyQgsLayoutUnitsComboBox test_qgslayoutunitscombobox.py) ADD_PYTHON_TEST(PyQgsLineSymbolLayers test_qgslinesymbollayers.py) @@ -124,7 +120,7 @@ ADD_PYTHON_TEST(PyQgsOptional test_qgsoptional.py) ADD_PYTHON_TEST(PyQgsOwsConnection test_qgsowsconnection.py) ADD_PYTHON_TEST(PyQgsPalLabelingBase test_qgspallabeling_base.py) ADD_PYTHON_TEST(PyQgsPalLabelingCanvas test_qgspallabeling_canvas.py) -ADD_PYTHON_TEST(PyQgsPalLabelingComposer test_qgspallabeling_composer.py) +ADD_PYTHON_TEST(PyQgsPalLabelingLayout test_qgspallabeling_layout.py) ADD_PYTHON_TEST(PyQgsPalLabelingPlacement test_qgspallabeling_placement.py) ADD_PYTHON_TEST(PyQgsPanelWidget test_qgspanelwidget.py) ADD_PYTHON_TEST(PyQgsPanelWidgetStack test_qgspanelwidgetstack.py) @@ -146,6 +142,7 @@ ADD_PYTHON_TEST(PyQgsRelation test_qgsrelation.py) ADD_PYTHON_TEST(PyQgsRelationManager test_qgsrelationmanager.py) ADD_PYTHON_TEST(PyQgsRenderContext test_qgsrendercontext.py) ADD_PYTHON_TEST(PyQgsRenderer test_qgsrenderer.py) +ADD_PYTHON_TEST(PyQgsReport test_qgsreport.py) ADD_PYTHON_TEST(PyQgsRulebasedRenderer test_qgsrulebasedrenderer.py) ADD_PYTHON_TEST(PyQgsSingleSymbolRenderer test_qgssinglesymbolrenderer.py) ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py) diff --git a/tests/src/python/featuresourcetestbase.py b/tests/src/python/featuresourcetestbase.py index a801f4013a7..86c6b168058 100644 --- a/tests/src/python/featuresourcetestbase.py +++ b/tests/src/python/featuresourcetestbase.py @@ -20,6 +20,7 @@ from qgis.core import ( QgsFeatureRequest, QgsFeature, QgsWkbTypes, + QgsProject, QgsGeometry, QgsAbstractFeatureIterator, QgsExpressionContextScope, @@ -485,6 +486,14 @@ class FeatureSourceTestCase(object): assert set(features) == set([1, 2, 3, 4, 5]), 'Got {} instead'.format(features) self.assertTrue(all_valid) + # ExactIntersection flag set, but no filter rect set. Should be ignored. + request = QgsFeatureRequest() + request.setFlags(QgsFeatureRequest.ExactIntersect) + features = [f['pk'] for f in self.source.getFeatures(request)] + all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) + assert set(features) == set([1, 2, 3, 4, 5]), 'Got {} instead'.format(features) + self.assertTrue(all_valid) + def testRectAndExpression(self): extent = QgsRectangle(-70, 67, -60, 80) request = QgsFeatureRequest().setFilterExpression('"cnt">200').setFilterRect(extent) @@ -509,7 +518,7 @@ class FeatureSourceTestCase(object): self.assertEqual(request.acceptFeature(f), f['pk'] in expected) def testGetFeaturesDestinationCrs(self): - request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785')) + request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785'), QgsProject.instance().transformContext()) features = {f['pk']: f for f in self.source.getFeatures(request)} # test that features have been reprojected self.assertAlmostEqual(features[1].geometry().constGet().x(), -7829322, -5) @@ -524,7 +533,7 @@ class FeatureSourceTestCase(object): # when destination crs is set, filter rect should be in destination crs rect = QgsRectangle(-7650000, 10500000, -7200000, 15000000) - request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785')).setFilterRect(rect) + request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785'), QgsProject.instance().transformContext()).setFilterRect(rect) features = {f['pk']: f for f in self.source.getFeatures(request)} self.assertEqual(set(features.keys()), {2, 4}) # test that features have been reprojected @@ -535,7 +544,7 @@ class FeatureSourceTestCase(object): # bad rect for transform rect = QgsRectangle(-99999999999, 99999999999, -99999999998, 99999999998) - request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:28356')).setFilterRect(rect) + request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:28356'), QgsProject.instance().transformContext()).setFilterRect(rect) features = [f for f in self.source.getFeatures(request)] self.assertFalse(features) diff --git a/tests/src/python/offlineditingtestbase.py b/tests/src/python/offlineditingtestbase.py index 0cd514a0755..7ad247909e9 100644 --- a/tests/src/python/offlineditingtestbase.py +++ b/tests/src/python/offlineditingtestbase.py @@ -67,7 +67,7 @@ class OfflineTestBase(object): layer = self._getLayer('test_point') assert layer.startEditing() for id, name, geom in TEST_FEATURES: - f = QgsFeature(layer.pendingFields()) + f = QgsFeature(layer.fields()) f['id'] = id f['name'] = name f.setGeometry(QgsGeometry.fromPointXY(geom)) @@ -271,7 +271,7 @@ class OfflineTestBase(object): self.assertTrue(offline_layer.startEditing()) features = [] for id, name, geom in TEST_FEATURES_INSERT: - f = QgsFeature(offline_layer.pendingFields()) + f = QgsFeature(offline_layer.fields()) f['id'] = id f['name'] = name f.setGeometry(QgsGeometry.fromPointXY(geom)) diff --git a/tests/src/python/providertestbase.py b/tests/src/python/providertestbase.py index f0c17fdf58a..91a43565bd0 100644 --- a/tests/src/python/providertestbase.py +++ b/tests/src/python/providertestbase.py @@ -26,6 +26,7 @@ from qgis.core import ( QgsVectorDataProvider, QgsVectorLayerFeatureSource, QgsFeatureSink, + QgsTestUtils, NULL ) @@ -192,6 +193,15 @@ class ProviderTestCase(FeatureSourceTestCase): """Individual providers may need to override this depending on their subset string formats""" return '"name"=\'AppleBearOrangePear\'' + def testGetFeaturesThreadSafety(self): + # no request + self.assertTrue(QgsTestUtils.testProviderIteratorThreadSafety(self.source)) + + # filter rect request + extent = QgsRectangle(-73, 70, -63, 80) + request = QgsFeatureRequest().setFilterRect(extent) + self.assertTrue(QgsTestUtils.testProviderIteratorThreadSafety(self.source, request)) + def testOrderBy(self): try: self.disableCompiler() diff --git a/tests/src/python/qgis_wrapped_server.py b/tests/src/python/qgis_wrapped_server.py index 94bd50c017a..dca3633a94a 100644 --- a/tests/src/python/qgis_wrapped_server.py +++ b/tests/src/python/qgis_wrapped_server.py @@ -10,6 +10,11 @@ A XYZ map service is also available for multithreading testing: ?MAP=/path/to/projects.qgs&SERVICE=XYZ&X=1&Y=0&Z=1&LAYERS=world +Note that multi threading in QGIS server is not officially supported and +it is not supposed to work in any case + +Set MULTITHREADING environment variable to 1 to activate. + For testing purposes, HTTP Basic can be enabled by setting the following environment variables: @@ -161,6 +166,8 @@ class Handler(BaseHTTPRequestHandler): headers = {} for k, v in self.headers.items(): headers['HTTP_%s' % k.replace(' ', '-').replace('-', '_').replace(' ', '-').upper()] = v + if not self.path.startswith('http'): + self.path = "%s://%s:%s%s" % ('https' if https else 'http', QGIS_SERVER_HOST, QGIS_SERVER_PORT, self.path) request = QgsBufferServerRequest(self.path, (QgsServerRequest.PostMethod if post_body is not None else QgsServerRequest.GetMethod), headers, post_body) response = QgsBufferServerResponse() qgs_server.handleRequest(request, response) @@ -188,7 +195,10 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): if __name__ == '__main__': - server = ThreadedHTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler) + if os.environ.get('MULTITHREADING') == '1': + server = ThreadedHTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler) + else: + server = HTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler) if https: server.socket = ssl.wrap_socket(server.socket, certfile=QGIS_SERVER_PKI_CERTIFICATE, diff --git a/tests/src/python/qgscompositionchecker.py b/tests/src/python/qgscompositionchecker.py deleted file mode 100644 index 1fe88ad06ee..00000000000 --- a/tests/src/python/qgscompositionchecker.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -''' -qgscompositionchecker.py - check rendering of Qgscomposition against an expected image - -------------------------------------- - Date : 31 Juli 2012 - Copyright : (C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler - email : horst.duester@sourcepole.ch - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -''' - -import qgis # NOQA - -from qgis.PyQt.QtCore import QSize, QDir, QFileInfo -from qgis.PyQt.QtGui import QImage, QPainter -from qgis.core import QgsMultiRenderChecker, QgsComposition - - -class QgsCompositionChecker(QgsMultiRenderChecker): - - def __init__(self, mTestName, mComposition): - super(QgsCompositionChecker, self).__init__() - self.mComposition = mComposition - self.mTestName = mTestName - self.mDotsPerMeter = 96 / 25.4 * 1000 - self.mSize = QSize(1122, 794) - self.setColorTolerance(5) - - def testComposition(self, page=0, pixelDiff=0): - if self.mComposition is None: - myMessage = "Composition not valid" - return False, myMessage - - # load expected image - self.setControlName("expected_" + self.mTestName) - - # get width/height, create image and render the composition to it - outputImage = QImage(self.mSize, QImage.Format_RGB32) - - self.mComposition.setPlotStyle(QgsComposition.Print) - outputImage.setDotsPerMeterX(self.mDotsPerMeter) - outputImage.setDotsPerMeterY(self.mDotsPerMeter) - QgsMultiRenderChecker.drawBackground(outputImage) - p = QPainter(outputImage) - self.mComposition.renderPage(p, page) - p.end() - - renderedFilePath = QDir.tempPath() + QDir.separator() + QFileInfo(self.mTestName).baseName() + "_rendered.png" - outputImage.save(renderedFilePath, "PNG") - - self.setRenderedImage(renderedFilePath) - - testResult = self.runTest(self.mTestName, pixelDiff) - - return testResult, self.report() diff --git a/tests/src/python/qgslayoutchecker.py b/tests/src/python/qgslayoutchecker.py index b34e8a73d8f..ab51c6d0a33 100644 --- a/tests/src/python/qgslayoutchecker.py +++ b/tests/src/python/qgslayoutchecker.py @@ -19,7 +19,7 @@ import qgis # NOQA from qgis.PyQt.QtCore import QSize, QDir, QFileInfo from qgis.PyQt.QtGui import QImage, QPainter -from qgis.core import QgsMultiRenderChecker, QgsLayout +from qgis.core import QgsMultiRenderChecker, QgsLayoutExporter class QgsLayoutChecker(QgsMultiRenderChecker): @@ -47,7 +47,8 @@ class QgsLayoutChecker(QgsMultiRenderChecker): outputImage.setDotsPerMeterY(self.dots_per_meter) QgsMultiRenderChecker.drawBackground(outputImage) p = QPainter(outputImage) - self.layout.exporter().renderPage(p, page) + exporter = QgsLayoutExporter(self.layout) + exporter.renderPage(p, page) p.end() renderedFilePath = QDir.tempPath() + QDir.separator() + QFileInfo(self.test_name).baseName() + "_rendered.png" diff --git a/tests/src/python/test_authmanager_password_ows.py b/tests/src/python/test_authmanager_password_ows.py index 36f11b38d6e..67279924171 100644 --- a/tests/src/python/test_authmanager_password_ows.py +++ b/tests/src/python/test_authmanager_password_ows.py @@ -276,7 +276,7 @@ class TestAuthManager(unittest.TestCase): # Check the we've got a likely PNG image self.assertTrue(self.completed_was_called) - self.assertTrue(os.path.getsize(destination) > 700000, "Image size: %s" % os.path.getsize(destination)) # > 1MB + self.assertTrue(os.path.getsize(destination) > 2000, "Image size: %s" % os.path.getsize(destination)) # > 1MB with open(destination, 'rb') as f: self.assertTrue(b'PNG' in f.read()) # is a PNG diff --git a/tests/src/python/test_provider_memory.py b/tests/src/python/test_provider_memory.py index 2239ebe7450..9fbbc6308b0 100644 --- a/tests/src/python/test_provider_memory.py +++ b/tests/src/python/test_provider_memory.py @@ -26,7 +26,9 @@ from qgis.core import ( QgsWkbTypes, NULL, QgsMemoryProviderUtils, - QgsCoordinateReferenceSystem + QgsCoordinateReferenceSystem, + QgsRectangle, + QgsTestUtils ) from qgis.testing import ( @@ -413,6 +415,23 @@ class TestPyQgsMemoryProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(layer.fields()[0].name(), 'rect') self.assertEqual(layer.fields()[0].type(), QVariant.String) # should be mapped to string + def testThreadSafetyWithIndex(self): + layer = QgsVectorLayer('Point?crs=epsg:4326&index=yes&field=pk:integer&field=cnt:int8&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk', + 'test', 'memory') + + provider = layer.dataProvider() + f = QgsFeature() + f.setAttributes([5, -200, NULL, 'NuLl', '5']) + f.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) + + for i in range(100000): + provider.addFeatures([f]) + + # filter rect request + extent = QgsRectangle(-73, 70, -63, 80) + request = QgsFeatureRequest().setFilterRect(extent) + self.assertTrue(QgsTestUtils.testProviderIteratorThreadSafety(self.source, request)) + class TestPyQgsMemoryProviderIndexed(unittest.TestCase, ProviderTestCase): diff --git a/tests/src/python/test_provider_ogr_gpkg.py b/tests/src/python/test_provider_ogr_gpkg.py index 2e01cd6bc53..ea2783a78de 100644 --- a/tests/src/python/test_provider_ogr_gpkg.py +++ b/tests/src/python/test_provider_ogr_gpkg.py @@ -13,6 +13,7 @@ __copyright__ = 'Copyright 2016, Even Rouault' __revision__ = '$Format:%H$' import os +import re import shutil import sys import tempfile @@ -39,6 +40,9 @@ from qgis.core import (QgsFeature, from qgis.PyQt.QtCore import QCoreApplication, QVariant from qgis.testing import start_app, unittest from qgis.utils import spatialite_connect +from utilities import unitTestDataPath + +TEST_DATA_DIR = unitTestDataPath() def GDAL_COMPUTE_VERSION(maj, min, rev): @@ -834,6 +838,65 @@ class TestPyQgsOGRProviderGpkg(unittest.TestCase): self.assertTrue(vl.dataProvider().capabilities() & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(vl.dataProvider().createSpatialIndex()) + def testSubSetStringEditable_bug17795(self): + """Test that a layer is not editable after setting a subset and it's reverted to editable after the filter is removed""" + + isEditable = QgsVectorDataProvider.ChangeAttributeValues + testPath = TEST_DATA_DIR + '/' + 'provider/bug_17795.gpkg|layername=bug_17795' + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + self.assertTrue(vl.isValid()) + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + vl.setSubsetString('') + self.assertTrue(vl.isValid()) + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + vl.setSubsetString('"category" = \'one\'') + self.assertTrue(vl.isValid()) + self.assertFalse(vl.dataProvider().capabilities() & isEditable) + + vl.setSubsetString('') + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + def testSubsetStringExtent_bug17863(self): + """Check that the extent is correct when applied in the ctor and when + modified after a subset string is set """ + + def _lessdigits(s): + return re.sub(r'(\d+\.\d{3})\d+', r'\1', s) + + testPath = TEST_DATA_DIR + '/' + 'provider/bug_17795.gpkg|layername=bug_17795' + subSetString = '"name" = \'int\'' + subSet = '|layername=bug_17795|subset=%s' % subSetString + + # unfiltered + vl = QgsVectorLayer(testPath, 'test', 'ogr') + self.assertTrue(vl.isValid()) + unfiltered_extent = _lessdigits(vl.extent().toString()) + del(vl) + + # filter after construction ... + subSet_vl2 = QgsVectorLayer(testPath, 'test', 'ogr') + self.assertEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + # ... apply filter now! + subSet_vl2.setSubsetString(subSetString) + self.assertEqual(subSet_vl2.subsetString(), subSetString) + self.assertNotEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + filtered_extent = _lessdigits(subSet_vl2.extent().toString()) + del(subSet_vl2) + + # filtered in constructor + subSet_vl = QgsVectorLayer(testPath + subSet, 'subset_test', 'ogr') + self.assertEqual(subSet_vl.subsetString(), subSetString) + self.assertTrue(subSet_vl.isValid()) + + # This was failing in bug 17863 + self.assertEqual(_lessdigits(subSet_vl.extent().toString()), filtered_extent) + self.assertNotEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_postgres.py b/tests/src/python/test_provider_postgres.py index 031bdf36b98..ac1482441b3 100644 --- a/tests/src/python/test_provider_postgres.py +++ b/tests/src/python/test_provider_postgres.py @@ -37,11 +37,13 @@ from qgis.core import ( QgsReadWriteContext, QgsRectangle, QgsDefaultValue, - QgsDataSourceUri, - QgsProject + QgsProject, + QgsWkbTypes, + QgsGeometry ) -from qgis.gui import QgsGui +from qgis.gui import QgsGui, QgsAttributeForm from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir, QObject +from qgis.PyQt.QtWidgets import QLabel from qgis.testing import start_app, unittest from qgis.PyQt.QtXml import QDomDocument from utilities import unitTestDataPath @@ -193,6 +195,8 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): test_table(self.dbconn, 'mls2d', 'MultiLineString ((0 0, 1 1),(2 2, 3 3))') test_table(self.dbconn, 'mls3d', 'MultiLineStringZ ((0 0 0, 1 1 1),(2 2 2, 3 3 3))') + test_table(self.dbconn, 'pt4d', 'PointZM (1 2 3 4)') + def testGetFeaturesUniqueId(self): """ Test tables with inheritance for unique ids @@ -355,6 +359,29 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): for i in range(100): iterators.append(self.vl.getFeatures(request)) + def testTransactionDirtyName(self): + # create a vector ayer based on postgres + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') + self.assertTrue(vl.isValid()) + + # prepare a project with transactions enabled + p = QgsProject() + p.setAutoTransaction(True) + p.addMapLayers([vl]) + vl.startEditing() + + # update the data within the transaction + tr = vl.dataProvider().transaction() + sql = "update qgis_test.some_poly_data set pk=1 where pk=1" + name = "My Awesome Transaction!" + self.assertTrue(tr.executeSql(sql, True, name)[0]) + + # test name + self.assertEqual(vl.undoStack().command(0).text(), name) + + # rollback + vl.rollBack() + def testTransactionDirty(self): # create a vector layer based on postgres vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') @@ -401,6 +428,46 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): ft1 = vl.getFeatures('pk=1') self.assertFalse(ft1.nextFeature(f)) + def testTransactionConstrains(self): + # create a vector layer based on postgres + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'id\' table="qgis_test"."check_constraints" sql=', 'test', 'postgres') + self.assertTrue(vl.isValid()) + + # prepare a project with transactions enabled + p = QgsProject() + p.setAutoTransaction(True) + p.addMapLayers([vl]) + + # get feature + f = QgsFeature() + self.assertTrue(vl.getFeatures('id=1').nextFeature(f)) + self.assertEqual(f.attributes(), [1, 4, 3]) + + # start edition + vl.startEditing() + + # update attribute form with a failing constraints + # coming from the database if attributes are updated + # one at a time. + # Current feature: a = 4 / b = 3 + # Update feature: a = 1 / b = 0 + # If updated one at a time, '(a = 1) < (b = 3)' => FAIL! + form = QgsAttributeForm(vl, f) + for w in form.findChildren(QLabel): + if w.buddy(): + spinBox = w.buddy() + if w.text() == 'a': + spinBox.setValue(1) + elif w.text() == 'b': + spinBox.setValue(0) + + # save + form.save() + + # check new values + self.assertTrue(vl.getFeatures('id=1').nextFeature(f)) + self.assertEqual(f.attributes(), [1, 1, 0]) + def testTransactionTuple(self): # create a vector layer based on postgres vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres') @@ -758,7 +825,7 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(oflds.size(), flds.size()) for i in range(oflds.size()): self.assertEqual(oflds[i].name(), flds[i].name()) - pks = olyr.pkAttributeList() + pks = olyr.primaryKeyAttributes() self.assertEqual(len(pks), len(kfnames)) for i in range(0, len(kfnames)): self.assertEqual(oflds[pks[i]].name(), kfnames[i]) @@ -782,7 +849,7 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): table = ("%s" % table) if schema is None else ("\"%s\".\"%s\"" % (schema, table)) dest_uri = "%s sslmode=disable table=%s (geom) sql" % (self.dbconn, table) - err = QgsVectorLayerExporter.exportLayer(lyr, dest_uri, "postgres", lyr.crs()) + QgsVectorLayerExporter.exportLayer(lyr, dest_uri, "postgres", lyr.crs()) olyr = QgsVectorLayer(dest_uri, "y", "postgres") self.assertTrue(olyr.isValid(), "Failed URI: %s" % dest_uri) @@ -1010,6 +1077,26 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase): ids = styles[1] self.assertEqual(len(ids), 0) + def testCurveToMultipolygon(self): + self.execSQLCommand('CREATE TABLE IF NOT EXISTS multicurve(pk SERIAL NOT NULL PRIMARY KEY, geom public.geometry(MultiPolygon, 4326))') + self.execSQLCommand('TRUNCATE multicurve') + + vl = QgsVectorLayer(self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=MULTIPOLYGON table="multicurve" (geom) sql=', 'test', 'postgres') + + f = QgsFeature(vl.fields()) + f.setGeometry(QgsGeometry.fromWkt('CurvePolygon(CircularString (20 30, 50 30, 50 90, 10 50, 20 30))')) + self.assertTrue(vl.startEditing()) + self.assertTrue(vl.addFeatures([f])) + self.assertTrue(vl.commitChanges()) + + f = next(vl.getFeatures(QgsFeatureRequest())) + + g = f.geometry().constGet() + self.assertTrue(g) + self.assertEqual(g.wkbType(), QgsWkbTypes.MultiPolygon) + self.assertEqual(g.childCount(), 1) + self.assertTrue(g.childGeometry(0).vertexCount() > 3) + class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase): diff --git a/tests/src/python/test_provider_shapefile.py b/tests/src/python/test_provider_shapefile.py index f6e612dfd11..d97e377fa94 100644 --- a/tests/src/python/test_provider_shapefile.py +++ b/tests/src/python/test_provider_shapefile.py @@ -13,6 +13,7 @@ __copyright__ = 'Copyright 2015, The QGIS Project' __revision__ = '$Format:%H$' import os +import re import tempfile import shutil import glob @@ -209,7 +210,7 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): ids = [f.id() for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('pk=1'))] vl.selectByIds(ids) self.assertEqual(vl.selectedFeatureIds(), ids) - self.assertEqual(vl.pendingFeatureCount(), 5) + self.assertEqual(vl.featureCount(), 5) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(3)) self.assertTrue(vl.commitChanges()) @@ -569,7 +570,7 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): ds = None def testOpenWithFilter(self): - file_path = os.path.join(TEST_DATA_DIR, 'provider') + file_path = os.path.join(TEST_DATA_DIR, 'provider', 'shapefile.shp') uri = '{}|layerid=0|subset="name" = \'Apple\''.format(file_path) # ensure that no longer required ogr SQL layers are correctly cleaned up # we need to run this twice for the incorrect cleanup asserts to trip, @@ -577,7 +578,7 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): # connection pool for i in range(2): vl = QgsVectorLayer(uri) - self.assertTrue(vl.isValid()) + self.assertTrue(vl.isValid(), 'Layer not valid, iteration {}'.format(i + 1)) self.assertEqual(vl.featureCount(), 1) f = next(vl.getFeatures()) self.assertEqual(f['name'], 'Apple') @@ -612,6 +613,65 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): self.assertTrue(vl.dataProvider().capabilities() & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(vl.dataProvider().createSpatialIndex()) + def testSubSetStringEditable_bug17795(self): + """Test that a layer is not editable after setting a subset and it's reverted to editable after the filter is removed""" + + testPath = TEST_DATA_DIR + '/' + 'lines.shp' + isEditable = QgsVectorDataProvider.ChangeAttributeValues + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + self.assertTrue(vl.isValid()) + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + vl.setSubsetString('') + self.assertTrue(vl.isValid()) + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + vl = QgsVectorLayer(testPath, 'subset_test', 'ogr') + vl.setSubsetString('"Name" = \'Arterial\'') + self.assertTrue(vl.isValid()) + self.assertFalse(vl.dataProvider().capabilities() & isEditable) + + vl.setSubsetString('') + self.assertTrue(vl.dataProvider().capabilities() & isEditable) + + def testSubsetStringExtent_bug17863(self): + """Check that the extent is correct when applied in the ctor and when + modified after a subset string is set """ + + def _lessdigits(s): + return re.sub(r'(\d+\.\d{3})\d+', r'\1', s) + + testPath = TEST_DATA_DIR + '/' + 'points.shp' + subSetString = '"Class" = \'Biplane\'' + subSet = '|layerid=0|subset=%s' % subSetString + + # unfiltered + vl = QgsVectorLayer(testPath, 'test', 'ogr') + self.assertTrue(vl.isValid()) + unfiltered_extent = _lessdigits(vl.extent().toString()) + del(vl) + + # filter after construction ... + subSet_vl2 = QgsVectorLayer(testPath, 'test', 'ogr') + self.assertEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + # ... apply filter now! + subSet_vl2.setSubsetString(subSetString) + self.assertEqual(subSet_vl2.subsetString(), subSetString) + self.assertNotEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + filtered_extent = _lessdigits(subSet_vl2.extent().toString()) + del(subSet_vl2) + + # filtered in constructor + subSet_vl = QgsVectorLayer(testPath + subSet, 'subset_test', 'ogr') + self.assertEqual(subSet_vl.subsetString(), subSetString) + self.assertTrue(subSet_vl.isValid()) + + # This was failing in bug 17863 + self.assertEqual(_lessdigits(subSet_vl.extent().toString()), filtered_extent) + self.assertNotEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index 2ea17acdd73..bae1499490d 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -15,6 +15,7 @@ __revision__ = '$Format:%H$' import qgis # NOQA import os +import re import sys import shutil import tempfile @@ -28,12 +29,13 @@ from qgis.core import (QgsVectorLayer, QgsFieldConstraints, QgsVectorLayerUtils, QgsSettings, - QgsDefaultValue) + QgsDefaultValue, + QgsWkbTypes) from qgis.testing import start_app, unittest from utilities import unitTestDataPath from providertestbase import ProviderTestCase -from qgis.PyQt.QtCore import QVariant +from qgis.PyQt.QtCore import QObject, QVariant from qgis.utils import spatialite_connect @@ -91,6 +93,33 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): sql += "VALUES (1, 'toto', GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))', 4326))" cur.execute(sql) + # table with Z dimension geometry + sql = "CREATE TABLE test_z (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_z', 'geometry', 4326, 'POINT', 'XYZ')" + cur.execute(sql) + sql = "INSERT INTO test_z (id, name, geometry) " + sql += "VALUES (1, 'toto', GeomFromText('POINT Z (0 0 1)', 4326))" + cur.execute(sql) + + # table with M value geometry + sql = "CREATE TABLE test_m (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_m', 'geometry', 4326, 'POINT', 'XYM')" + cur.execute(sql) + sql = "INSERT INTO test_m (id, name, geometry) " + sql += "VALUES (1, 'toto', GeomFromText('POINT M (0 0 1)', 4326))" + cur.execute(sql) + + # table with Z dimension and M value geometry + sql = "CREATE TABLE test_zm (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_zm', 'geometry', 4326, 'POINT', 'XYZM')" + cur.execute(sql) + sql = "INSERT INTO test_zm (id, name, geometry) " + sql += "VALUES (1, 'toto', GeomFromText('POINT ZM (0 0 1 1)', 4326))" + cur.execute(sql) + # table with multiple column primary key sql = "CREATE TABLE test_pg_mk (id INTEGER NOT NULL, name TEXT NOT NULL, PRIMARY KEY(id,name))" cur.execute(sql) @@ -145,6 +174,12 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): sql = "SELECT AddGeometryColumn('test_relation_b', 'Geometry', 4326, 'POLYGON', 'XY')" cur.execute(sql) + # table to test auto increment + sql = "CREATE TABLE test_autoincrement(id INTEGER PRIMARY KEY AUTOINCREMENT, num INTEGER);" + cur.execute(sql) + sql = "INSERT INTO test_autoincrement (num) VALUES (123);" + cur.execute(sql) + # tables with constraints sql = "CREATE TABLE test_constraints(id INTEGER PRIMARY KEY, num INTEGER NOT NULL, desc TEXT UNIQUE, desc2 TEXT, num2 INTEGER NOT NULL UNIQUE)" cur.execute(sql) @@ -153,6 +188,37 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): sql = "CREATE TABLE test_defaults (id INTEGER NOT NULL PRIMARY KEY, name TEXT DEFAULT 'qgis ''is good', number INTEGER DEFAULT 5, number2 REAL DEFAULT 5.7, no_default REAL)" cur.execute(sql) + # simple table with catgorized points + sql = "CREATE TABLE test_filter (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_filter', 'geometry', 4326, 'POINT', 'XY')" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (1, 'ext', GeomFromText('POINT(0 0)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (2, 'ext', GeomFromText('POINT(0 3)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (3, 'ext', GeomFromText('POINT(3 3)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (4, 'ext', GeomFromText('POINT(3 0)', 4326))" + cur.execute(sql) + + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (5, 'int', GeomFromText('POINT(1 1)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (6, 'int', GeomFromText('POINT(1 2)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (7, 'int', GeomFromText('POINT(2 2)', 4326))" + cur.execute(sql) + sql = "INSERT INTO test_filter (id, name, geometry) " + sql += "VALUES (8, 'int', GeomFromText('POINT(2 1)', 4326))" + cur.execute(sql) + cur.execute("COMMIT") con.close() @@ -308,6 +374,31 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(sum_id1, 32) self.assertEqual(sum_id2, 32) + def test_zm(self): + """Test Z dimension and M value""" + l = QgsVectorLayer("dbname=%s table='test_z' (geometry) key='id'" % self.dbname, "test_z", "spatialite") + self.assertTrue(l.isValid()) + self.assertTrue(QgsWkbTypes.hasZ(l.wkbType())) + feature = l.getFeature(1) + geom = feature.geometry().constGet() + self.assertEqual(geom.z(), 1.0) + + l = QgsVectorLayer("dbname=%s table='test_m' (geometry) key='id'" % self.dbname, "test_m", "spatialite") + self.assertTrue(l.isValid()) + self.assertTrue(QgsWkbTypes.hasM(l.wkbType())) + feature = l.getFeature(1) + geom = feature.geometry().constGet() + self.assertEqual(geom.m(), 1.0) + + l = QgsVectorLayer("dbname=%s table='test_zm' (geometry) key='id'" % self.dbname, "test_zm", "spatialite") + self.assertTrue(l.isValid()) + self.assertTrue(QgsWkbTypes.hasZ(l.wkbType())) + self.assertTrue(QgsWkbTypes.hasM(l.wkbType())) + feature = l.getFeature(1) + geom = feature.geometry().constGet() + self.assertEqual(geom.z(), 1.0) + self.assertEqual(geom.m(), 1.0) + def test_case(self): """Test case sensitivity issues""" l = QgsVectorLayer("dbname=%s table='test_n' (geometry) key='id'" % self.dbname, "test_n1", "spatialite") @@ -515,6 +606,14 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): self.assertTrue(fields.at(4).constraints().constraints() & QgsFieldConstraints.ConstraintUnique) self.assertEqual(fields.at(4).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique), QgsFieldConstraints.ConstraintOriginProvider) + def testSkipConstraintCheck(self): + vl = QgsVectorLayer("dbname=%s table=test_autoincrement" % self.dbname, "test_autoincrement", + "spatialite") + self.assertTrue(vl.isValid()) + + self.assertTrue(vl.dataProvider().skipConstraintCheck(0, QgsFieldConstraints.ConstraintUnique, str("Autogenerate"))) + self.assertFalse(vl.dataProvider().skipConstraintCheck(0, QgsFieldConstraints.ConstraintUnique, 123)) + # This test would fail. It would require turning on WAL def XXXXXtestLocking(self): @@ -596,6 +695,52 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(set(indexed_columns), set(['name', 'number'])) con.close() + def testSubsetStringExtent_bug17863(self): + """Check that the extent is correct when applied in the ctor and when + modified after a subset string is set """ + + def _lessdigits(s): + return re.sub(r'(\d+\.\d{3})\d+', r'\1', s) + + testPath = "dbname=%s table='test_filter' (geometry) key='id'" % self.dbname + + subSetString = '"name" = \'int\'' + subSet = ' sql=%s' % subSetString + + # unfiltered + vl = QgsVectorLayer(testPath, 'test', 'spatialite') + self.assertTrue(vl.isValid()) + self.assertEqual(vl.featureCount(), 8) + unfiltered_extent = _lessdigits(vl.extent().toString()) + self.assertNotEqual('Empty', unfiltered_extent) + del(vl) + + # filter after construction ... + subSet_vl2 = QgsVectorLayer(testPath, 'test', 'spatialite') + self.assertEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + self.assertEqual(subSet_vl2.featureCount(), 8) + # ... apply filter now! + subSet_vl2.setSubsetString(subSetString) + self.assertEqual(subSet_vl2.featureCount(), 4) + self.assertEqual(subSet_vl2.subsetString(), subSetString) + self.assertNotEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) + filtered_extent = _lessdigits(subSet_vl2.extent().toString()) + del(subSet_vl2) + + # filtered in constructor + subSet_vl = QgsVectorLayer(testPath + subSet, 'subset_test', 'spatialite') + self.assertEqual(subSet_vl.subsetString(), subSetString) + self.assertTrue(subSet_vl.isValid()) + + # This was failing in bug 17863 + self.assertEqual(subSet_vl.featureCount(), 4) + self.assertEqual(_lessdigits(subSet_vl.extent().toString()), filtered_extent) + self.assertNotEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent) + + self.assertTrue(subSet_vl.setSubsetString('')) + self.assertEqual(subSet_vl.featureCount(), 8) + self.assertEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_virtual.py b/tests/src/python/test_provider_virtual.py index 53f9e20b8b8..9b4a865de25 100644 --- a/tests/src/python/test_provider_virtual.py +++ b/tests/src/python/test_provider_virtual.py @@ -16,6 +16,7 @@ import qgis # NOQA import os from qgis.core import (QgsVectorLayer, + QgsField, QgsFeature, QgsFeatureRequest, QgsGeometry, @@ -81,6 +82,11 @@ class TestQgsVirtualLayerProvider(unittest.TestCase, ProviderTestCase): print("****************************************************") pass + def testGetFeaturesThreadSafety(self): + # provider does not work with this test - sqlite mutex prevents + # execution + pass + def tearDown(self): """Run after each test.""" pass @@ -842,6 +848,29 @@ class TestQgsVirtualLayerProvider(unittest.TestCase, ProviderTestCase): QgsProject.instance().removeMapLayers([v1.id(), v2.id(), v3.id()]) + def testFieldsWithSpecialCharacters(self): + ml = QgsVectorLayer("Point?srid=EPSG:4326&field=123:int", "mem_with_nontext_fieldnames", "memory") + self.assertEqual(ml.isValid(), True) + QgsProject.instance().addMapLayer(ml) + + ml.startEditing() + self.assertTrue(ml.addAttribute(QgsField('abc:123', QVariant.String))) + f1 = QgsFeature(ml.fields()) + f1.setGeometry(QgsGeometry.fromWkt('POINT(0 0)')) + f2 = QgsFeature(ml.fields()) + f2.setGeometry(QgsGeometry.fromWkt('POINT(1 1)')) + ml.addFeatures([f1, f2]) + ml.commitChanges() + + vl = QgsVectorLayer("?query=select * from mem_with_nontext_fieldnames", "vl", "virtual") + self.assertEqual(vl.isValid(), True) + self.assertEqual(vl.fields().at(0).name(), '123') + self.assertEqual(vl.fields().at(1).name(), 'abc:123') + + self.assertEqual(vl.featureCount(), 2) + + QgsProject.instance().removeMapLayer(ml) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index 33b6fda0768..7e3a9b6c6a8 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -14,6 +14,7 @@ __revision__ = '$Format:%H$' import hashlib import os +import re import tempfile import shutil @@ -104,7 +105,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -986,7 +987,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -1285,7 +1286,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -1564,7 +1565,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """ - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename,my:othertypename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename,my:othertypename'), 'wb') as f: f.write(schema.encode('UTF-8')) with open(sanitize(endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename,my:othertypename&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER= @@ -1630,7 +1631,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): # main table not appearing in first - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:othertypename,my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:othertypename,my:typename'), 'wb') as f: f.write(schema.encode('UTF-8')) vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='2.0.0' sql=SELECT * FROM othertypename o, typename WHERE typename.id = o.main_id AND typename.id > 0 ORDER BY typename.id DESC", 'test', 'WFS') @@ -1680,7 +1681,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(len(fields), 1, fields) self.assertEqual(fields[0].name(), 'id') - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(schema.encode('UTF-8')) # Duplicate fields @@ -1884,7 +1885,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): f.write(schema.encode('UTF-8')) with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=my:typename'), 'wb') as f: f.write(schema.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(schema.encode('UTF-8')) # Existing function and validation enabled @@ -1949,7 +1950,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2066,7 +2067,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2174,7 +2175,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2470,7 +2471,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2546,7 +2547,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.format(endpoint=endpoint).encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2662,7 +2663,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): """.encode('UTF-8')) - with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: f.write(""" @@ -2710,6 +2711,337 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): self.assertAlmostEqual(provider_extent.yMinimum(), 66.33, 3) self.assertAlmostEqual(provider_extent.yMaximum(), 78.3, 3) + def testWfs20DescribeFeatureTypeSingularForm(self): + """Specs are inconsistent and some 2.0 servers use the TYPENAME singular form""" + + endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_describefeaturetype_singular_form' + + with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=2.0.0'), 'wb') as f: + f.write(""" + + + + my:typename + Title + Abstract + urn:ogc:def:crs:EPSG::4326 + + -71.123 66.33 + -65.32 78.3 + + + +""".encode('UTF-8')) + + # plural form not supported! + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f: + f.write(b"") + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f: + f.write(""" + + + + + + + + + + + + + + + + + + + + + + +""".encode('UTF-8')) + + # Create test layer + vl = QgsVectorLayer("url='http://" + endpoint + "' version='2.0.0' typename='my:typename'", 'test', 'WFS') + self.assertTrue(vl.isValid()) + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: + f.write(""" + + + + 66.33 -70.33266.33 -70.332 + 66.33 -70.332 + 1 + 100 + Orange + oranGe + 1 + + + + + 70.8 -68.270.8 -68.2 + 70.8 -68.2 + 2 + 200 + Apple + Apple + 2 + + + + + 78.3 -65.3278.3 -65.32 + 78.3 -65.32 + 4 + 400 + Honey + Honey + 4 + + + + + 3 + 300 + Pear + PEaR + 3 + + + + + 78.23 -71.12378.23 -71.123 + 78.23 -71.123 + 5 + -200 + NuLl + 5 + + +""".encode('UTF-8')) + + features = list(vl.getFeatures()) + self.assertEqual(len(features), 5) + geom = features[0].geometry() + self.assertAlmostEqual(geom.asPoint().x(), -70.332, 1) + self.assertAlmostEqual(geom.asPoint().y(), 66.33, 1) + + def testWfs20SamServer(self): + """Unknown russian WFS 2.0.0 http://geoportal.samregion.ru/wfs12""" + + endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_sam' + + with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=2.0.0'), 'wb') as f: + f.write(""" + + + EC422 + Title + Abstract + urn:ogc:def:crs:EPSG::4326 + + +""".encode('UTF-8')) + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=EC422'), 'wb') as f: + f.write(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""".encode('UTF-8')) + + feature_content = """ + + + + + 13172455 + + + + + + + + + + + + + + checked + Андрей Чернов + false + + false + false + false + + + + + + 9540051.88156246 5997366.8135842243 9539934.21894572 5997127.7749966066 9539822.1483417116 5996862.6127466606 9539504.179093 5996097.3096572906 9539529.9650254529 5996093.5547346519 9539584.6281441431 5996148.1036405731 9539709.8322301712 5996306.1476564864 9539514.2094426583 5996393.6699969191 9539315.5400513224 5996461.6283053206 9539418.05506746 5996708.1687648129 9539601.002140563 5996948.5162237845 9539715.6644945163 5997103.3323533693 9539806.8714339714 5997185.6346932752 9539980.407018993 5997401.3173021814 9540034.9262902886 5997461.1185212145 9540144.520062916 5997647.082066942 9540205.6388517376 5997752.865820759 9540413.93051952 5998022.2412844934 9540636.78114721 5998289.1650444875 9540652.2228743583 5998292.3310790323 9540739.3873799425 5998253.7093249289 9540742.8882315382 5998264.8666193094 9540928.0440610871 5998447.0783388717 9540964.2606249321 5998465.5247420967 9540992.4471577276 5998468.9503919454 9541266.994057592 5998700.44280944 9541489.2456994727 5998870.8459224468 9542015.9473830853 5999245.7052185535 9542481.7197104339 5999585.458734531 9542594.2400093842 5999581.2418252891 9542791.0368823726 5999731.6623752853 9543204.6598267779 6000066.4150706194 9543245.7990274262 6000086.6195615921 9543303.6317887139 6000098.2128836326 9543392.7859923933 6000084.2088917186 9543473.2299142312 6000041.19114427 9543582.34122052 5999959.7280100482 9543796.5102230646 5999788.4518707721 9544237.3357650079 6000148.6245372053 9544242.356376797 6000146.87913009 + + + + + + + + + + 13172458 + + + + + + + + + + + + + + checked + Андрей Чернов + false + + false + false + false + + + + + + 9540865.6444970388 5998183.9317809641 9540775.852046784 5997947.0331188506 9540680.5655983184 5997718.6045682346 9540569.58023185 5997466.57064837 9540466.8184371851 5997200.4314374486 9540244.6014337484 5996676.7638938017 9540169.35653367 5996705.7142945267 9540148.0577711649 5996682.1794316517 9540111.4476553015 5996665.8381209867 9540077.9721918479 5996676.0606173435 9540043.4139141534 5996711.499830937 9540043.9248592574 5996752.0983768944 9540045.5009976458 5996766.0802566176 9539966.0914670844 5996797.8834326165 9539818.507223323 5996864.1487550773 9539610.9541244339 5996949.3710925905 + + + + + + + + + + 13172454 + + + + + + + + + + + + + + checked + Андрей Чернов + false + + false + false + false + + + + + + 9542485.8345187921 5998971.7039023517 9542453.32676163 5998980.1340847686 9542423.4887301736 5998965.47791457 9541839.9022341352 5998539.8925312571 9541447.9249842446 5998260.2124332683 9541414.5872483123 5998203.50518699 9541330.9900199063 5998001.1229150137 9540804.3199365921 5996757.0503186928 9540212.17485467 5997003.9579582512 9539930.5850430559 5997125.116400606 9539816.1399206612 5997176.6829545 9539806.7162561137 5997183.794419908 + + + + + + +""" + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: + f.write(feature_content.encode('UTF-8')) + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=EC422&SRSNAME=urn:ogc:def:crs:EPSG::4326'), 'wb') as f: + f.write(feature_content.encode('UTF-8')) + + vl = QgsVectorLayer("url='http://" + endpoint + "' version='2.0.0' typename='EC422'", 'test', 'WFS') + self.assertTrue(vl.isValid()) + features = list(vl.getFeatures()) + self.assertEqual(len(features), 3) + geom = features[0].geometry() + geom_string = geom.asWkt() + geom_string = re.sub(r'\.\d+', '', geom_string)[:100] + self.assertEqual(geom_string, "LineString (9540051 5997366, 9539934 5997127, 9539822 5996862, 9539504 5996097, 9539529 5996093, 953") + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsatlascomposition.py b/tests/src/python/test_qgsatlascomposition.py deleted file mode 100644 index ade29fe9523..00000000000 --- a/tests/src/python/test_qgsatlascomposition.py +++ /dev/null @@ -1,380 +0,0 @@ -# -*- coding: utf-8 -*- -''' -test_qgsatlascomposition.py - -------------------------------------- - Date : Oct 2012 - Copyright : (C) 2012 by Dr. Hugo Mercier - email : hugo dot mercier at oslandia dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -''' -import qgis # NOQA -import os -import glob -import shutil -import tempfile -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgis.PyQt.QtCore import QFileInfo, QRectF, qWarning -from qgis.core import ( - QgsCategorizedSymbolRenderer, - QgsComposerLabel, - QgsComposerLegend, - QgsComposerMap, - QgsComposition, - QgsCoordinateReferenceSystem, - QgsFeature, - QgsFillSymbol, - QgsFontUtils, - QgsGeometry, - QgsMarkerSymbol, - QgsPointXY, - QgsProject, - QgsRectangle, - QgsRendererCategory, - QgsSingleSymbolRenderer, - QgsVectorLayer, -) -from qgscompositionchecker import QgsCompositionChecker - -start_app() - - -class TestQgsAtlasComposition(unittest.TestCase): - - def testCase(self): - self.TEST_DATA_DIR = unitTestDataPath() - tmppath = tempfile.mkdtemp() - for file in glob.glob(os.path.join(self.TEST_DATA_DIR, 'france_parts.*')): - shutil.copy(os.path.join(self.TEST_DATA_DIR, file), tmppath) - vectorFileInfo = QFileInfo(tmppath + "/france_parts.shp") - mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") - - QgsProject.instance().addMapLayers([mVectorLayer]) - self.layers = [mVectorLayer] - - # create composition with composer map - - # select epsg:2154 - crs = QgsCoordinateReferenceSystem() - crs.createFromSrid(2154) - QgsProject.instance().setCrs(crs) - - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - - # fix the renderer, fill with green - props = {"color": "0,127,0"} - fillSymbol = QgsFillSymbol.createSimple(props) - renderer = QgsSingleSymbolRenderer(fillSymbol) - mVectorLayer.setRenderer(renderer) - - # the atlas map - self.mAtlasMap = QgsComposerMap(self.mComposition, 20, 20, 130, 130) - self.mAtlasMap.setFrameEnabled(True) - self.mAtlasMap.setLayers([mVectorLayer]) - self.mComposition.addComposerMap(self.mAtlasMap) - - # the atlas - self.mAtlas = self.mComposition.atlasComposition() - self.mAtlas.setCoverageLayer(mVectorLayer) - self.mAtlas.setEnabled(True) - self.mComposition.setAtlasMode(QgsComposition.ExportAtlas) - - # an overview - self.mOverview = QgsComposerMap(self.mComposition, 180, 20, 50, 50) - self.mOverview.setFrameEnabled(True) - self.mOverview.overview().setFrameMap(self.mAtlasMap.id()) - self.mOverview.setLayers([mVectorLayer]) - self.mComposition.addComposerMap(self.mOverview) - nextent = QgsRectangle(49670.718, 6415139.086, 699672.519, 7065140.887) - self.mOverview.setNewExtent(nextent) - - # set the fill symbol of the overview map - props2 = {"color": "127,0,0,127"} - fillSymbol2 = QgsFillSymbol.createSimple(props2) - self.mOverview.overview().setFrameSymbol(fillSymbol2) - - # header label - self.mLabel1 = QgsComposerLabel(self.mComposition) - self.mComposition.addComposerLabel(self.mLabel1) - self.mLabel1.setText("[% \"NAME_1\" %] area") - self.mLabel1.setFont(QgsFontUtils.getStandardTestFont()) - self.mLabel1.adjustSizeToText() - self.mLabel1.setSceneRect(QRectF(150, 5, 60, 15)) - - qWarning( - "header label font: %s exactMatch:%s" % (self.mLabel1.font().toString(), self.mLabel1.font().exactMatch())) - - # feature number label - self.mLabel2 = QgsComposerLabel(self.mComposition) - self.mComposition.addComposerLabel(self.mLabel2) - self.mLabel2.setText("# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]") - self.mLabel2.setFont(QgsFontUtils.getStandardTestFont()) - self.mLabel2.adjustSizeToText() - self.mLabel2.setSceneRect(QRectF(150, 200, 60, 15)) - - qWarning("feature number label font: %s exactMatch:%s" % ( - self.mLabel2.font().toString(), self.mLabel2.font().exactMatch())) - - self.filename_test() - self.autoscale_render_test() - self.fixedscale_render_test() - self.predefinedscales_render_test() - self.hidden_render_test() - self.legend_test() - self.rotation_test() - - shutil.rmtree(tmppath, True) - - def filename_test(self): - self.mAtlas.setFilenamePattern("'output_' || @atlas_featurenumber") - self.mAtlas.beginRender() - for i in range(0, self.mAtlas.numFeatures()): - self.mAtlas.prepareForFeature(i) - expected = "output_%d" % (i + 1) - self.assertEqual(self.mAtlas.currentFilename(), expected) - self.mAtlas.endRender() - - def autoscale_render_test(self): - self.mAtlasMap.setAtlasDriven(True) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) - self.mAtlasMap.setAtlasMargin(0.10) - - self.mAtlas.beginRender() - - for i in range(0, 2): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_autoscale%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - self.mAtlasMap.setAtlasDriven(False) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) - self.mAtlasMap.setAtlasMargin(0) - - def fixedscale_render_test(self): - self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) - self.mAtlasMap.setAtlasDriven(True) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) - - self.mAtlas.beginRender() - - for i in range(0, 2): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_fixedscale%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - def predefinedscales_render_test(self): - self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) - self.mAtlasMap.setAtlasDriven(True) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Predefined) - - scales = [1800000, 5000000] - self.mAtlas.setPredefinedScales(scales) - for i, s in enumerate(self.mAtlas.predefinedScales()): - assert s == scales[i] - - self.mAtlas.beginRender() - - for i in range(0, 2): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_predefinedscales%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - def hidden_render_test(self): - self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) - self.mAtlas.setHideCoverage(True) - - self.mAtlas.beginRender() - - for i in range(0, 2): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_hiding%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - self.mAtlas.setHideCoverage(False) - - def sorting_render_test(self): - self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) - self.mAtlas.setHideCoverage(False) - - self.mAtlas.setSortFeatures(True) - self.mAtlas.setSortKeyAttributeIndex(4) # departement name - self.mAtlas.setSortAscending(False) - - self.mAtlas.beginRender() - - for i in range(0, 2): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_sorting%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - def filtering_render_test(self): - self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) - self.mAtlas.setHideCoverage(False) - - self.mAtlas.setSortFeatures(False) - - self.mAtlas.setFilterFeatures(True) - self.mAtlas.setFeatureFilter("substr(NAME_1,1,1)='P'") # select only 'Pays de la loire' - - self.mAtlas.beginRender() - - for i in range(0, 1): - self.mAtlas.prepareForFeature(i) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_filtering%d' % (i + 1), self.mComposition) - checker.setControlPathPrefix("atlas") - myTestResult, myMessage = checker.testComposition(0, 200) - - assert myTestResult - self.mAtlas.endRender() - - def legend_test(self): - self.mAtlasMap.setAtlasDriven(True) - self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) - self.mAtlasMap.setAtlasMargin(0.10) - - # add a point layer - ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory") - - pr = ptLayer.dataProvider() - f1 = QgsFeature(1) - f1.initAttributes(2) - f1.setAttribute(0, 1) - f1.setAttribute(1, "Test label 1") - f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-0.638, 48.954))) - f2 = QgsFeature(2) - f2.initAttributes(2) - f2.setAttribute(0, 2) - f2.setAttribute(1, "Test label 2") - f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1.682, 48.550))) - pr.addFeatures([f1, f2]) - - # categorized symbology - r = QgsCategorizedSymbolRenderer("attr", [QgsRendererCategory(1, QgsMarkerSymbol.createSimple({"color": "255,0,0"}), "red"), - QgsRendererCategory(2, QgsMarkerSymbol.createSimple({"color": "0,0,255"}), "blue")]) - ptLayer.setRenderer(r) - - QgsProject.instance().addMapLayer(ptLayer) - - # add the point layer to the map settings - layers = self.layers - layers = [ptLayer] + layers - self.mAtlasMap.setLayers(layers) - self.mOverview.setLayers(layers) - - # add a legend - legend = QgsComposerLegend(self.mComposition) - legend.moveBy(200, 100) - # sets the legend filter parameter - legend.setComposerMap(self.mAtlasMap) - legend.setLegendFilterOutAtlas(True) - self.mComposition.addComposerLegend(legend) - - self.mAtlas.beginRender() - - self.mAtlas.prepareForFeature(0) - self.mLabel1.adjustSizeToText() - - checker = QgsCompositionChecker('atlas_legend', self.mComposition) - myTestResult, myMessage = checker.testComposition() - assert myTestResult - - self.mAtlas.endRender() - - # restore state - self.mAtlasMap.setLayers([layers[1]]) - self.mComposition.removeComposerItem(legend) - QgsProject.instance().removeMapLayer(ptLayer.id()) - - def rotation_test(self): - # We will create a polygon layer with a rotated rectangle. - # Then we will make it the object layer for the atlas, - # rotate the map and test that the bounding rectangle - # is smaller than the bounds without rotation. - polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory') - poly = QgsFeature(polygonLayer.pendingFields()) - points = [(10, 15), (15, 10), (45, 40), (40, 45)] - poly.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(x[0], x[1]) for x in points]])) - polygonLayer.dataProvider().addFeatures([poly]) - QgsProject.instance().addMapLayer(polygonLayer) - - # Recreating the composer locally - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - # the atlas map - atlasMap = QgsComposerMap(composition, 20, 20, 130, 130) - atlasMap.setFrameEnabled(True) - atlasMap.setLayers([polygonLayer]) - atlasMap.setNewExtent(QgsRectangle(0, 0, 100, 50)) - composition.addComposerMap(atlasMap) - - # the atlas - atlas = composition.atlasComposition() - atlas.setCoverageLayer(polygonLayer) - atlas.setEnabled(True) - composition.setAtlasMode(QgsComposition.ExportAtlas) - - atlasMap.setAtlasDriven(True) - atlasMap.setAtlasScalingMode(QgsComposerMap.Auto) - atlasMap.setAtlasMargin(0.0) - - # Testing - atlasMap.setMapRotation(0.0) - atlas.firstFeature() - nonRotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) - - atlasMap.setMapRotation(45.0) - atlas.firstFeature() - rotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) - - assert rotatedExtent.width() < nonRotatedExtent.width() * 0.9 - assert rotatedExtent.height() < nonRotatedExtent.height() * 0.9 - - QgsProject.instance().removeMapLayer(polygonLayer) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgsbox3d.py b/tests/src/python/test_qgsbox3d.py index 6414dfa44ac..97f60aba75d 100644 --- a/tests/src/python/test_qgsbox3d.py +++ b/tests/src/python/test_qgsbox3d.py @@ -52,6 +52,14 @@ class TestQgsBox3d(unittest.TestCase): self.assertEqual(box.yMaximum(), 11.0) self.assertEqual(box.zMaximum(), 12.0) + box = QgsBox3d(QgsRectangle(5, 6, 11, 13)) + self.assertEqual(box.xMinimum(), 5.0) + self.assertEqual(box.yMinimum(), 6.0) + self.assertEqual(box.zMinimum(), 0.0) + self.assertEqual(box.xMaximum(), 11.0) + self.assertEqual(box.yMaximum(), 13.0) + self.assertEqual(box.zMaximum(), 0.0) + def testSetters(self): box = QgsBox3d(5.0, 6.0, 7.0, 10.0, 11.0, 12.0) diff --git a/tests/src/python/test_qgscomposereffects.py b/tests/src/python/test_qgscomposereffects.py deleted file mode 100644 index cdb3d6fa10e..00000000000 --- a/tests/src/python/test_qgscomposereffects.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerEffects. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler' -__date__ = '20/08/2012' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.PyQt.QtGui import QPainter, QColor - -from qgis.core import (QgsComposerShape, - QgsComposition, - QgsMapSettings, - QgsProject - ) -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerEffects(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - # create composition - self.mMapSettings = QgsMapSettings() - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - - self.mComposerRect1 = QgsComposerShape(20, 20, 150, 100, self.mComposition) - self.mComposerRect1.setShapeType(QgsComposerShape.Rectangle) - self.mComposerRect1.setBackgroundColor(QColor.fromRgb(255, 150, 0)) - self.mComposition.addComposerShape(self.mComposerRect1) - - self.mComposerRect2 = QgsComposerShape(50, 50, 150, 100, self.mComposition) - self.mComposerRect2.setShapeType(QgsComposerShape.Rectangle) - self.mComposerRect2.setBackgroundColor(QColor.fromRgb(0, 100, 150)) - self.mComposition.addComposerShape(self.mComposerRect2) - - def testBlendModes(self): - """Test that blend modes work for composer items.""" - - self.mComposerRect2.setBlendMode(QPainter.CompositionMode_Multiply) - - checker = QgsCompositionChecker('composereffects_blend', self.mComposition) - checker.setControlPathPrefix("composer_effects") - myTestResult, myMessage = checker.testComposition() - - self.mComposerRect2.setBlendMode(QPainter.CompositionMode_SourceOver) - - assert myTestResult, myMessage - - def testTransparency(self): - """Test that transparency works for composer items.""" - - self.mComposerRect2.setOpacity(0.5) - - checker = QgsCompositionChecker('composereffects_transparency', self.mComposition) - checker.setControlPathPrefix("composer_effects") - myTestResult, myMessage = checker.testComposition() - - self.mComposerRect2.setOpacity(0) - - assert myTestResult, myMessage - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerhtml.py b/tests/src/python/test_qgscomposerhtml.py deleted file mode 100644 index 0fba3336e48..00000000000 --- a/tests/src/python/test_qgscomposerhtml.py +++ /dev/null @@ -1,141 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsApplication. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = 'Dr. Horst Düster, Dr. Marco Hugentobler, Tim Sutton' -__date__ = '20/01/2011' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -import os - -from qgis.PyQt.QtCore import QUrl, qDebug -from qgis.core import (QgsComposition, - QgsComposerHtml, - QgsComposerFrame, - QgsComposerMultiFrame, - QgsMapSettings, - QgsProject - ) - -from qgscompositionchecker import QgsCompositionChecker - -from qgis.testing import start_app, unittest -from qgis.testing.mocked import get_iface -from utilities import unitTestDataPath - -start_app() - -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerHtml(unittest.TestCase): - - def setUp(self): - """Run before each test.""" - self.iface = get_iface() - self.mapSettings = QgsMapSettings() - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) # A4 landscape - - def tearDown(self): - """Run after each test.""" - print("Tear down") - - def htmlUrl(self): - """Helper to get the url of the html doc.""" - myPath = os.path.join(TEST_DATA_DIR, "test_html.html") - myUrl = QUrl("file:///" + myPath) - return myUrl - - def testTable(self): - """Test we can render a html table in a single frame.""" - composerHtml = QgsComposerHtml(self.mComposition, False) - htmlFrame = QgsComposerFrame(self.mComposition, - composerHtml, 0, 0, 100, 200) - htmlFrame.setFrameEnabled(True) - composerHtml.addFrame(htmlFrame) - composerHtml.setUrl(self.htmlUrl()) - - checker = QgsCompositionChecker('composerhtml_table', self.mComposition) - checker.setControlPathPrefix("composer_html") - myTestResult, myMessage = checker.testComposition() - - qDebug(myMessage) - self.mComposition.removeMultiFrame(composerHtml) - composerHtml = None - - assert myTestResult, myMessage - - def testTableMultiFrame(self): - """Test we can render to multiframes.""" - composerHtml = QgsComposerHtml(self.mComposition, False) - htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, - 10, 10, 100, 50) - composerHtml.addFrame(htmlFrame) - composerHtml.setResizeMode( - QgsComposerMultiFrame.RepeatUntilFinished) - composerHtml.setUseSmartBreaks(False) - composerHtml.setUrl(self.htmlUrl()) - composerHtml.frame(0).setFrameEnabled(True) - - print("Checking page 1") - myPage = 0 - checker1 = QgsCompositionChecker('composerhtml_multiframe1', self.mComposition) - checker1.setControlPathPrefix("composer_html") - myTestResult, myMessage = checker1.testComposition(myPage) - assert myTestResult, myMessage - - print("Checking page 2") - myPage = 1 - checker2 = QgsCompositionChecker('composerhtml_multiframe2', self.mComposition) - checker2.setControlPathPrefix("composer_html") - myTestResult, myMessage = checker2.testComposition(myPage) - assert myTestResult, myMessage - - self.mComposition.removeMultiFrame(composerHtml) - composerHtml = None - - assert myTestResult, myMessage - - def testHtmlSmartBreaks(self): - """Test rendering to multiframes with smart breaks.""" - composerHtml = QgsComposerHtml(self.mComposition, False) - htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, - 10, 10, 100, 52) - composerHtml.addFrame(htmlFrame) - composerHtml.setResizeMode( - QgsComposerMultiFrame.RepeatUntilFinished) - composerHtml.setUseSmartBreaks(True) - composerHtml.setUrl(self.htmlUrl()) - composerHtml.frame(0).setFrameEnabled(True) - - print("Checking page 1") - myPage = 0 - checker1 = QgsCompositionChecker('composerhtml_smartbreaks1', self.mComposition) - checker1.setControlPathPrefix("composer_html") - myTestResult, myMessage = checker1.testComposition(myPage, 200) - assert myTestResult, myMessage - - print("Checking page 2") - myPage = 1 - checker2 = QgsCompositionChecker('composerhtml_smartbreaks2', self.mComposition) - checker2.setControlPathPrefix("composer_html") - myTestResult, myMessage = checker2.testComposition(myPage, 200) - assert myTestResult, myMessage - - self.mComposition.removeMultiFrame(composerHtml) - composerHtml = None - - assert myTestResult, myMessage - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposeritem.py b/tests/src/python/test_qgscomposeritem.py deleted file mode 100644 index 5de67ed7a3f..00000000000 --- a/tests/src/python/test_qgscomposeritem.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerItem. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2017 by Nyall Dawson' -__date__ = '17/01/2017' -__copyright__ = 'Copyright 2017, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' -import qgis # NOQA - -from qgis.testing import start_app, unittest -from qgis.core import (QgsProject, - QgsMapSettings, - QgsComposition, - QgsComposerLabel, - QgsComposerObject, - QgsProperty) -from qgis.PyQt.QtGui import QColor - - -start_app() - - -class TestQgsComposerItem(unittest.TestCase): - - def testDataDefinedFrameColor(self): - mapSettings = QgsMapSettings() # NOQA - - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - item = QgsComposerLabel(composition) - composition.addComposerLabel(item) - - item.setFrameStrokeColor(QColor(255, 0, 0)) - self.assertEqual(item.frameStrokeColor(), QColor(255, 0, 0)) - self.assertEqual(item.pen().color().name(), QColor(255, 0, 0).name()) - - item.dataDefinedProperties().setProperty(QgsComposerObject.FrameColor, QgsProperty.fromExpression("'blue'")) - item.refreshDataDefinedProperty() - self.assertEqual(item.frameStrokeColor(), QColor(255, 0, 0)) # should not change - self.assertEqual(item.pen().color().name(), QColor(0, 0, 255).name()) - - def testDataDefinedBackgroundColor(self): - mapSettings = QgsMapSettings() # NOQA - - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - item = QgsComposerLabel(composition) - composition.addComposerLabel(item) - - item.setBackgroundColor(QColor(255, 0, 0)) - self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) - self.assertEqual(item.brush().color().name(), QColor(255, 0, 0).name()) - - item.dataDefinedProperties().setProperty(QgsComposerObject.BackgroundColor, QgsProperty.fromExpression("'blue'")) - item.refreshDataDefinedProperty() - self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) # should not change - self.assertEqual(item.brush().color().name(), QColor(0, 0, 255).name()) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerlabel.py b/tests/src/python/test_qgscomposerlabel.py deleted file mode 100644 index de0ba237380..00000000000 --- a/tests/src/python/test_qgscomposerlabel.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -''' -test_qgscomposerlabel.py - -------------------------------------- - Date : Oct 2012 - Copyright : (C) 2012 by Dr. Hugo Mercier - email : hugo dot mercier at oslandia dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -''' -import qgis # NOQA - -from qgis.testing import start_app, unittest -from qgis.PyQt.QtCore import QFileInfo, QDate, QDateTime -from qgis.core import QgsVectorLayer, QgsMapSettings, QgsComposition, QgsComposerLabel, QgsProject -from utilities import unitTestDataPath - - -start_app() - - -class TestQgsComposerLabel(unittest.TestCase): - - def testCase(self): - TEST_DATA_DIR = unitTestDataPath() - vectorFileInfo = QFileInfo(TEST_DATA_DIR + "/france_parts.shp") - mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") - - QgsProject.instance().addMapLayers([mVectorLayer]) - - # create composition with composer map - mapSettings = QgsMapSettings() - mapSettings.setLayers([mVectorLayer]) - - mComposition = QgsComposition(QgsProject.instance()) - mComposition.setPaperSize(297, 210) - - mLabel = QgsComposerLabel(mComposition) - mComposition.addComposerLabel(mLabel) - - self.evaluation_test(mComposition, mLabel) - self.feature_evaluation_test(mComposition, mLabel, mVectorLayer) - self.page_evaluation_test(mComposition, mLabel, mVectorLayer) - - def evaluation_test(self, mComposition, mLabel): - # $CURRENT_DATE evaluation - mLabel.setText("__$CURRENT_DATE__") - assert mLabel.displayText() == ("__" + QDate.currentDate().toString() + "__") - - # $CURRENT_DATE() evaluation - mLabel.setText("__$CURRENT_DATE(dd)(ok)__") - expected = "__" + QDateTime.currentDateTime().toString("dd") + "(ok)__" - assert mLabel.displayText() == expected - - # $CURRENT_DATE() evaluation (inside an expression) - mLabel.setText("__[%$CURRENT_DATE(dd) + 1%](ok)__") - dd = QDate.currentDate().day() - expected = "__%d(ok)__" % (dd + 1) - assert mLabel.displayText() == expected - - # expression evaluation (without associated feature) - mLabel.setText("__[%\"NAME_1\"%][%21*2%]__") - assert mLabel.displayText() == "__[NAME_1]42__" - - def feature_evaluation_test(self, mComposition, mLabel, mVectorLayer): - atlas = mComposition.atlasComposition() - atlas.setCoverageLayer(mVectorLayer) - atlas.setEnabled(True) - mComposition.setAtlasMode(QgsComposition.ExportAtlas) - - mLabel.setText("[%\"NAME_1\"||'_ok'%]") - atlas.beginRender() - atlas.prepareForFeature(0) - assert mLabel.displayText() == "Basse-Normandie_ok" - - atlas.prepareForFeature(1) - assert mLabel.displayText() == "Bretagne_ok" - - def page_evaluation_test(self, mComposition, mLabel, mVectorLayer): - mComposition.setNumPages(2) - mLabel.setText("[%@layout_page||'/'||@layout_numpages%]") - assert mLabel.displayText() == "1/2" - - # move the the second page and re-evaluate - mLabel.setItemPosition(0, 320) - assert mLabel.displayText() == "2/2" - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerlegend.py b/tests/src/python/test_qgscomposerlegend.py deleted file mode 100644 index 8845a8c8ecc..00000000000 --- a/tests/src/python/test_qgscomposerlegend.py +++ /dev/null @@ -1,242 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerLegend. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2016 by Nyall Dawson' -__date__ = '13/07/2016' -__copyright__ = 'Copyright 2016, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -from qgis.PyQt.QtCore import QRectF -from qgis.PyQt.QtGui import QColor - -from qgis.core import (QgsComposerLegend, - QgsComposerMap, - QgsComposition, - QgsMapSettings, - QgsVectorLayer, - QgsMarkerSymbol, - QgsSingleSymbolRenderer, - QgsRectangle, - QgsProject, - QgsComposerObject, - QgsProperty - ) -from qgis.testing import (start_app, - unittest - ) -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker -import os - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerLegend(unittest.TestCase): - - def testInitialSizeSymbolMapUnits(self): - """Test initial size of legend with a symbol size in map units""" - - point_path = os.path.join(TEST_DATA_DIR, 'points.shp') - point_layer = QgsVectorLayer(point_path, 'points', 'ogr') - QgsProject.instance().addMapLayers([point_layer]) - - marker_symbol = QgsMarkerSymbol.createSimple({'color': '#ff0000', 'outline_style': 'no', 'size': '5', 'size_unit': 'MapUnit'}) - - point_layer.setRenderer(QgsSingleSymbolRenderer(marker_symbol)) - - s = QgsMapSettings() - s.setLayers([point_layer]) - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - composer_map = QgsComposerMap(composition, 20, 20, 80, 80) - composer_map.setFrameEnabled(True) - composer_map.setLayers([point_layer]) - composition.addComposerMap(composer_map) - composer_map.setNewExtent(point_layer.extent()) - - legend = QgsComposerLegend(composition) - legend.setSceneRect(QRectF(120, 20, 80, 80)) - legend.setFrameEnabled(True) - legend.setFrameStrokeWidth(2) - legend.setBackgroundColor(QColor(200, 200, 200)) - legend.setTitle('') - composition.addComposerLegend(legend) - legend.setComposerMap(composer_map) - - checker = QgsCompositionChecker( - 'composer_legend_mapunits', composition) - checker.setControlPathPrefix("composer_legend") - result, message = checker.testComposition() - self.assertTrue(result, message) - - QgsProject.instance().removeMapLayers([point_layer.id()]) - - def testResizeWithMapContent(self): - """Test test legend resizes to match map content""" - - point_path = os.path.join(TEST_DATA_DIR, 'points.shp') - point_layer = QgsVectorLayer(point_path, 'points', 'ogr') - QgsProject.instance().addMapLayers([point_layer]) - - s = QgsMapSettings() - s.setLayers([point_layer]) - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - composer_map = QgsComposerMap(composition, 20, 20, 80, 80) - composer_map.setFrameEnabled(True) - composer_map.setLayers([point_layer]) - composition.addComposerMap(composer_map) - composer_map.setNewExtent(point_layer.extent()) - - legend = QgsComposerLegend(composition) - legend.setSceneRect(QRectF(120, 20, 80, 80)) - legend.setFrameEnabled(True) - legend.setFrameStrokeWidth(2) - legend.setBackgroundColor(QColor(200, 200, 200)) - legend.setTitle('') - legend.setLegendFilterByMapEnabled(True) - composition.addComposerLegend(legend) - legend.setComposerMap(composer_map) - - composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) - - checker = QgsCompositionChecker( - 'composer_legend_size_content', composition) - checker.setControlPathPrefix("composer_legend") - result, message = checker.testComposition() - self.assertTrue(result, message) - - QgsProject.instance().removeMapLayers([point_layer.id()]) - - def testResizeDisabled(self): - """Test that test legend does not resize if auto size is disabled""" - - point_path = os.path.join(TEST_DATA_DIR, 'points.shp') - point_layer = QgsVectorLayer(point_path, 'points', 'ogr') - QgsProject.instance().addMapLayers([point_layer]) - - s = QgsMapSettings() - s.setLayers([point_layer]) - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - composer_map = QgsComposerMap(composition, 20, 20, 80, 80) - composer_map.setFrameEnabled(True) - composer_map.setLayers([point_layer]) - composition.addComposerMap(composer_map) - composer_map.setNewExtent(point_layer.extent()) - - legend = QgsComposerLegend(composition) - legend.setSceneRect(QRectF(120, 20, 80, 80)) - legend.setFrameEnabled(True) - legend.setFrameStrokeWidth(2) - legend.setBackgroundColor(QColor(200, 200, 200)) - legend.setTitle('') - legend.setLegendFilterByMapEnabled(True) - - # disable auto resizing - legend.setResizeToContents(False) - - composition.addComposerLegend(legend) - legend.setComposerMap(composer_map) - - composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) - - checker = QgsCompositionChecker( - 'composer_legend_noresize', composition) - checker.setControlPathPrefix("composer_legend") - result, message = checker.testComposition() - self.assertTrue(result, message) - - QgsProject.instance().removeMapLayers([point_layer.id()]) - - def testResizeDisabledCrop(self): - """Test that if legend resizing is disabled, and legend is too small, then content is cropped""" - - point_path = os.path.join(TEST_DATA_DIR, 'points.shp') - point_layer = QgsVectorLayer(point_path, 'points', 'ogr') - QgsProject.instance().addMapLayers([point_layer]) - - s = QgsMapSettings() - s.setLayers([point_layer]) - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - composer_map = QgsComposerMap(composition, 20, 20, 80, 80) - composer_map.setFrameEnabled(True) - composer_map.setLayers([point_layer]) - composition.addComposerMap(composer_map) - composer_map.setNewExtent(point_layer.extent()) - - legend = QgsComposerLegend(composition) - legend.setSceneRect(QRectF(120, 20, 20, 20)) - legend.setFrameEnabled(True) - legend.setFrameStrokeWidth(2) - legend.setBackgroundColor(QColor(200, 200, 200)) - legend.setTitle('') - legend.setLegendFilterByMapEnabled(True) - - # disable auto resizing - legend.setResizeToContents(False) - - composition.addComposerLegend(legend) - legend.setComposerMap(composer_map) - - composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) - - checker = QgsCompositionChecker( - 'composer_legend_noresize_crop', composition) - checker.setControlPathPrefix("composer_legend") - result, message = checker.testComposition() - self.assertTrue(result, message) - - QgsProject.instance().removeMapLayers([point_layer.id()]) - - def testDataDefinedTitle(self): - mapSettings = QgsMapSettings() # NOQA - - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - legend = QgsComposerLegend(composition) - composition.addComposerLegend(legend) - - legend.setTitle('original') - self.assertEqual(legend.title(), 'original') - self.assertEqual(legend.legendSettings().title(), 'original') - - legend.dataDefinedProperties().setProperty(QgsComposerObject.LegendTitle, QgsProperty.fromExpression("'new'")) - legend.refreshDataDefinedProperty() - self.assertEqual(legend.title(), 'original') - self.assertEqual(legend.legendSettings().title(), 'new') - - def testDataDefinedColumnCount(self): - mapSettings = QgsMapSettings() # NOQA - - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - legend = QgsComposerLegend(composition) - composition.addComposerLegend(legend) - - legend.setColumnCount(2) - self.assertEqual(legend.columnCount(), 2) - self.assertEqual(legend.legendSettings().columnCount(), 2) - - legend.dataDefinedProperties().setProperty(QgsComposerObject.LegendColumnCount, QgsProperty.fromExpression("5")) - legend.refreshDataDefinedProperty() - self.assertEqual(legend.columnCount(), 2) - self.assertEqual(legend.legendSettings().columnCount(), 5) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposermap.py b/tests/src/python/test_qgscomposermap.py deleted file mode 100644 index 9df1a4e5e55..00000000000 --- a/tests/src/python/test_qgscomposermap.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerMap. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler' -__date__ = '20/08/2012' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -import os - -from qgis.PyQt.QtCore import QFileInfo -from qgis.PyQt.QtXml import QDomDocument -from qgis.PyQt.QtGui import QPainter - -from qgis.core import (QgsComposerMap, - QgsRectangle, - QgsRasterLayer, - QgsVectorLayer, - QgsComposition, - QgsMapSettings, - QgsProject, - QgsMultiBandColorRenderer, - QgsCoordinateReferenceSystem - ) - -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerMap(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - myPath = os.path.join(TEST_DATA_DIR, 'rgb256x256.png') - rasterFileInfo = QFileInfo(myPath) - self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), - rasterFileInfo.completeBaseName()) - rasterRenderer = QgsMultiBandColorRenderer( - self.raster_layer.dataProvider(), 1, 2, 3) - self.raster_layer.setRenderer(rasterRenderer) - - myPath = os.path.join(TEST_DATA_DIR, 'points.shp') - vector_file_info = QFileInfo(myPath) - self.vector_layer = QgsVectorLayer(vector_file_info.filePath(), - vector_file_info.completeBaseName(), 'ogr') - assert self.vector_layer.isValid() - - # pipe = mRasterLayer.pipe() - # assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' - QgsProject.instance().addMapLayers([self.raster_layer, self.vector_layer]) - - # create composition with composer map - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) - self.mComposerMap.setFrameEnabled(True) - self.mComposerMap.setLayers([self.raster_layer]) - self.mComposition.addComposerMap(self.mComposerMap) - - def testOverviewMap(self): - overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) - overviewMap.setFrameEnabled(True) - overviewMap.setLayers([self.raster_layer]) - self.mComposition.addComposerMap(overviewMap) - # zoom in - myRectangle = QgsRectangle(96, -152, 160, -120) - self.mComposerMap.setNewExtent(myRectangle) - myRectangle2 = QgsRectangle(0, -256, 256, 0) - overviewMap.setNewExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.mComposerMap.id()) - checker = QgsCompositionChecker('composermap_overview', self.mComposition) - checker.setControlPathPrefix("composer_mapoverview") - myTestResult, myMessage = checker.testComposition() - self.mComposition.removeComposerItem(overviewMap) - assert myTestResult, myMessage - - def testOverviewMapBlend(self): - overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) - overviewMap.setFrameEnabled(True) - overviewMap.setLayers([self.raster_layer]) - self.mComposition.addComposerMap(overviewMap) - # zoom in - myRectangle = QgsRectangle(96, -152, 160, -120) - self.mComposerMap.setNewExtent(myRectangle) - myRectangle2 = QgsRectangle(0, -256, 256, 0) - overviewMap.setNewExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.mComposerMap.id()) - overviewMap.overview().setBlendMode(QPainter.CompositionMode_Multiply) - checker = QgsCompositionChecker('composermap_overview_blending', self.mComposition) - checker.setControlPathPrefix("composer_mapoverview") - myTestResult, myMessage = checker.testComposition() - self.mComposition.removeComposerItem(overviewMap) - assert myTestResult, myMessage - - def testOverviewMapInvert(self): - overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) - overviewMap.setFrameEnabled(True) - overviewMap.setLayers([self.raster_layer]) - self.mComposition.addComposerMap(overviewMap) - # zoom in - myRectangle = QgsRectangle(96, -152, 160, -120) - self.mComposerMap.setNewExtent(myRectangle) - myRectangle2 = QgsRectangle(0, -256, 256, 0) - overviewMap.setNewExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.mComposerMap.id()) - overviewMap.overview().setInverted(True) - checker = QgsCompositionChecker('composermap_overview_invert', self.mComposition) - checker.setControlPathPrefix("composer_mapoverview") - myTestResult, myMessage = checker.testComposition() - self.mComposition.removeComposerItem(overviewMap) - assert myTestResult, myMessage - - def testOverviewMapCenter(self): - overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) - overviewMap.setFrameEnabled(True) - overviewMap.setLayers([self.raster_layer]) - self.mComposition.addComposerMap(overviewMap) - # zoom in - myRectangle = QgsRectangle(192, -288, 320, -224) - self.mComposerMap.setNewExtent(myRectangle) - myRectangle2 = QgsRectangle(0, -256, 256, 0) - overviewMap.setNewExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.mComposerMap.id()) - overviewMap.overview().setInverted(False) - overviewMap.overview().setCentered(True) - checker = QgsCompositionChecker('composermap_overview_center', self.mComposition) - checker.setControlPathPrefix("composer_mapoverview") - myTestResult, myMessage = checker.testComposition() - self.mComposition.removeComposerItem(overviewMap) - assert myTestResult, myMessage - - def testMapCrs(self): - # create composition with composer map - map_settings = QgsMapSettings() - map_settings.setLayers([self.vector_layer]) - composition = QgsComposition(QgsProject.instance()) - composition.setPaperSize(297, 210) - - # check that new maps inherit project CRS - QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) - map = QgsComposerMap(composition, 20, 20, 200, 100) - map.setFrameEnabled(True) - rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909) - map.setNewExtent(rectangle) - map.setLayers([self.vector_layer]) - composition.addComposerMap(map) - - self.assertEqual(map.crs().authid(), 'EPSG:4326') - self.assertFalse(map.presetCrs().isValid()) - - # overwrite CRS - map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) - self.assertEqual(map.crs().authid(), 'EPSG:3857') - self.assertEqual(map.presetCrs().authid(), 'EPSG:3857') - checker = QgsCompositionChecker('composermap_crs3857', composition) - checker.setControlPathPrefix("composer_map") - result, message = checker.testComposition() - self.assertTrue(result, message) - - # overwrite CRS - map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) - self.assertEqual(map.presetCrs().authid(), 'EPSG:4326') - self.assertEqual(map.crs().authid(), 'EPSG:4326') - rectangle = QgsRectangle(-124, 17, -78, 52) - map.zoomToExtent(rectangle) - checker = QgsCompositionChecker('composermap_crs4326', composition) - checker.setControlPathPrefix("composer_map") - result, message = checker.testComposition() - self.assertTrue(result, message) - - # change back to project CRS - map.setCrs(QgsCoordinateReferenceSystem()) - self.assertEqual(map.crs().authid(), 'EPSG:4326') - self.assertFalse(map.presetCrs().isValid()) - - def testuniqueId(self): - doc = QDomDocument() - documentElement = doc.createElement('ComposerItemClipboard') - self.mComposition.writeXml(documentElement, doc) - self.mComposition.addItemsFromXml(documentElement, doc) - - # test if both composer maps have different ids - newMap = QgsComposerMap(self.mComposition, 0, 0, 10, 10) - mapList = self.mComposition.composerMapItems() - - for mapIt in mapList: - if mapIt != self.mComposerMap: - newMap = mapIt - break - - oldId = self.mComposerMap.id() - newId = newMap.id() - - self.mComposition.removeComposerItem(newMap) - myMessage = 'old: %s new: %s' % (oldId, newId) - assert oldId != newId, myMessage - - def testWorldFileGeneration(self): - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.setMapRotation(30.0) - - self.mComposition.setGenerateWorldFile(True) - self.mComposition.setReferenceMap(self.mComposerMap) - - p = self.mComposition.computeWorldFileParameters() - pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, - 2.4136013686911886, -4.179969388427311, 3342408.5663611) - ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) - for i in range(0, 6): - assert abs(p[i] - pexpected[i]) < ptolerance[i] - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposermapgrid.py b/tests/src/python/test_qgscomposermapgrid.py deleted file mode 100644 index 8a5c305a41e..00000000000 --- a/tests/src/python/test_qgscomposermapgrid.py +++ /dev/null @@ -1,248 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerMap. - -.. note. This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler' -__date__ = '20/08/2012' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.PyQt.QtGui import QPainter, QColor - -from qgis.core import (QgsComposerMap, - QgsComposerMapGrid, - QgsRectangle, - QgsComposition, - QgsMapSettings, - QgsCoordinateReferenceSystem, - QgsFontUtils, - QgsProject - ) -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerMap(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - # create composition with composer map - self.mMapSettings = QgsMapSettings() - crs = QgsCoordinateReferenceSystem(32633) - self.mMapSettings.setDestinationCrs(crs) - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) - self.mComposerMap.setFrameEnabled(True) - self.mComposerMap.setBackgroundColor(QColor(150, 100, 100)) - self.mComposition.addComposerMap(self.mComposerMap) - - def testGrid(self): - """Test that we can create a grid for a map.""" - myRectangle = QgsRectangle(781662.375, 3339523.125, - 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setAnnotationEnabled(True) - self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) - self.mComposerMap.grid().setGridLineWidth(0.5) - self.mComposerMap.grid().setAnnotationFont(QgsFontUtils.getStandardTestFont()) - self.mComposerMap.grid().setAnnotationPrecision(0) - self.mComposerMap.grid().setAnnotationDisplay(QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Left) - self.mComposerMap.grid().setAnnotationPosition(QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Right) - self.mComposerMap.grid().setAnnotationDisplay(QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Top) - self.mComposerMap.grid().setAnnotationPosition(QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Bottom) - self.mComposerMap.grid().setAnnotationDirection(QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Right) - self.mComposerMap.grid().setAnnotationDirection(QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Bottom) - self.mComposerMap.grid().setAnnotationFontColor(QColor(255, 0, 0, 150)) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_Overlay) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_grid', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition() - self.mComposerMap.grid().setEnabled(False) - self.mComposerMap.grid().setAnnotationEnabled(False) - - assert myTestResult, myMessage - - def testCrossGrid(self): - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Cross) - self.mComposerMap.grid().setCrossLength(2.0) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setAnnotationEnabled(False) - self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) - self.mComposerMap.grid().setGridLineWidth(0.5) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_crossgrid', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition() - - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) - self.mComposerMap.grid().setEnabled(False) - self.mComposerMap.grid().setAnnotationEnabled(False) - - assert myTestResult, myMessage - - def testMarkerGrid(self): - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Markers) - self.mComposerMap.grid().setCrossLength(2.0) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setAnnotationEnabled(False) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_markergrid', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition() - - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) - self.mComposerMap.grid().setEnabled(False) - self.mComposerMap.grid().setAnnotationEnabled(False) - - assert myTestResult, myMessage - - def testFrameOnly(self): - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.FrameAnnotationsOnly) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setAnnotationEnabled(False) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - self.mComposerMap.grid().setFramePenSize(0.5) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_gridframeonly', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition() - - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) - self.mComposerMap.grid().setEnabled(False) - self.mComposerMap.grid().setAnnotationEnabled(False) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) - - assert myTestResult, myMessage - - def testZebraStyle(self): - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - myRectangle = QgsRectangle(785462.375, 3341423.125, - 789262.375, 3343323.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - self.mComposerMap.grid().setFrameWidth(10) - self.mComposerMap.grid().setFramePenSize(1) - self.mComposerMap.grid().setGridLineWidth(0.5) - self.mComposerMap.grid().setFramePenColor(QColor(255, 100, 0, 200)) - self.mComposerMap.grid().setFrameFillColor1(QColor(50, 90, 50, 100)) - self.mComposerMap.grid().setFrameFillColor2(QColor(200, 220, 100, 60)) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_zebrastyle', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition(0, 100) - assert myTestResult, myMessage - - def testZebraStyleSides(self): - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - self.mComposerMap.grid().setFrameWidth(10) - self.mComposerMap.grid().setFramePenSize(1) - self.mComposerMap.grid().setGridLineWidth(0.5) - self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setFrameFillColor1(QColor(0, 0, 0)) - self.mComposerMap.grid().setFrameFillColor2(QColor(255, 255, 255)) - self.mComposerMap.grid().setEnabled(True) - - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameLeft, True) - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameRight, False) - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, False) - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameBottom, False) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_zebrastyle_left', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition(0, 100) - assert myTestResult, myMessage - - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, True) - self.mComposerMap.updateBoundingRect() - checker = QgsCompositionChecker('composermap_zebrastyle_lefttop', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition(0, 100) - assert myTestResult, myMessage - - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameRight, True) - self.mComposerMap.updateBoundingRect() - checker = QgsCompositionChecker('composermap_zebrastyle_lefttopright', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition(0, 100) - assert myTestResult, myMessage - - self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameBottom, True) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) - - def testInteriorTicks(self): - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.mComposerMap.setNewExtent(myRectangle) - self.mComposerMap.grid().setIntervalX(2000) - self.mComposerMap.grid().setIntervalY(2000) - self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) - self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.InteriorTicks) - self.mComposerMap.grid().setFrameWidth(10) - self.mComposerMap.grid().setFramePenSize(1) - self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) - self.mComposerMap.grid().setEnabled(True) - self.mComposerMap.grid().setStyle(QgsComposerMapGrid.FrameAnnotationsOnly) - self.mComposerMap.updateBoundingRect() - - checker = QgsCompositionChecker('composermap_interiorticks', self.mComposition) - checker.setControlPathPrefix("composer_mapgrid") - myTestResult, myMessage = checker.testComposition(0, 100) - assert myTestResult, myMessage - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerpicture.py b/tests/src/python/test_qgscomposerpicture.py deleted file mode 100644 index 95d41c5af9c..00000000000 --- a/tests/src/python/test_qgscomposerpicture.py +++ /dev/null @@ -1,148 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerPicture. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2015 by Nyall Dawson' -__date__ = '02/07/2015' -__copyright__ = 'Copyright 2015, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -import os -import socketserver -import threading -import http.server -from qgis.PyQt.QtCore import QRectF - -from qgis.core import (QgsComposerPicture, - QgsComposition, - QgsComposerMap, - QgsRectangle, - QgsCoordinateReferenceSystem, - QgsProject - ) -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerPicture(unittest.TestCase): - - @classmethod - def setUpClass(cls): - # Bring up a simple HTTP server, for remote picture tests - os.chdir(unitTestDataPath() + '') - handler = http.server.SimpleHTTPRequestHandler - - cls.httpd = socketserver.TCPServer(('localhost', 0), handler) - cls.port = cls.httpd.server_address[1] - - cls.httpd_thread = threading.Thread(target=cls.httpd.serve_forever) - cls.httpd_thread.setDaemon(True) - cls.httpd_thread.start() - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - TEST_DATA_DIR = unitTestDataPath() - self.pngImage = TEST_DATA_DIR + "/sample_image.png" - - # create composition - self.composition = QgsComposition(QgsProject.instance()) - self.composition.setPaperSize(297, 210) - - self.composerPicture = QgsComposerPicture(self.composition) - self.composerPicture.setPicturePath(self.pngImage) - self.composerPicture.setSceneRect(QRectF(70, 70, 100, 100)) - self.composerPicture.setFrameEnabled(True) - self.composition.addComposerPicture(self.composerPicture) - - def testResizeZoom(self): - """Test picture resize zoom mode.""" - self.composerPicture.setResizeMode(QgsComposerPicture.Zoom) - - checker = QgsCompositionChecker('composerpicture_resize_zoom', self.composition) - checker.setControlPathPrefix("composer_picture") - testResult, message = checker.testComposition() - - assert testResult, message - - @unittest.skip('test is broken for qt5/python3 - feature works') - def testRemoteImage(self): - """Test fetching remote picture.""" - self.composerPicture.setPicturePath('http://localhost:' + str(TestQgsComposerPicture.port) + '/qgis_local_server/logo.png') - - checker = QgsCompositionChecker('composerpicture_remote', self.composition) - checker.setControlPathPrefix("composer_picture") - testResult, message = checker.testComposition() - - self.composerPicture.setPicturePath(self.pngImage) - assert testResult, message - - def testGridNorth(self): - """Test syncing picture to grid north""" - - composition = QgsComposition(QgsProject.instance()) - - composerMap = QgsComposerMap(composition) - composerMap.setNewExtent(QgsRectangle(0, -256, 256, 0)) - composition.addComposerMap(composerMap) - - composerPicture = QgsComposerPicture(composition) - composition.addComposerPicture(composerPicture) - - composerPicture.setRotationMap(composerMap.id()) - self.assertTrue(composerPicture.rotationMap() >= 0) - - composerPicture.setNorthMode(QgsComposerPicture.GridNorth) - composerMap.setMapRotation(45) - self.assertEqual(composerPicture.pictureRotation(), 45) - - # add an offset - composerPicture.setNorthOffset(-10) - self.assertEqual(composerPicture.pictureRotation(), 35) - - def testTrueNorth(self): - """Test syncing picture to true north""" - - composition = QgsComposition(QgsProject.instance()) - - composerMap = QgsComposerMap(composition) - composerMap.setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575)) - composerMap.setNewExtent(QgsRectangle(-2126029.962, -2200807.749, -119078.102, -757031.156)) - composition.addComposerMap(composerMap) - - composerPicture = QgsComposerPicture(composition) - composition.addComposerPicture(composerPicture) - - composerPicture.setRotationMap(composerMap.id()) - self.assertTrue(composerPicture.rotationMap() >= 0) - - composerPicture.setNorthMode(QgsComposerPicture.TrueNorth) - self.assertAlmostEqual(composerPicture.pictureRotation(), 37.20, 1) - - # shift map - composerMap.setNewExtent(QgsRectangle(2120672.293, -3056394.691, 2481640.226, -2796718.780)) - self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18, 1) - - # rotate map - composerMap.setMapRotation(45) - self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 45, 1) - - # add an offset - composerPicture.setNorthOffset(-10) - self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 35, 1) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerpolygon.py b/tests/src/python/test_qgscomposerpolygon.py deleted file mode 100644 index ab7c7e239e1..00000000000 --- a/tests/src/python/test_qgscomposerpolygon.py +++ /dev/null @@ -1,223 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerPolygon. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2016 by Paul Blottiere' -__date__ = '14/03/2016' -__copyright__ = 'Copyright 2016, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.PyQt.QtGui import QPolygonF -from qgis.PyQt.QtCore import QPointF - -from qgis.core import (QgsComposerPolygon, - QgsComposerItem, - QgsComposition, - QgsFillSymbol, - QgsProject - ) -from qgis.testing import (start_app, - unittest - ) -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerPolygon(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - # create composition - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - - # create - polygon = QPolygonF() - polygon.append(QPointF(0.0, 0.0)) - polygon.append(QPointF(100.0, 0.0)) - polygon.append(QPointF(200.0, 100.0)) - polygon.append(QPointF(100.0, 200.0)) - - self.mComposerPolygon = QgsComposerPolygon(polygon, self.mComposition) - self.mComposition.addComposerPolygon(self.mComposerPolygon) - - # style - props = {} - props["color"] = "green" - props["style"] = "solid" - props["style_border"] = "solid" - props["color_border"] = "black" - props["width_border"] = "10.0" - props["joinstyle"] = "miter" - - style = QgsFillSymbol.createSimple(props) - self.mComposerPolygon.setPolygonStyleSymbol(style) - - def testDisplayName(self): - """Test if displayName is valid""" - - self.assertEqual(self.mComposerPolygon.displayName(), "") - - def testType(self): - """Test if type is valid""" - - self.assertEqual( - self.mComposerPolygon.type(), QgsComposerItem.ComposerPolygon) - - def testDefaultStyle(self): - """Test polygon rendering with default style.""" - - self.mComposerPolygon.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolygon_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testDisplayNodes(self): - """Test displayNodes method""" - - self.mComposerPolygon.setDisplayNodes(True) - checker = QgsCompositionChecker( - 'composerpolygon_displaynodes', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.mComposerPolygon.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolygon_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testSelectedNode(self): - """Test selectedNode and deselectNode methods""" - - self.mComposerPolygon.setDisplayNodes(True) - - self.mComposerPolygon.setSelectedNode(3) - checker = QgsCompositionChecker( - 'composerpolygon_selectednode', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.mComposerPolygon.deselectNode() - self.mComposerPolygon.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolygon_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testRemoveNode(self): - """Test removeNode method""" - - rc = self.mComposerPolygon.removeNode(100) - self.assertEqual(rc, False) - - checker = QgsCompositionChecker( - 'composerpolygon_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - - def testAddNode(self): - """Test addNode method""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - rc = self.mComposerPolygon.addNode(QPointF(50.0, 10.0)) - self.assertEqual(rc, False) - - # default searching radius is 10 - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - rc = self.mComposerPolygon.addNode(QPointF(50.0, 9.99)) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolygon.nodesSize(), 5) - - def testAddNodeCustomRadius(self): - """Test addNode with custom radius""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - rc = self.mComposerPolygon.addNode(QPointF(50.0, 8.1), True, 8.0) - self.assertEqual(rc, False) - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - - # default searching radius is 10 - rc = self.mComposerPolygon.addNode(QPointF(50.0, 7.9), True, 8.0) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolygon.nodesSize(), 5) - - def testAddNodeWithoutCheckingArea(self): - """Test addNode without checking the maximum distance allowed""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - rc = self.mComposerPolygon.addNode(QPointF(50.0, 20.0)) - self.assertEqual(rc, False) - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - - # default searching radius is 10 - self.assertEqual(self.mComposerPolygon.nodesSize(), 4) - rc = self.mComposerPolygon.addNode(QPointF(50.0, 20.0), False) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolygon.nodesSize(), 5) - - checker = QgsCompositionChecker( - 'composerpolygon_addnode', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testMoveNode(self): - """Test moveNode method""" - - rc = self.mComposerPolygon.moveNode(30, QPointF(100.0, 300.0)) - self.assertEqual(rc, False) - - rc = self.mComposerPolygon.moveNode(3, QPointF(100.0, 150.0)) - self.assertEqual(rc, True) - - checker = QgsCompositionChecker( - 'composerpolygon_movenode', self.mComposition) - checker.setControlPathPrefix("composer_polygon") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testNodeAtPosition(self): - """Test nodeAtPosition method""" - - # default searching radius is 10 - rc = self.mComposerPolygon.nodeAtPosition(QPointF(100.0, 210.0)) - self.assertEqual(rc, -1) - - # default searching radius is 10 - rc = self.mComposerPolygon.nodeAtPosition( - QPointF(100.0, 210.0), False) - self.assertEqual(rc, 3) - - # default searching radius is 10 - rc = self.mComposerPolygon.nodeAtPosition( - QPointF(100.0, 210.0), True, 10.1) - self.assertEqual(rc, 3) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerpolyline.py b/tests/src/python/test_qgscomposerpolyline.py deleted file mode 100644 index 58b07b17631..00000000000 --- a/tests/src/python/test_qgscomposerpolyline.py +++ /dev/null @@ -1,230 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerPolyline. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2016 by Paul Blottiere' -__date__ = '14/03/2016' -__copyright__ = 'Copyright 2016, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.PyQt.QtGui import QPolygonF -from qgis.PyQt.QtCore import QPointF - -from qgis.core import (QgsComposerPolyline, - QgsComposerItem, - QgsComposition, - QgsLineSymbol, - QgsProject - ) -from qgis.testing import (start_app, - unittest - ) -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerPolyline(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - # create composition - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - - # create - polygon = QPolygonF() - polygon.append(QPointF(0.0, 0.0)) - polygon.append(QPointF(100.0, 0.0)) - polygon.append(QPointF(200.0, 100.0)) - polygon.append(QPointF(100.0, 200.0)) - - self.mComposerPolyline = QgsComposerPolyline( - polygon, self.mComposition) - self.mComposition.addComposerPolyline(self.mComposerPolyline) - - # style - props = {} - props["color"] = "0,0,0,255" - props["width"] = "10.0" - props["capstyle"] = "square" - - style = QgsLineSymbol.createSimple(props) - self.mComposerPolyline.setPolylineStyleSymbol(style) - - def testDisplayName(self): - """Test if displayName is valid""" - - self.assertEqual(self.mComposerPolyline.displayName(), "") - - def testType(self): - """Test if type is valid""" - - self.assertEqual( - self.mComposerPolyline.type(), QgsComposerItem.ComposerPolyline) - - def testDefaultStyle(self): - """Test polygon rendering with default style.""" - - self.mComposerPolyline.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolyline_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testDisplayNodes(self): - """Test displayNodes method""" - - self.mComposerPolyline.setDisplayNodes(True) - checker = QgsCompositionChecker( - 'composerpolyline_displaynodes', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.mComposerPolyline.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolyline_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testSelectedNode(self): - """Test selectedNode and deselectNode methods""" - - self.mComposerPolyline.setDisplayNodes(True) - - self.mComposerPolyline.setSelectedNode(3) - checker = QgsCompositionChecker( - 'composerpolyline_selectednode', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.mComposerPolyline.deselectNode() - self.mComposerPolyline.setDisplayNodes(False) - checker = QgsCompositionChecker( - 'composerpolyline_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testRemoveNode(self): - """Test removeNode method""" - - rc = self.mComposerPolyline.removeNode(100) - self.assertEqual(rc, False) - - checker = QgsCompositionChecker( - 'composerpolyline_defaultstyle', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.removeNode(3) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolyline.nodesSize(), 3) - - checker = QgsCompositionChecker( - 'composerpolyline_removednode', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testAddNode(self): - """Test addNode method""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.addNode(QPointF(50.0, 10.0)) - self.assertEqual(rc, False) - - # default searching radius is 10 - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.addNode(QPointF(50.0, 9.99)) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolyline.nodesSize(), 5) - - def testAddNodeCustomRadius(self): - """Test addNode with custom radius""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.addNode(QPointF(50.0, 8.1), True, 8.0) - self.assertEqual(rc, False) - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - - # default searching radius is 10 - rc = self.mComposerPolyline.addNode(QPointF(50.0, 7.9), True, 8.0) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolyline.nodesSize(), 5) - - def testAddNodeWithoutCheckingArea(self): - """Test addNode without checking the maximum distance allowed""" - - # default searching radius is 10 - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.addNode(QPointF(50.0, 20.0)) - self.assertEqual(rc, False) - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - - # default searching radius is 10 - self.assertEqual(self.mComposerPolyline.nodesSize(), 4) - rc = self.mComposerPolyline.addNode(QPointF(50.0, 20.0), False) - self.assertEqual(rc, True) - self.assertEqual(self.mComposerPolyline.nodesSize(), 5) - - checker = QgsCompositionChecker( - 'composerpolyline_addnode', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testMoveNode(self): - """Test moveNode method""" - - rc = self.mComposerPolyline.moveNode(30, QPointF(100.0, 300.0)) - self.assertEqual(rc, False) - - rc = self.mComposerPolyline.moveNode(3, QPointF(100.0, 150.0)) - self.assertEqual(rc, True) - - checker = QgsCompositionChecker( - 'composerpolyline_movenode', self.mComposition) - checker.setControlPathPrefix("composer_polyline") - myTestResult, myMessage = checker.testComposition() - assert myTestResult, myMessage - - def testNodeAtPosition(self): - """Test nodeAtPosition method""" - - # default searching radius is 10 - rc = self.mComposerPolyline.nodeAtPosition(QPointF(100.0, 210.0)) - self.assertEqual(rc, -1) - - # default searching radius is 10 - rc = self.mComposerPolyline.nodeAtPosition( - QPointF(100.0, 210.0), False) - self.assertEqual(rc, 3) - - # default searching radius is 10 - rc = self.mComposerPolyline.nodeAtPosition( - QPointF(100.0, 210.0), True, 10.1) - self.assertEqual(rc, 3) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposershapes.py b/tests/src/python/test_qgscomposershapes.py deleted file mode 100644 index 259e0a077ad..00000000000 --- a/tests/src/python/test_qgscomposershapes.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerShape. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2012 by Dr. Horst Düster / Dr. Marco Hugentobler' -__date__ = '20/08/2012' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.PyQt.QtGui import QColor - -from qgis.core import (QgsComposerShape, - QgsComposition, - QgsProject) -from qgis.testing import start_app, unittest -from utilities import unitTestDataPath -from qgscompositionchecker import QgsCompositionChecker - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposerShapes(unittest.TestCase): - - def __init__(self, methodName): - """Run once on class initialization.""" - unittest.TestCase.__init__(self, methodName) - - # create composition - self.mComposition = QgsComposition(QgsProject.instance()) - self.mComposition.setPaperSize(297, 210) - - self.mComposerShape = QgsComposerShape(20, 20, 150, 100, self.mComposition) - self.mComposerShape.setBackgroundColor(QColor.fromRgb(255, 150, 0)) - self.mComposition.addComposerShape(self.mComposerShape) - - def testRectangle(self): - """Test rectangle composer shape""" - - self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) - - checker = QgsCompositionChecker('composershapes_rectangle', self.mComposition) - checker.setControlPathPrefix("composer_shapes") - myTestResult, myMessage = checker.testComposition() - - assert myTestResult, myMessage - - def testEllipse(self): - """Test ellipse composer shape""" - - self.mComposerShape.setShapeType(QgsComposerShape.Ellipse) - - checker = QgsCompositionChecker('composershapes_ellipse', self.mComposition) - checker.setControlPathPrefix("composer_shapes") - myTestResult, myMessage = checker.testComposition() - - assert myTestResult, myMessage - - def testTriangle(self): - """Test triangle composer shape""" - - self.mComposerShape.setShapeType(QgsComposerShape.Triangle) - - checker = QgsCompositionChecker('composershapes_triangle', self.mComposition) - checker.setControlPathPrefix("composer_shapes") - myTestResult, myMessage = checker.testComposition() - - assert myTestResult, myMessage - - def testRoundedRectangle(self): - """Test rounded rectangle composer shape""" - - self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) - self.mComposerShape.setCornerRadius(30) - - checker = QgsCompositionChecker('composershapes_roundedrect', self.mComposition) - checker.setControlPathPrefix("composer_shapes") - myTestResult, myMessage = checker.testComposition() - - self.mComposerShape.setCornerRadius(0) - assert myTestResult, myMessage - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposerview.py b/tests/src/python/test_qgscomposerview.py deleted file mode 100644 index 1f158435a7e..00000000000 --- a/tests/src/python/test_qgscomposerview.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposerView. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = 'Nyall Dawson' -__date__ = '29/05/2016' -__copyright__ = 'Copyright 2016, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -from qgis.gui import QgsComposerView -from qgis.PyQt.QtCore import QRectF -from qgis.PyQt.QtGui import QTransform - -from qgis.testing import start_app, unittest - -start_app() - - -class TestQgsComposerView(unittest.TestCase): - - def testScaleSafe(self): - """ test scaleSafe method """ - - view = QgsComposerView() - view.fitInView(QRectF(0, 0, 10, 10)) - scale = view.transform().m11() - view.scaleSafe(2) - self.assertAlmostEqual(view.transform().m11(), 2) - view.scaleSafe(4) - self.assertAlmostEqual(view.transform().m11(), 8) - - # try to zoom in heaps - view.scaleSafe(99999999) - # assume we have hit the limit - scale = view.transform().m11() - view.scaleSafe(2) - self.assertAlmostEqual(view.transform().m11(), scale) - - view.setTransform(QTransform.fromScale(1, 1)) - self.assertAlmostEqual(view.transform().m11(), 1) - # test zooming out - view.scaleSafe(0.5) - self.assertAlmostEqual(view.transform().m11(), 0.5) - view.scaleSafe(0.1) - self.assertAlmostEqual(view.transform().m11(), 0.05) - - # try zooming out heaps - view.scaleSafe(0.000000001) - # assume we have hit the limit - scale = view.transform().m11() - view.scaleSafe(0.5) - self.assertAlmostEqual(view.transform().m11(), scale) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscomposition.py b/tests/src/python/test_qgscomposition.py deleted file mode 100644 index e40b4b4b33e..00000000000 --- a/tests/src/python/test_qgscomposition.py +++ /dev/null @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsComposition. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" -__author__ = '(C) 2012 by Tim Sutton' -__date__ = '20/08/2012' -__copyright__ = 'Copyright 2012, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -import os - -from qgis.PyQt.QtCore import QFileInfo, QDir -from qgis.PyQt.QtXml import QDomDocument - -from qgis.core import (QgsComposition, - QgsPointXY, - QgsRasterLayer, - QgsMultiBandColorRenderer, - QgsProject, - QgsCoordinateFormatter) - -from qgis.testing import start_app, unittest -from qgis.testing.mocked import get_iface -from utilities import unitTestDataPath -from qgis.PyQt.QtXml import QDomDocument - -start_app() -TEST_DATA_DIR = unitTestDataPath() - - -class TestQgsComposition(unittest.TestCase): - - def setUp(self): - """Run before each test.""" - self.iface = get_iface() - - def tearDown(self): - """Run after each test.""" - pass - - def testSubstitutionMap(self): - """Test that we can use degree symbols in substitutions. - """ - # Create a point and convert it to text containing a degree symbol. - myPoint = QgsPointXY(12.3, -33.33) - myCoordinates = QgsCoordinateFormatter.format(myPoint, QgsCoordinateFormatter.FormatDegreesMinutesSeconds, 2) - myTokens = myCoordinates.split(',') - myLongitude = myTokens[0] - myLatitude = myTokens[1] - myText = 'Latitude: %s, Longitude: %s' % (myLatitude, myLongitude) - - # Load the composition with the substitutions - myComposition = QgsComposition(QgsProject.instance()) - mySubstitutionMap = {'replace-me': myText} - myFile = os.path.join(TEST_DATA_DIR, 'template-for-substitution.qpt') - with open(myFile) as f: - myTemplateContent = f.read() - myDocument = QDomDocument() - myDocument.setContent(myTemplateContent) - myComposition.loadFromTemplate(myDocument, mySubstitutionMap) - - # We should be able to get map0 - myMap = myComposition.getComposerMapById(0) - myMessage = ('Map 0 could not be found in template %s', myFile) - assert myMap is not None, myMessage - - def testNoSubstitutionMap(self): - """Test that we can get a map if we use no text substitutions.""" - myComposition = QgsComposition(QgsProject.instance()) - myFile = os.path.join(TEST_DATA_DIR, 'template-for-substitution.qpt') - with open(myFile) as f: - myTemplateContent = f.read() - myDocument = QDomDocument() - myDocument.setContent(myTemplateContent) - myComposition.loadFromTemplate(myDocument) - - # We should be able to get map0 - myMap = myComposition.getComposerMapById(0) - myMessage = ('Map 0 could not be found in template %s', myFile) - assert myMap is not None, myMessage - - def testPrintMapFromTemplate(self): - """Test that we can get a map to render in the template.""" - myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') - myFileInfo = QFileInfo(myPath) - myRasterLayer = QgsRasterLayer(myFileInfo.filePath(), - myFileInfo.completeBaseName()) - myRenderer = QgsMultiBandColorRenderer( - myRasterLayer.dataProvider(), 2, 3, 4 - ) - # mRasterLayer.setRenderer( rasterRenderer ) - myPipe = myRasterLayer.pipe() - assert myPipe.set(myRenderer), "Cannot set pipe renderer" - - QgsProject.instance().addMapLayers([myRasterLayer]) - - myComposition = QgsComposition(QgsProject.instance()) - myFile = os.path.join(TEST_DATA_DIR, 'template-for-substitution.qpt') - with open(myFile) as f: - myTemplateContent = f.read() - myDocument = QDomDocument() - myDocument.setContent(myTemplateContent) - myComposition.loadFromTemplate(myDocument) - - # now render the map, first zooming to the raster extents - myMap = myComposition.getComposerMapById(0) - myMessage = ('Map 0 could not be found in template %s', myFile) - assert myMap is not None, myMessage - - myExtent = myRasterLayer.extent() - myMap.setNewExtent(myExtent) - myMap.setLayers([myRasterLayer]) - - myImagePath = os.path.join(str(QDir.tempPath()), - 'template_map_render_python.png') - - myPageNumber = 0 - myImage = myComposition.printPageAsRaster(myPageNumber) - myImage.save(myImagePath) - assert os.path.exists(myImagePath), 'Map render was not created.' - - # Not sure if this is a predictable way to test but its quicker than - # rendering. - myFileSize = QFileInfo(myImagePath).size() - myExpectedFileSize = 100000 - myMessage = ('Expected file size to be greater than %s, got %s' - ' for %s' % - (myExpectedFileSize, myFileSize, myImagePath)) - assert myFileSize > myExpectedFileSize, myMessage - - def testSaveRestore(self): - # test that properties are restored correctly from XML - composition = QgsComposition(QgsProject.instance()) - composition.setName('test composition') - - doc = QDomDocument("testdoc") - elem = doc.createElement("qgis") - doc.appendChild(elem) - elem = doc.createElement("composer") - self.assertTrue(composition.writeXml(elem, doc)) - - composition2 = QgsComposition(QgsProject.instance()) - self.assertTrue(composition2.readXml(elem, doc)) - - self.assertEqual(composition.name(), 'test composition') - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/src/python/test_qgscoordinatetransform.py b/tests/src/python/test_qgscoordinatetransform.py index f76246a71f0..cfbeacecc01 100644 --- a/tests/src/python/test_qgscoordinatetransform.py +++ b/tests/src/python/test_qgscoordinatetransform.py @@ -16,8 +16,10 @@ import qgis # NOQA from qgis.core import (QgsRectangle, QgsCoordinateReferenceSystem, - QgsCoordinateTransform - ) + QgsCoordinateTransform, + QgsCoordinateTransformContext, + QgsDatumTransform, + QgsProject) from qgis.testing import start_app, unittest start_app() @@ -32,7 +34,7 @@ class TestQgsCoordinateTransform(unittest.TestCase): myGeoCrs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) myUtmCrs = QgsCoordinateReferenceSystem() myUtmCrs.createFromId(32756, QgsCoordinateReferenceSystem.EpsgCrsId) - myXForm = QgsCoordinateTransform(myUtmCrs, myGeoCrs) + myXForm = QgsCoordinateTransform(myUtmCrs, myGeoCrs, QgsProject.instance()) myProjectedExtent = myXForm.transformBoundingBox(myExtent) myExpectedExtent = ('150.1509239873580270,-35.7176936443908772 : ' '150.1964384662953194,-35.6971885216629090') @@ -47,6 +49,200 @@ class TestQgsCoordinateTransform(unittest.TestCase): self.assertAlmostEqual(myExpectedValues[2], myProjectedExtent.xMaximum(), msg=myMessage) self.assertAlmostEqual(myExpectedValues[3], myProjectedExtent.yMaximum(), msg=myMessage) + def testTransformQgsRectangle_Regression17600(self): + """Test that rectangle transform is in the bindings""" + myExtent = QgsRectangle(-1797107, 4392148, 6025926, 6616304) + myGeoCrs = QgsCoordinateReferenceSystem() + myGeoCrs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) + myUtmCrs = QgsCoordinateReferenceSystem() + myUtmCrs.createFromId(3857, QgsCoordinateReferenceSystem.EpsgCrsId) + myXForm = QgsCoordinateTransform(myUtmCrs, myGeoCrs, QgsProject.instance()) + myTransformedExtent = myXForm.transform(myExtent) + myTransformedExtentForward = myXForm.transform(myExtent, QgsCoordinateTransform.ForwardTransform) + self.assertAlmostEquals(myTransformedExtentForward.xMaximum(), myTransformedExtent.xMaximum()) + self.assertAlmostEquals(myTransformedExtentForward.xMinimum(), myTransformedExtent.xMinimum()) + self.assertAlmostEquals(myTransformedExtentForward.yMaximum(), myTransformedExtent.yMaximum()) + self.assertAlmostEquals(myTransformedExtentForward.yMinimum(), myTransformedExtent.yMinimum()) + self.assertAlmostEquals(myTransformedExtentForward.xMaximum(), 54.13181426773211) + self.assertAlmostEquals(myTransformedExtentForward.xMinimum(), -16.14368685298181) + self.assertAlmostEquals(myTransformedExtentForward.yMaximum(), 50.971783118386895) + self.assertAlmostEquals(myTransformedExtentForward.yMinimum(), 36.66235970825241) + myTransformedExtentReverse = myXForm.transform(myTransformedExtent, QgsCoordinateTransform.ReverseTransform) + self.assertAlmostEquals(myTransformedExtentReverse.xMaximum(), myExtent.xMaximum()) + self.assertAlmostEquals(myTransformedExtentReverse.xMinimum(), myExtent.xMinimum()) + self.assertAlmostEquals(myTransformedExtentReverse.yMaximum(), myExtent.yMaximum()) + self.assertAlmostEquals(myTransformedExtentReverse.yMinimum(), myExtent.yMinimum()) + + @unittest.skip('ifdefed out in c++ until required') + def testContextSingle(self): + """ + Various tests to ensure that datum transforms are correctly set respecting context + """ + context = QgsCoordinateTransformContext() + context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 1) + context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4283'), 2) + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), + 3, 4) + + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28354'), QgsCoordinateReferenceSystem('EPSG:28353'), context) + # should be no datum transforms + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + # matching source + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:28353'), context) + self.assertEqual(transform.sourceDatumTransformId(), 1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + # matching dest + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28354'), + QgsCoordinateReferenceSystem('EPSG:4283'), context) + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), 2) + # matching src/dest pair + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), context) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + + # test manual overwriting + transform.setSourceDatumTransform(11) + transform.setDestinationDatumTransform(13) + self.assertEqual(transform.sourceDatumTransformId(), 11) + self.assertEqual(transform.destinationDatumTransformId(), 13) + + # test that auto datum setting occurs when updating src/dest crs + transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + transform.setSourceDatumTransform(11) + transform.setDestinationDatumTransform(13) + + transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + transform.setSourceDatumTransform(11) + transform.setDestinationDatumTransform(13) + + # delayed context set + transform = QgsCoordinateTransform() + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) + transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + transform.setContext(context) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + + def testContext(self): + """ + Various tests to ensure that datum transforms are correctly set respecting context + """ + context = QgsCoordinateTransformContext() + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), + 3, 4) + + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28354'), QgsCoordinateReferenceSystem('EPSG:28353'), context) + # should be no datum transforms + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + # matching source + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:28353'), context) + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + # matching dest + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28354'), + QgsCoordinateReferenceSystem('EPSG:4283'), context) + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + # matching src/dest pair + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), context) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + + # test manual overwriting + transform.setSourceDatumTransformId(11) + transform.setDestinationDatumTransformId(13) + self.assertEqual(transform.sourceDatumTransformId(), 11) + self.assertEqual(transform.destinationDatumTransformId(), 13) + + # test that auto datum setting occurs when updating src/dest crs + transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + transform.setSourceDatumTransformId(11) + transform.setDestinationDatumTransformId(13) + + transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + transform.setSourceDatumTransformId(11) + transform.setDestinationDatumTransformId(13) + + # delayed context set + transform = QgsCoordinateTransform() + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) + transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) + self.assertEqual(transform.sourceDatumTransformId(), -1) + self.assertEqual(transform.destinationDatumTransformId(), -1) + transform.setContext(context) + self.assertEqual(transform.sourceDatumTransformId(), 3) + self.assertEqual(transform.destinationDatumTransformId(), 4) + + def testProjectContext(self): + """ + Test creating transform using convenience constructor which takes project reference + """ + p = QgsProject() + context = p.transformContext() + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:3111'), 1, 2) + p.setTransformContext(context) + + transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:3111'), p) + self.assertEqual(transform.sourceDatumTransformId(), 1) + self.assertEqual(transform.destinationDatumTransformId(), 2) + + def testTransformInfo(self): + # hopefully this transform is available on all platforms! + transforms = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4613), QgsCoordinateReferenceSystem(4326)) + self.assertTrue(len(transforms) > 0) + self.assertIn('+towgs84=-403,684,41', [QgsDatumTransform.datumTransformToProj(t.sourceTransformId) for t in transforms]) + self.assertEqual([''] * len(transforms), [QgsDatumTransform.datumTransformToProj(t.destinationTransformId) for t in transforms]) + self.assertIn('EPSG:4613', [QgsDatumTransform.datumTransformInfo(t.sourceTransformId).sourceCrsAuthId for t in + transforms]) + self.assertEqual([''] * len(transforms), [QgsDatumTransform.datumTransformInfo(t.destinationTransformId).destinationCrsAuthId for t in + transforms]) + + # and the reverse + transforms = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(4613)) + self.assertTrue(len(transforms) > 0) + self.assertEqual([''] * len(transforms), [QgsDatumTransform.datumTransformToProj(t.sourceTransformId) for t in transforms]) + self.assertIn('+towgs84=-403,684,41', + [QgsDatumTransform.datumTransformToProj(t.destinationTransformId) for t in transforms]) + self.assertEqual([''] * len(transforms), [QgsDatumTransform.datumTransformInfo(t.sourceTransformId).destinationCrsAuthId for t in + transforms]) + self.assertIn('EPSG:4613', [QgsDatumTransform.datumTransformInfo(t.destinationTransformId).sourceCrsAuthId for t in + transforms]) + + def testStringToTransformId(self): + """ + Test converting proj strings to corresponding datum IDs + """ + self.assertEqual(QgsDatumTransform.projStringToDatumTransformId(''), -1) + self.assertEqual(QgsDatumTransform.projStringToDatumTransformId('not'), -1) + test_string = '+towgs84=-403,684,41' + id = QgsDatumTransform.projStringToDatumTransformId(test_string) + self.assertNotEqual(id, -1) + string = QgsDatumTransform.datumTransformToProj(id) + self.assertEqual(string, test_string) + self.assertEqual(QgsDatumTransform.projStringToDatumTransformId(test_string.upper()), id) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgscoordinatetransformcontext.py b/tests/src/python/test_qgscoordinatetransformcontext.py new file mode 100644 index 00000000000..7d0b6b89866 --- /dev/null +++ b/tests/src/python/test_qgscoordinatetransformcontext.py @@ -0,0 +1,413 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsCoordinateTransformContext + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '11/5/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsCoordinateReferenceSystem, + QgsCoordinateTransformContext, + QgsCoordinateTransform, + QgsDatumTransform, + QgsReadWriteContext, + QgsProject, + QgsSettings) +from qgis.testing import start_app, unittest +from qgis.PyQt.QtXml import QDomDocument +from qgis.PyQt.QtTest import QSignalSpy +from qgis.PyQt.QtCore import QCoreApplication + +app = start_app() + + +class TestQgsCoordinateTransformContext(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """Run before all tests""" + QCoreApplication.setOrganizationName("QGIS_Test") + QCoreApplication.setOrganizationDomain("TestPyQgsWFSProvider.com") + QCoreApplication.setApplicationName("TestPyQgsWFSProvider") + QgsSettings().clear() + + @unittest.skip('ifdefed out in c++ until required') + def testSourceDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.sourceDatumTransforms(), {}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), 1)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 2)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 3)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem(28356), 3)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # invalid crs should fail + self.assertFalse(context.addSourceDatumTransform(QgsCoordinateReferenceSystem(), 4)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # indicate no transform required + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem(28357), -1)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3, 'EPSG:28357': -1}) + + # removing non-existing + context.removeSourceDatumTransform(QgsCoordinateReferenceSystem(28354)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3, 'EPSG:28357': -1}) + + # remove existing + context.removeSourceDatumTransform(QgsCoordinateReferenceSystem(28356)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28357': -1}) + + context.clear() + self.assertEqual(context.sourceDatumTransforms(), {}) + + @unittest.skip('ifdefed out in c++ until required') + def testDestDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.destinationDatumTransforms(), {}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), 1)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 2)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 3)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem(28356), 3)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # invalid crs should fail + self.assertFalse(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem(), 4)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # indicate no transform required + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem(28357), -1)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3, 'EPSG:28357': -1}) + + # removing non-existing + context.removeSourceDatumTransform(QgsCoordinateReferenceSystem(28354)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3, 'EPSG:28357': -1}) + + # remove existing + context.removeDestinationDatumTransform(QgsCoordinateReferenceSystem(28356)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28357': -1}) + + context.clear() + self.assertEqual(context.destinationDatumTransforms(), {}) + + def testSourceDestinationDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.sourceDestinationDatumTransforms(), {}) + self.assertFalse(context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3111'), QgsCoordinateReferenceSystem('EPSG:4283'))) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2)) + self.assertTrue( + context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3111'), QgsCoordinateReferenceSystem('EPSG:4283'))) + self.assertFalse( + context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3111'), QgsCoordinateReferenceSystem('EPSG:4326'))) + self.assertFalse( + context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3113'), QgsCoordinateReferenceSystem('EPSG:4283'))) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem(4283), 3, 4)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem(28357), 7, 8)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(7, 8)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:28357'), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11)}) + + # invalid additions + self.assertFalse(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(), + QgsCoordinateReferenceSystem('EPSG:28357'), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11)}) + self.assertFalse(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem(), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11)}) + + # indicate no transform required + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(28357), + QgsCoordinateReferenceSystem(28356), -1, -1)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3111), + QgsCoordinateReferenceSystem(28356), 17, -1)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1), + ('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3113), + QgsCoordinateReferenceSystem(28356), -1, 18)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1), + ('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1), + ('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)}) + # remove non-existing + context.removeSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3113), QgsCoordinateReferenceSystem(3111)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2), + ('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1), + ('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1), + ('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)}) + + # remove existing + context.removeSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3111), + QgsCoordinateReferenceSystem(4283)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1), + ('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1), + ('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)}) + context.removeSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3111), + QgsCoordinateReferenceSystem(28356)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4), + ('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11), + ('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1), + ('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)}) + + context.clear() + self.assertEqual(context.sourceDestinationDatumTransforms(), {}) + + @unittest.skip('ifdefed out in c++ until required') + def testCalculate(self): + context = QgsCoordinateTransformContext() + + #empty context + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (-1, -1)) + + #add src transform + context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 1) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (-1, -1)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (1, -1)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:4283'), + QgsCoordinateReferenceSystem('EPSG:28356')), + (-1, -1)) + + #add dest transform + context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4283'), 2) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4326')), + (-1, -1)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (-1, 2)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:4283'), + QgsCoordinateReferenceSystem('EPSG:3111')), + (-1, -1)) + + #add specific source/dest pair - should take precedence + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), + 3, 4) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (3, 4)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + (-1, 2)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:3111')), + (1, -1)) + + def testCalculateSourceDest(self): + context = QgsCoordinateTransformContext() + + #empty context + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + QgsDatumTransform.TransformPair(-1, -1)) + + #add specific source/dest pair - should take precedence + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283'), + 3, 4) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:4283')), + QgsDatumTransform.TransformPair(3, 4)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283')), + QgsDatumTransform.TransformPair(-1, -1)) + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:3111')), + QgsDatumTransform.TransformPair(-1, -1)) + # check that reverse transforms are automatically supported + self.assertEqual(context.calculateDatumTransforms(QgsCoordinateReferenceSystem('EPSG:4283'), + QgsCoordinateReferenceSystem('EPSG:28356')), + QgsDatumTransform.TransformPair(4, 3)) + + @unittest.skip('ifdefed out in c++ until required') + def testWriteReadXmlSingleVariant(self): + # setup a context + context = QgsCoordinateTransformContext() + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), 1)) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 2)) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3113'), 11)) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28355'), 12)) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2)) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem(4283), 3, 4)) + + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3113': 11, 'EPSG:28355': 12}) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4)}) + + # save to xml + doc = QDomDocument("testdoc") + elem = doc.createElement("test") + context.writeXml(elem, QgsReadWriteContext()) + + # restore from xml + context2 = QgsCoordinateTransformContext() + context2.readXml(elem, QgsReadWriteContext()) + + # check result + self.assertEqual(context2.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertEqual(context2.destinationDatumTransforms(), {'EPSG:3113': 11, 'EPSG:28355': 12}) + self.assertEqual(context2.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4)}) + + def testWriteReadXml(self): + # setup a context + context = QgsCoordinateTransformContext() + + source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204), + QgsCoordinateReferenceSystem(4326))[0].sourceTransformId + dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204), + QgsCoordinateReferenceSystem(4326))[0].destinationTransformId + + source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205), + QgsCoordinateReferenceSystem(4326))[0].sourceTransformId + dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205), + QgsCoordinateReferenceSystem(4326))[0].destinationTransformId + + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(4204), + QgsCoordinateReferenceSystem(4326), source_id_1, dest_id_1)) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(4205), + QgsCoordinateReferenceSystem(4326), source_id_2, dest_id_2)) + + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:4204', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_1, dest_id_1), + ('EPSG:4205', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_2, dest_id_2)}) + + # save to xml + doc = QDomDocument("testdoc") + elem = doc.createElement("test") + context.writeXml(elem, QgsReadWriteContext()) + + # restore from xml + context2 = QgsCoordinateTransformContext() + context2.readXml(elem, QgsReadWriteContext()) + + # check result + self.assertEqual(context2.sourceDestinationDatumTransforms(), {('EPSG:4204', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_1, dest_id_1), + ('EPSG:4205', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_2, dest_id_2)}) + + def testMissingTransforms(self): + # fudge context xml with a missing transform + doc = QDomDocument("testdoc") + elem = doc.createElement("test") + contextElem = doc.createElement("transformContext") + transformElem = doc.createElement("srcDest") + transformElem.setAttribute("source", 'EPSG:4204') + transformElem.setAttribute("dest", 'EPSG:4326') + transformElem.setAttribute("sourceTransform", 'not valid') + transformElem.setAttribute("destTransform", 'not valid 2') + contextElem.appendChild(transformElem) + + elem2 = doc.createElement("test2") + elem2.appendChild(contextElem) + + # restore from xml + context2 = QgsCoordinateTransformContext() + ok, errors = context2.readXml(elem2, QgsReadWriteContext()) + self.assertFalse(ok) + + # check result + self.assertEqual(errors, ['not valid', 'not valid 2']) + + def testProject(self): + """ + Test project's transform context + """ + project = QgsProject() + context_changed_spy = QSignalSpy(project.transformContextChanged) + context = project.transformContext() + context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2) + project.setTransformContext(context) + self.assertEqual(len(context_changed_spy), 1) + self.assertEqual(project.transformContext().sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2)}) + + def testReadWriteSettings(self): + context = QgsCoordinateTransformContext() + context.readSettings() + + source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204), + QgsCoordinateReferenceSystem(4326))[0].sourceTransformId + dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204), + QgsCoordinateReferenceSystem(4326))[0].destinationTransformId + + source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205), + QgsCoordinateReferenceSystem(4326))[0].sourceTransformId + dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205), + QgsCoordinateReferenceSystem(4326))[0].destinationTransformId + + # should be empty + self.assertEqual(context.sourceDestinationDatumTransforms(), {}) + + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4204'), + QgsCoordinateReferenceSystem('EPSG:4326'), source_id_1, dest_id_1)) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4205'), + QgsCoordinateReferenceSystem(4326), source_id_2, dest_id_2)) + + self.assertEqual(context.sourceDestinationDatumTransforms(), + {('EPSG:4204', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_1, dest_id_1), + ('EPSG:4205', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_2, dest_id_2)}) + + # save to settings + context.writeSettings() + + # restore from settings + context2 = QgsCoordinateTransformContext() + self.assertEqual(context2.sourceDestinationDatumTransforms(), {}) + context2.readSettings() + + # check result + self.assertEqual(context2.sourceDestinationDatumTransforms(), + {('EPSG:4204', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_1, dest_id_1), + ('EPSG:4205', 'EPSG:4326'): QgsDatumTransform.TransformPair(source_id_2, dest_id_2)}) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgsdatetimeedit.py b/tests/src/python/test_qgsdatetimeedit.py new file mode 100644 index 00000000000..ad78e1ffe17 --- /dev/null +++ b/tests/src/python/test_qgsdatetimeedit.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsDateTimeEdit + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Denis Rouzaud' +__date__ = '2018-01-04' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.gui import QgsDateTimeEdit +from qgis.PyQt.QtCore import Qt, QDateTime +from qgis.testing import start_app, unittest + +start_app() + +DATE = QDateTime.fromString('2018-01-01 01:02:03', Qt.ISODate) + + +class TestQgsDateTimeEdit(unittest.TestCase): + + def testSettersGetters(self): + """ test widget handling of null values """ + w = qgis.gui.QgsDateTimeEdit() + w.setAllowNull(False) + + w.setDateTime(DATE) + self.assertEqual(DATE, w.dateTime()) + # date should remain when setting an invalid date + w.setDateTime(QDateTime()) + self.assertEqual(DATE, w.dateTime()) + + def testNullValueHandling(self): + """ test widget handling of null values """ + w = qgis.gui.QgsDateTimeEdit() + w.setAllowNull(True) + + # date should be valid again when not allowing NULL values + w.setDateTime(QDateTime()) + w.setAllowNull(False) + self.assertTrue(w.dateTime().isValid()) + + w.setAllowNull(True) + w.setDateTime(QDateTime()) + self.assertFalse(w.dateTime().isValid()) + + w.setAllowNull(False) + self.assertTrue(w.dateTime().isValid()) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgsdistancearea.py b/tests/src/python/test_qgsdistancearea.py index eac1e54c14e..9cb27a10005 100644 --- a/tests/src/python/test_qgsdistancearea.py +++ b/tests/src/python/test_qgsdistancearea.py @@ -19,8 +19,8 @@ from qgis.core import (QgsGeometry, QgsPointXY, QgsDistanceArea, QgsCoordinateReferenceSystem, - QgsUnitTypes - ) + QgsUnitTypes, + QgsProject) from qgis.testing import start_app, unittest from qgis.PyQt.QtCore import QLocale @@ -40,7 +40,7 @@ class TestQgsDistanceArea(unittest.TestCase): # try setting using a CRS object crs = QgsCoordinateReferenceSystem(3111, QgsCoordinateReferenceSystem.EpsgCrsId) - da.setSourceCrs(crs) + da.setSourceCrs(crs, QgsProject.instance().transformContext()) self.assertEqual(da.sourceCrs().srsid(), crs.srsid()) def testMeasureLine(self): @@ -64,12 +64,12 @@ class TestQgsDistanceArea(unittest.TestCase): da_3068 = QgsDistanceArea() da_wsg84 = QgsDistanceArea() - da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068')) + da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068'), QgsProject.instance().transformContext()) if (da_3068.sourceCrs().isGeographic()): da_3068.setEllipsoid(da_3068.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}]".format(u'Soldner Berlin', da_3068.sourceCrs().authid(), da_3068.sourceCrs().description()))) self.assertEqual(da_3068.sourceCrs().authid(), 'EPSG:3068') - da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326')) + da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_wsg84.sourceCrs().authid(), 'EPSG:4326') @@ -132,38 +132,38 @@ class TestQgsDistanceArea(unittest.TestCase): # +-+ + # checking returned length_mapunits/projected_points of diffferent world points with results from SpatiaLite ST_Project da_3068 = QgsDistanceArea() - da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068')) + da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068'), QgsProject.instance().transformContext()) if (da_3068.sourceCrs().isGeographic()): da_3068.setEllipsoid(da_3068.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_3068.sourceCrs().authid(), 'EPSG:3068') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:3068', da_3068.sourceCrs().authid(), da_3068.sourceCrs().description(), da_3068.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_3068.lengthUnits()), da_3068.sourceCrs().projectionAcronym(), da_3068.sourceCrs().ellipsoidAcronym()))) da_wsg84 = QgsDistanceArea() - da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326')) + da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_wsg84.sourceCrs().authid(), 'EPSG:4326') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}] ellipsoid[{}]".format(u'EPSG:4326', da_wsg84.sourceCrs().authid(), da_wsg84.sourceCrs().description(), da_wsg84.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_wsg84.lengthUnits()), da_wsg84.sourceCrs().projectionAcronym(), da_wsg84.sourceCrs().ellipsoidAcronym(), da_wsg84.ellipsoid()))) da_4314 = QgsDistanceArea() - da_4314.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4314')) + da_4314.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4314'), QgsProject.instance().transformContext()) if (da_4314.sourceCrs().isGeographic()): da_4314.setEllipsoid(da_4314.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_4314.sourceCrs().authid(), 'EPSG:4314') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:4314', da_4314.sourceCrs().authid(), da_4314.sourceCrs().description(), da_4314.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_4314.lengthUnits()), da_4314.sourceCrs().projectionAcronym(), da_4314.sourceCrs().ellipsoidAcronym()))) da_4805 = QgsDistanceArea() - da_4805.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4805')) + da_4805.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4805'), QgsProject.instance().transformContext()) if (da_4805.sourceCrs().isGeographic()): da_4805.setEllipsoid(da_4805.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_4805.sourceCrs().authid(), 'EPSG:4805') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:4805', da_4805.sourceCrs().authid(), da_4805.sourceCrs().description(), da_4805.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_4805.lengthUnits()), da_4805.sourceCrs().projectionAcronym(), da_4805.sourceCrs().ellipsoidAcronym()))) # EPSG:5665 unknown, why? da_5665 = QgsDistanceArea() - da_5665.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:5665')) + da_5665.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:5665'), QgsProject.instance().transformContext()) if (da_5665.sourceCrs().isGeographic()): da_5665.setEllipsoid(da_5665.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:5665', da_5665.sourceCrs().authid(), da_5665.sourceCrs().description(), da_5665.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_5665.lengthUnits()), da_5665.sourceCrs().projectionAcronym(), da_5665.sourceCrs().ellipsoidAcronym()))) #self.assertEqual(da_5665.sourceCrs().authid(), 'EPSG:5665') da_25833 = QgsDistanceArea() - da_25833.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:25833')) + da_25833.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:25833'), QgsProject.instance().transformContext()) if (da_25833.sourceCrs().isGeographic()): da_25833.setEllipsoid(da_25833.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:25833', da_25833.sourceCrs().authid(), da_25833.sourceCrs().description(), da_25833.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_25833.lengthUnits()), da_25833.sourceCrs().projectionAcronym(), da_25833.sourceCrs().ellipsoidAcronym()))) @@ -470,7 +470,7 @@ class TestQgsDistanceArea(unittest.TestCase): """ da = QgsDistanceArea() - da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452)) + da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # We check both the measured length AND the units, in case the logic regarding @@ -496,7 +496,7 @@ class TestQgsDistanceArea(unittest.TestCase): self.assertAlmostEqual(distance, 133.669, delta=0.01) # now try with a source CRS which is in feet - da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469)) + da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # measurement should be in feet distance = da.measureLine(QgsPointXY(1, 1), QgsPointXY(2, 3)) @@ -527,7 +527,7 @@ class TestQgsDistanceArea(unittest.TestCase): """ da = QgsDistanceArea() - da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452)) + da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") polygon = QgsGeometry.fromPolygonXY( @@ -564,7 +564,7 @@ class TestQgsDistanceArea(unittest.TestCase): QgsPointXY(1850000, 4423000), QgsPointXY(1851000, 4423000), QgsPointXY(1851000, 4424000), QgsPointXY(1852000, 4424000), QgsPointXY(1852000, 4425000), QgsPointXY(1851000, 4425000), QgsPointXY(1850000, 4423000) ]] ) - da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469)) + da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # measurement should be in square feet area = da.measureArea(polygon) diff --git a/tests/src/python/test_qgseditformconfig.py b/tests/src/python/test_qgseditformconfig.py new file mode 100644 index 00000000000..b32d1b2211b --- /dev/null +++ b/tests/src/python/test_qgseditformconfig.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsEditFormConfig. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '11/04/2017' +__copyright__ = 'Copyright 2018, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsVectorLayer, + QgsReadWriteContext) +from qgis.gui import QgsGui + +from qgis.testing import start_app, unittest +from qgis.PyQt.QtXml import QDomDocument, QDomElement + +start_app() + + +class TestQgsEditFormConfig(unittest.TestCase): + + @classmethod + def setUpClass(cls): + QgsGui.editorWidgetRegistry().initEditors() + + def createLayer(self): + self.layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", + "addfeat", "memory") + return self.layer + + def testReadWriteXml(self): + layer = self.createLayer() + config = layer.editFormConfig() + + config.setReadOnly(0, True) + config.setReadOnly(1, False) + config.setLabelOnTop(0, False) + config.setLabelOnTop(1, True) + + doc = QDomDocument("testdoc") + elem = doc.createElement('edit') + config.writeXml(elem, QgsReadWriteContext()) + + layer2 = self.createLayer() + config2 = layer2.editFormConfig() + config2.readXml(elem, QgsReadWriteContext()) + + self.assertTrue(config2.readOnly(0)) + self.assertFalse(config2.readOnly(1)) + self.assertFalse(config2.labelOnTop(0)) + self.assertTrue(config2.labelOnTop(1)) + + def testReadOnly(self): + layer = self.createLayer() + config = layer.editFormConfig() + + # safety checks + config.setReadOnly(-1, True) + config.setReadOnly(100, True) + + # real checks + config.setReadOnly(0, True) + config.setReadOnly(1, True) + self.assertTrue(config.readOnly(0)) + self.assertTrue(config.readOnly(1)) + + config.setReadOnly(0, False) + config.setReadOnly(1, False) + self.assertFalse(config.readOnly(0)) + self.assertFalse(config.readOnly(1)) + + def testLabelOnTop(self): + layer = self.createLayer() + config = layer.editFormConfig() + + # safety checks + config.setLabelOnTop(-1, True) + config.setLabelOnTop(100, True) + + # real checks + config.setLabelOnTop(0, True) + config.setLabelOnTop(1, True) + self.assertTrue(config.labelOnTop(0)) + self.assertTrue(config.labelOnTop(1)) + + config.setLabelOnTop(0, False) + config.setLabelOnTop(1, False) + self.assertFalse(config.labelOnTop(0)) + self.assertFalse(config.labelOnTop(1)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgseditwidgets.py b/tests/src/python/test_qgseditwidgets.py index cfc13353222..7f09dffb6e7 100644 --- a/tests/src/python/test_qgseditwidgets.py +++ b/tests/src/python/test_qgseditwidgets.py @@ -26,8 +26,6 @@ start_app() class TestQgsTextEditWidget(unittest.TestCase): - VALUEMAP_NULL_TEXT = "{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}" - @classmethod def setUpClass(cls): QgsGui.editorWidgetRegistry().initEditors() @@ -39,8 +37,8 @@ class TestQgsTextEditWidget(unittest.TestCase): f = QgsFeature() f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) - assert pr.addFeatures([f]) - assert self.layer.pendingFeatureCount() == 1 + self.assertTrue(pr.addFeatures([f])) + self.assertEqual(self.layer.featureCount(), 1) return self.layer def doAttributeTest(self, idx, expected): @@ -50,16 +48,16 @@ class TestQgsTextEditWidget(unittest.TestCase): editwidget = reg.create('TextEdit', self.layer, idx, config, None, None) editwidget.setValue('value') - assert editwidget.value() == expected[0] + self.assertEqual(editwidget.value(), expected[0]) editwidget.setValue(123) - assert editwidget.value() == expected[1] + self.assertEqual(editwidget.value(), expected[1]) editwidget.setValue(None) - assert editwidget.value() == expected[2] + self.assertEqual(editwidget.value(), expected[2]) editwidget.setValue(NULL) - assert editwidget.value() == expected[3] + self.assertEqual(editwidget.value(), expected[3]) def test_SetValue(self): self.createLayerWithOnePoint() @@ -70,7 +68,7 @@ class TestQgsTextEditWidget(unittest.TestCase): def testStringWithMaxLen(self): """ tests that text edit wrappers correctly handle string fields with a maximum length """ layer = QgsVectorLayer("none?field=fldint:integer", "layer", "memory") - assert layer.isValid() + self.assertTrue(layer.isValid()) layer.dataProvider().addAttributes([QgsField('max', QVariant.String, 'string', 10), QgsField('nomax', QVariant.String, 'string', 0)]) layer.updateFields() @@ -94,15 +92,34 @@ class TestQgsTextEditWidget(unittest.TestCase): QgsProject.instance().removeAllMapLayers() + +class TestQgsValueRelationWidget(unittest.TestCase): + + def test_enableDisable(self): + reg = QgsGui.editorWidgetRegistry() + layer = QgsVectorLayer("none?field=number:integer", "layer", "memory") + wrapper = reg.create('ValueRelation', layer, 0, {}, None, None) + + widget = wrapper.widget() + + self.assertTrue(widget.isEnabled()) + wrapper.setEnabled(False) + self.assertFalse(widget.isEnabled()) + wrapper.setEnabled(True) + self.assertTrue(widget.isEnabled()) + + +class TestQgsValueMapEditWidget(unittest.TestCase): + VALUEMAP_NULL_TEXT = "{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}" + def test_ValueMap_set_get(self): layer = QgsVectorLayer("none?field=number:integer", "layer", "memory") - assert layer.isValid() + self.assertTrue(layer.isValid()) QgsProject.instance().addMapLayer(layer) reg = QgsGui.editorWidgetRegistry() configWdg = reg.createConfigWidget('ValueMap', layer, 0, None) - config = {'map': {'two': '2', 'twoandhalf': '2.5', 'NULL text': 'NULL', - 'nothing': self.VALUEMAP_NULL_TEXT}} + config = {'map': [{'two': '2'}, {'twoandhalf': '2.5'}, {'NULL text': 'NULL'}, {'nothing': self.VALUEMAP_NULL_TEXT}]} # Set a configuration containing values and NULL and check if it # is returned intact. @@ -110,4 +127,7 @@ class TestQgsTextEditWidget(unittest.TestCase): self.assertEqual(configWdg.config(), config) QgsProject.instance().removeAllMapLayers() + + +if __name__ == "__main__": unittest.main() diff --git a/tests/src/python/test_qgsexpressionbuilderwidget.py b/tests/src/python/test_qgsexpressionbuilderwidget.py index 090102e20dc..b5e2771f1b2 100644 --- a/tests/src/python/test_qgsexpressionbuilderwidget.py +++ b/tests/src/python/test_qgsexpressionbuilderwidget.py @@ -32,13 +32,13 @@ def createReferencingLayer(): "referencinglayer", "memory") pr = layer.dataProvider() f1 = QgsFeature() - f1.setFields(layer.pendingFields()) + f1.setFields(layer.fields()) f1.setAttributes(["test1", 123]) f2 = QgsFeature() - f2.setFields(layer.pendingFields()) + f2.setFields(layer.fields()) f2.setAttributes(["test2", 123]) f3 = QgsFeature() - f3.setFields(layer.pendingFields()) + f3.setFields(layer.fields()) f3.setAttributes(["foobar'bar", 124]) assert pr.addFeatures([f1, f2, f3]) return layer @@ -50,13 +50,13 @@ def createReferencedLayer(): "referencedlayer", "memory") pr = layer.dataProvider() f1 = QgsFeature() - f1.setFields(layer.pendingFields()) + f1.setFields(layer.fields()) f1.setAttributes(["foo", 123, 321]) f2 = QgsFeature() - f2.setFields(layer.pendingFields()) + f2.setFields(layer.fields()) f2.setAttributes(["bar", 456, 654]) f3 = QgsFeature() - f3.setFields(layer.pendingFields()) + f3.setFields(layer.fields()) f3.setAttributes(["foobar'bar", 789, 554]) assert pr.addFeatures([f1, f2, f3]) return layer diff --git a/tests/src/python/test_qgsfeatureiterator.py b/tests/src/python/test_qgsfeatureiterator.py index 3a32cf4510c..0f048213718 100644 --- a/tests/src/python/test_qgsfeatureiterator.py +++ b/tests/src/python/test_qgsfeatureiterator.py @@ -103,13 +103,13 @@ class TestQgsFeatureIterator(unittest.TestCase): def addFeatures(self, vl): feat = QgsFeature() - fields = vl.pendingFields() + fields = vl.fields() feat.setFields(fields) feat['Staff'] = 4 vl.addFeature(feat) feat = QgsFeature() - fields = vl.pendingFields() + fields = vl.fields() feat.setFields(fields) feat['Staff'] = 2 vl.addFeature(feat) @@ -149,7 +149,7 @@ class TestQgsFeatureIterator(unittest.TestCase): layer = QgsVectorLayer(myShpFile, 'Points', 'ogr') self.assertTrue(layer.isValid()) - cnt = layer.pendingFields().count() # NOQA + cnt = layer.fields().count() # NOQA idx = layer.addExpressionField('"exp3"*2', QgsField('exp1', QVariant.LongLong)) # NOQA idx = layer.addExpressionField('"exp1"-1', QgsField('exp2', QVariant.LongLong)) # NOQA idx = layer.addExpressionField('"exp2"*3', QgsField('exp3', QVariant.LongLong)) # NOQA diff --git a/tests/src/python/test_qgsfeaturesource.py b/tests/src/python/test_qgsfeaturesource.py index 4a3ef3a5894..841a05b92b3 100644 --- a/tests/src/python/test_qgsfeaturesource.py +++ b/tests/src/python/test_qgsfeaturesource.py @@ -19,6 +19,7 @@ from qgis.core import (QgsVectorLayer, QgsFeature, QgsGeometry, QgsPointXY, + QgsProject, QgsFeatureRequest, QgsWkbTypes, QgsCoordinateReferenceSystem) @@ -121,7 +122,7 @@ class TestQgsFeatureSource(unittest.TestCase): self.assertEqual(new_features[id].attributes(), f.attributes()) # materialize with reprojection - request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3785')) + request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3785'), QgsProject.instance().transformContext()) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields(), layer.fields()) self.assertEqual(new_layer.crs().authid(), 'EPSG:3785') diff --git a/tests/src/python/test_qgsfieldmodel.py b/tests/src/python/test_qgsfieldmodel.py old mode 100644 new mode 100755 index fcbe36f4e08..de00a831bc3 --- a/tests/src/python/test_qgsfieldmodel.py +++ b/tests/src/python/test_qgsfieldmodel.py @@ -14,8 +14,10 @@ __revision__ = '$Format:%H$' import qgis # NOQA -from qgis.core import QgsFields, QgsVectorLayer -from qgis.core import QgsFieldModel +from qgis.core import (QgsField, + QgsFields, + QgsVectorLayer, + QgsFieldModel) from qgis.PyQt.QtCore import QVariant, Qt from qgis.testing import start_app, unittest @@ -38,7 +40,6 @@ def create_model(): class TestQgsFieldModel(unittest.TestCase): - def testGettersSetters(self): """ test model getters/setters """ l = create_layer() @@ -245,6 +246,16 @@ class TestQgsFieldModel(unittest.TestCase): m.setAllowEmptyFieldName(True) self.assertFalse(m.data(m.indexFromName(None), Qt.DisplayRole)) + def testFieldTooltip(self): + f = QgsField('my_string', QVariant.String, 'string') + self.assertEqual(QgsFieldModel.fieldToolTip(f), 'my_string

string

') + f.setAlias('my alias') + self.assertEqual(QgsFieldModel.fieldToolTip(f), 'my alias (my_string)

string

') + f.setLength(20) + self.assertEqual(QgsFieldModel.fieldToolTip(f), 'my alias (my_string)

string (20)

') + f = QgsField('my_real', QVariant.Double, 'real', 8, 3) + self.assertEqual(QgsFieldModel.fieldToolTip(f), 'my_real

real (8, 3)

') + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsfileutils.py b/tests/src/python/test_qgsfileutils.py new file mode 100644 index 00000000000..5db01cd9ac7 --- /dev/null +++ b/tests/src/python/test_qgsfileutils.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsFileUtils. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '18/12/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import QgsFileUtils +from qgis.testing import unittest + + +class TestQgsFileUtils(unittest.TestCase): + + def testExtensionsFromFilter(self): + self.assertEqual(QgsFileUtils.extensionsFromFilter(''), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('bad'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('*'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('*.'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('Tiff files'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('(*.)'), []) + self.assertEqual(QgsFileUtils.extensionsFromFilter('PNG Files (*.png)'), ['png']) + self.assertEqual(QgsFileUtils.extensionsFromFilter('PNG Files (*.PNG)'), ['PNG']) + self.assertEqual(QgsFileUtils.extensionsFromFilter('Geotiff Files (*.tiff *.tif)'), ['tiff', 'tif']) + + def testEnsureFileNameHasExtension(self): + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', ['']), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', []), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', []), 'test') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('', ['.tif']), '') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', ['.tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test', ['tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', []), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['bmp']), 'test.tif.bmp') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['tiff']), 'test.tif.tiff') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['tiff', 'tif']), 'test.tif') + self.assertEqual(QgsFileUtils.ensureFileNameHasExtension('test.tif', ['TIFF', 'TIF']), 'test.tif') + + def testAddExtensionFromFilter(self): + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'TIFF Files (*.tif)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'TIFF Files (*.tif)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', ''), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'BMP Files (*.bmp)'), 'test.tif.bmp') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.tiff)'), 'test.tif.tiff') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.tif *.tiff)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'TIFF Files (*.TIF *.TIFF)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test.tif', 'All Files (*.*)'), 'test.tif') + self.assertEqual(QgsFileUtils.addExtensionFromFilter('test', 'All Files (*.*)'), 'test') + + def testStringToSafeFilename(self): + self.assertEqual(QgsFileUtils.stringToSafeFilename('my FiLe v2.0_new.tif'), 'my FiLe v2.0_new.tif') + self.assertEqual( + QgsFileUtils.stringToSafeFilename('rendered map_final? rev (12-03-1017)_real@#$&*#%&*$!!@$%^&(*(.tif'), + 'rendered map_final_ rev _12-03-1017__real____________________.tif') + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgsgeometry_avoid_intersections.py b/tests/src/python/test_qgsgeometry_avoid_intersections.py index 95ca2e112e3..5d1b8a20da0 100644 --- a/tests/src/python/test_qgsgeometry_avoid_intersections.py +++ b/tests/src/python/test_qgsgeometry_avoid_intersections.py @@ -57,7 +57,7 @@ class TestQgsGeometryAvoidIntersections(unittest.TestCase): features.append(f) l.dataProvider().addFeatures(features) - assert l.pendingFeatureCount() == 7 + assert l.featureCount() == 7 # create a geometry and remove its intersections with other geometries diff --git a/tests/src/python/test_qgsinterval.py b/tests/src/python/test_qgsinterval.py index 7a19e47f117..09f5795fa71 100644 --- a/tests/src/python/test_qgsinterval.py +++ b/tests/src/python/test_qgsinterval.py @@ -118,6 +118,9 @@ class TestQgsInterval(unittest.TestCase): i = QgsInterval.fromString('2 Years') self.assertTrue(i.isValid()) self.assertEqual(i.years(), 2) + i = QgsInterval.fromString('20000 Years') + self.assertTrue(i.isValid()) + self.assertEqual(i.years(), 20000) i = QgsInterval.fromString('30 month') self.assertTrue(i.isValid()) self.assertEqual(i.months(), 30) @@ -133,6 +136,9 @@ class TestQgsInterval(unittest.TestCase): i = QgsInterval.fromString('1 Day') self.assertTrue(i.isValid()) self.assertEqual(i.seconds(), 24 * 60 * 60) + i = QgsInterval.fromString('101.5 Days') + self.assertTrue(i.isValid()) + self.assertEqual(i.seconds(), 101.5 * 24 * 60 * 60) i = QgsInterval.fromString('2 dAys') self.assertTrue(i.isValid()) self.assertEqual(i.seconds(), 48 * 60 * 60) diff --git a/tests/src/python/test_qgslayout.py b/tests/src/python/test_qgslayout.py index 2a06d74ac41..41f28cae779 100644 --- a/tests/src/python/test_qgslayout.py +++ b/tests/src/python/test_qgslayout.py @@ -14,6 +14,9 @@ __revision__ = '$Format:%H$' import qgis # NOQA import sip +import tempfile +import shutil +import os from qgis.core import (QgsUnitTypes, QgsLayout, @@ -21,12 +24,18 @@ from qgis.core import (QgsUnitTypes, QgsLayoutGuide, QgsLayoutObject, QgsProject, + QgsPrintLayout, + QgsLayoutItemGroup, + QgsLayoutItem, + QgsLayoutItemHtml, QgsProperty, QgsLayoutPageCollection, QgsLayoutMeasurement, + QgsLayoutFrame, QgsFillSymbol, QgsReadWriteContext, QgsLayoutItemMap, + QgsLayoutItemLabel, QgsLayoutSize, QgsLayoutPoint) from qgis.PyQt.QtCore import Qt, QCoreApplication, QEvent, QPointF, QRectF @@ -40,9 +49,19 @@ start_app() class TestQgsLayout(unittest.TestCase): + @classmethod + def setUpClass(cls): + """Run before all tests""" + cls.basetestpath = tempfile.mkdtemp() + + @classmethod + def tearDownClass(cls): + """Run after all tests""" + shutil.rmtree(cls.basetestpath, True) + def testReadWriteXml(self): p = QgsProject() - l = QgsLayout(p) + l = QgsPrintLayout(p) l.setName('my layout') l.setUnits(QgsUnitTypes.LayoutInches) collection = l.pageCollection() @@ -62,10 +81,20 @@ class TestQgsLayout(unittest.TestCase): snapper = l.snapper() snapper.setSnapTolerance(7) + # add some items + item1 = QgsLayoutItemMap(l) + item1.setId('xxyyxx') + l.addItem(item1) + item2 = QgsLayoutItemMap(l) + item2.setId('zzyyzz') + l.addItem(item2) + + l.setReferenceMap(item2) + doc = QDomDocument("testdoc") elem = l.writeXml(doc, QgsReadWriteContext()) - l2 = QgsLayout(p) + l2 = QgsPrintLayout(p) self.assertTrue(l2.readXml(elem, doc, QgsReadWriteContext())) self.assertEqual(l2.name(), 'my layout') self.assertEqual(l2.units(), QgsUnitTypes.LayoutInches) @@ -81,6 +110,281 @@ class TestQgsLayout(unittest.TestCase): self.assertEqual(l2.guides().guidesOnPage(0)[0].position().units(), QgsUnitTypes.LayoutCentimeters) self.assertEqual(l2.snapper().snapTolerance(), 7) + # check restored items + new_item1 = l2.itemByUuid(item1.uuid()) + self.assertTrue(new_item1) + self.assertEqual(new_item1.id(), 'xxyyxx') + new_item2 = l2.itemByUuid(item2.uuid()) + self.assertTrue(new_item2) + self.assertEqual(new_item2.id(), 'zzyyzz') + self.assertEqual(l2.referenceMap().id(), 'zzyyzz') + + def testAddItemsFromXml(self): + p = QgsProject() + l = QgsLayout(p) + + # add some items + item1 = QgsLayoutItemLabel(l) + item1.setId('xxyyxx') + item1.attemptMove(QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + item1.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + l.addItem(item1) + item2 = QgsLayoutItemLabel(l) + item2.setId('zzyyzz') + item2.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + item2.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + l.addItem(item2) + + doc = QDomDocument("testdoc") + # store in xml + elem = l.writeXml(doc, QgsReadWriteContext()) + + l2 = QgsLayout(p) + new_items = l2.addItemsFromXml(elem, doc, QgsReadWriteContext()) + self.assertEqual(len(new_items), 2) + items = l2.items() + self.assertTrue([i for i in items if i.id() == 'xxyyxx']) + self.assertTrue([i for i in items if i.id() == 'zzyyzz']) + self.assertTrue(new_items[0] in l2.items()) + self.assertTrue(new_items[1] in l2.items()) + new_item1 = [i for i in items if i.id() == 'xxyyxx'][0] + new_item2 = [i for i in items if i.id() == 'zzyyzz'][0] + self.assertEqual(new_item1.positionWithUnits(), QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item2.positionWithUnits(), QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + self.assertEqual(new_item2.sizeWithUnits(), QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + + # test with a group + group = QgsLayoutItemGroup(l) + group.addItem(item1) + group.addItem(item2) + l.addLayoutItem(group) + elem = l.writeXml(doc, QgsReadWriteContext()) + + l3 = QgsLayout(p) + new_items = l3.addItemsFromXml(elem, doc, QgsReadWriteContext()) + self.assertEqual(len(new_items), 3) + items = l3.items() + self.assertTrue([i for i in items if i.id() == 'xxyyxx']) + self.assertTrue([i for i in items if i.id() == 'zzyyzz']) + self.assertTrue(new_items[0] in l3.items()) + self.assertTrue(new_items[1] in l3.items()) + self.assertTrue(new_items[2] in l3.items()) + + # f*** you sip, I'll just manually cast + new_group = sip.cast(l3.itemByUuid(group.uuid()), QgsLayoutItemGroup) + self.assertIsNotNone(new_group) + other_items = [i for i in new_items if i.type() != new_group.type()] + self.assertCountEqual(new_group.items(), other_items) + + # test restoring at set position + l3 = QgsLayout(p) + new_items = l3.addItemsFromXml(elem, doc, QgsReadWriteContext(), QPointF(10, 30)) + self.assertEqual(len(new_items), 3) + items = l3.items() + new_item1 = [i for i in items if i.id() == 'xxyyxx'][0] + new_item2 = [i for i in items if i.id() == 'zzyyzz'][0] + self.assertEqual(new_item1.positionWithUnits(), QgsLayoutPoint(10, 30, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item2.positionWithUnits(), QgsLayoutPoint(2.0, 4.0, QgsUnitTypes.LayoutCentimeters)) + self.assertEqual(new_item2.sizeWithUnits(), QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + + # paste in place + l4 = QgsLayout(p) + page = QgsLayoutItemPage(l) + page.setPageSize('A3') + l4.pageCollection().addPage(page) + page = QgsLayoutItemPage(l) + page.setPageSize('A6') + l4.pageCollection().addPage(page) + + new_items = l4.addItemsFromXml(elem, doc, QgsReadWriteContext(), QPointF(10, 30), True) + self.assertEqual(len(new_items), 3) + new_item1 = [i for i in new_items if i.id() == 'xxyyxx'][0] + new_item2 = [i for i in new_items if i.id() == 'zzyyzz'][0] + self.assertEqual(new_item1.pagePositionWithUnits(), QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item1.page(), 0) + self.assertEqual(new_item2.pagePositionWithUnits(), QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + self.assertEqual(new_item2.sizeWithUnits(), QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + self.assertEqual(new_item2.page(), 0) + + # paste in place, page 2 + new_items = l4.addItemsFromXml(elem, doc, QgsReadWriteContext(), QPointF(10, 550), True) + self.assertEqual(len(new_items), 3) + new_item1 = [i for i in new_items if i.id() == 'xxyyxx'][0] + new_item2 = [i for i in new_items if i.id() == 'zzyyzz'][0] + self.assertEqual(new_item1.pagePositionWithUnits(), QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item1.page(), 1) + self.assertEqual(new_item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + self.assertEqual(new_item2.pagePositionWithUnits(), QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + self.assertEqual(new_item2.page(), 1) + self.assertEqual(new_item2.sizeWithUnits(), QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + + #TODO - test restoring multiframe + + def testSaveLoadTemplate(self): + tmpfile = os.path.join(self.basetestpath, 'testTemplate.qpt') + + p = QgsProject() + l = QgsLayout(p) + l.initializeDefaults() + + # add some items + item1 = QgsLayoutItemLabel(l) + item1.setId('xxyyxx') + item1.attemptMove(QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + item1.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + l.addItem(item1) + item2 = QgsLayoutItemLabel(l) + item2.setId('zzyyzz') + item2.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + item2.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + l.addItem(item2) + + # multiframe + multiframe1 = QgsLayoutItemHtml(l) + multiframe1.setHtml('mf1') + l.addMultiFrame(multiframe1) + frame1 = QgsLayoutFrame(l, multiframe1) + frame1.setId('frame1') + frame1.attemptMove(QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters)) + frame1.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters)) + multiframe1.addFrame(frame1) + + multiframe2 = QgsLayoutItemHtml(l) + multiframe2.setHtml('mf2') + l.addMultiFrame(multiframe2) + frame2 = QgsLayoutFrame(l, multiframe2) + frame2.setId('frame2') + frame2.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters)) + frame2.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters)) + multiframe2.addFrame(frame2) + + uuids = {item1.uuid(), item2.uuid(), frame1.uuid(), frame2.uuid(), multiframe1.uuid(), multiframe2.uuid()} + original_uuids = {item1.uuid(), item2.uuid(), frame1.uuid(), frame2.uuid()} + + self.assertTrue(l.saveAsTemplate(tmpfile, QgsReadWriteContext())) + + l2 = QgsLayout(p) + with open(tmpfile) as f: + template_content = f.read() + doc = QDomDocument() + doc.setContent(template_content) + + # adding to existing items + new_items, ok = l2.loadFromTemplate(doc, QgsReadWriteContext(), False) + self.assertTrue(ok) + self.assertEqual(len(new_items), 4) + items = l2.items() + multiframes = l2.multiFrames() + self.assertEqual(len(multiframes), 2) + self.assertTrue([i for i in items if i.id() == 'xxyyxx']) + self.assertTrue([i for i in items if i.id() == 'zzyyzz']) + self.assertTrue([i for i in items if i.id() == 'frame1']) + self.assertTrue([i for i in items if i.id() == 'frame2']) + self.assertTrue([i for i in multiframes if i.html() == 'mf1']) + self.assertTrue([i for i in multiframes if i.html() == 'mf2']) + self.assertTrue(new_items[0] in l2.items()) + self.assertTrue(new_items[1] in l2.items()) + self.assertTrue(new_items[2] in l2.items()) + self.assertTrue(new_items[3] in l2.items()) + + # double check that new items have a unique uid + self.assertNotIn(new_items[0].uuid(), uuids) + uuids.add(new_items[0].uuid()) + self.assertNotIn(new_items[1].uuid(), uuids) + uuids.add(new_items[1].uuid()) + self.assertNotIn(new_items[2].uuid(), uuids) + uuids.add(new_items[2].uuid()) + self.assertNotIn(new_items[3].uuid(), uuids) + uuids.add(new_items[3].uuid()) + + self.assertNotIn(multiframes[0].uuid(), [multiframe1.uuid(), multiframe2.uuid()]) + self.assertNotIn(multiframes[1].uuid(), [multiframe1.uuid(), multiframe2.uuid()]) + new_multiframe1 = [i for i in multiframes if i.html() == 'mf1'][0] + self.assertEqual(new_multiframe1.layout(), l2) + new_multiframe2 = [i for i in multiframes if i.html() == 'mf2'][0] + self.assertEqual(new_multiframe2.layout(), l2) + new_frame1 = sip.cast([i for i in items if i.id() == 'frame1'][0], QgsLayoutFrame) + new_frame2 = sip.cast([i for i in items if i.id() == 'frame2'][0], QgsLayoutFrame) + self.assertEqual(new_frame1.multiFrame(), new_multiframe1) + self.assertEqual(new_multiframe1.frames()[0].uuid(), new_frame1.uuid()) + self.assertEqual(new_frame2.multiFrame(), new_multiframe2) + self.assertEqual(new_multiframe2.frames()[0].uuid(), new_frame2.uuid()) + + # adding to existing items + new_items2, ok = l2.loadFromTemplate(doc, QgsReadWriteContext(), False) + self.assertTrue(ok) + self.assertEqual(len(new_items2), 4) + items = l2.items() + self.assertEqual(len(items), 8) + multiframes2 = l2.multiFrames() + self.assertEqual(len(multiframes2), 4) + multiframes2 = [m for m in l2.multiFrames() if not m.uuid() in [new_multiframe1.uuid(), new_multiframe2.uuid()]] + self.assertEqual(len(multiframes2), 2) + self.assertTrue([i for i in items if i.id() == 'xxyyxx']) + self.assertTrue([i for i in items if i.id() == 'zzyyzz']) + self.assertTrue([i for i in items if i.id() == 'frame1']) + self.assertTrue([i for i in items if i.id() == 'frame2']) + self.assertTrue([i for i in multiframes2 if i.html() == 'mf1']) + self.assertTrue([i for i in multiframes2 if i.html() == 'mf2']) + self.assertTrue(new_items[0] in l2.items()) + self.assertTrue(new_items[1] in l2.items()) + self.assertTrue(new_items[2] in l2.items()) + self.assertTrue(new_items[3] in l2.items()) + self.assertTrue(new_items2[0] in l2.items()) + self.assertTrue(new_items2[1] in l2.items()) + self.assertTrue(new_items2[2] in l2.items()) + self.assertTrue(new_items2[3] in l2.items()) + self.assertNotIn(new_items2[0].uuid(), uuids) + uuids.add(new_items[0].uuid()) + self.assertNotIn(new_items2[1].uuid(), uuids) + uuids.add(new_items[1].uuid()) + self.assertNotIn(new_items2[2].uuid(), uuids) + uuids.add(new_items[2].uuid()) + self.assertNotIn(new_items2[3].uuid(), uuids) + uuids.add(new_items[3].uuid()) + + self.assertNotIn(multiframes2[0].uuid(), [multiframe1.uuid(), multiframe2.uuid(), new_multiframe1.uuid(), new_multiframe2.uuid()]) + self.assertNotIn(multiframes2[1].uuid(), [multiframe1.uuid(), multiframe2.uuid(), new_multiframe1.uuid(), new_multiframe2.uuid()]) + + new_multiframe1b = [i for i in multiframes2 if i.html() == 'mf1'][0] + self.assertEqual(new_multiframe1b.layout(), l2) + new_multiframe2b = [i for i in multiframes2 if i.html() == 'mf2'][0] + self.assertEqual(new_multiframe2b.layout(), l2) + new_frame1b = sip.cast([i for i in items if i.id() == 'frame1' and i.uuid() != new_frame1.uuid()][0], QgsLayoutFrame) + new_frame2b = sip.cast([i for i in items if i.id() == 'frame2' and i.uuid() != new_frame2.uuid()][0], QgsLayoutFrame) + self.assertEqual(new_frame1b.multiFrame(), new_multiframe1b) + self.assertEqual(new_multiframe1b.frames()[0].uuid(), new_frame1b.uuid()) + self.assertEqual(new_frame2b.multiFrame(), new_multiframe2b) + self.assertEqual(new_multiframe2b.frames()[0].uuid(), new_frame2b.uuid()) + + # clearing existing items + new_items3, ok = l2.loadFromTemplate(doc, QgsReadWriteContext(), True) + new_multiframes = l2.multiFrames() + self.assertTrue(ok) + self.assertEqual(len(new_items3), 5) # includes page + self.assertEqual(len(new_multiframes), 2) + items = l2.items() + self.assertTrue([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'xxyyxx']) + self.assertTrue([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'zzyyzz']) + self.assertTrue([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'frame1']) + self.assertTrue([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'frame2']) + self.assertTrue(new_items3[0] in l2.items()) + self.assertTrue(new_items3[1] in l2.items()) + self.assertTrue(new_items3[2] in l2.items()) + self.assertTrue(new_items3[3] in l2.items()) + new_multiframe1 = [i for i in new_multiframes if i.html() == 'mf1'][0] + new_multiframe2 = [i for i in new_multiframes if i.html() == 'mf2'][0] + + new_frame1 = sip.cast([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'frame1'][0], QgsLayoutFrame) + new_frame2 = sip.cast([i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'frame2'][0], QgsLayoutFrame) + self.assertEqual(new_frame1.multiFrame(), new_multiframe1) + self.assertEqual(new_multiframe1.frames()[0].uuid(), new_frame1.uuid()) + self.assertEqual(new_frame2.multiFrame(), new_multiframe2) + self.assertEqual(new_multiframe2.frames()[0].uuid(), new_frame2.uuid()) + def testSelectedItems(self): p = QgsProject() l = QgsLayout(p) diff --git a/tests/src/python/test_qgslayoutatlas.py b/tests/src/python/test_qgslayoutatlas.py new file mode 100644 index 00000000000..6154bc8a8a1 --- /dev/null +++ b/tests/src/python/test_qgslayoutatlas.py @@ -0,0 +1,617 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutAtlas + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '19/12/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA +import sip +import tempfile +import shutil +import os +import glob + +from qgis.core import (QgsUnitTypes, + QgsFeature, + QgsLayout, + QgsPrintLayout, + QgsLayoutAtlas, + QgsLayoutItemPage, + QgsLayoutGuide, + QgsLayoutObject, + QgsProject, + QgsLayoutItemGroup, + QgsLayoutItem, + QgsProperty, + QgsLayoutPageCollection, + QgsLayoutMeasurement, + QgsFillSymbol, + QgsReadWriteContext, + QgsLayoutItemMap, + QgsLayoutItemLabel, + QgsLayoutSize, + QgsLayoutPoint, + QgsVectorLayer, + QgsRectangle, + QgsCoordinateReferenceSystem, + QgsSingleSymbolRenderer, + QgsLayoutItemLabel, + QgsFontUtils, + QgsFeature, + QgsGeometry, + QgsPointXY, + QgsCategorizedSymbolRenderer, + QgsRendererCategory, + QgsMarkerSymbol, + QgsLayoutItemLegend) +from qgis.PyQt.QtCore import QFileInfo, QRectF, QDir +from qgis.PyQt.QtTest import QSignalSpy +from qgis.PyQt.QtXml import QDomDocument +from utilities import unitTestDataPath +from qgis.testing import start_app, unittest +from qgis.PyQt.QtTest import QSignalSpy + +from qgslayoutchecker import QgsLayoutChecker + +start_app() + + +class TestQgsLayoutAtlas(unittest.TestCase): + + def setUp(self): + self.report = "

Python QgsLayoutAtlas Tests

\n" + + def tearDown(self): + report_file_path = "%s/qgistest.html" % QDir.tempPath() + with open(report_file_path, 'a') as report_file: + report_file.write(self.report) + + def testCase(self): + self.TEST_DATA_DIR = unitTestDataPath() + tmppath = tempfile.mkdtemp() + for file in glob.glob(os.path.join(self.TEST_DATA_DIR, 'france_parts.*')): + shutil.copy(os.path.join(self.TEST_DATA_DIR, file), tmppath) + vectorFileInfo = QFileInfo(tmppath + "/france_parts.shp") + mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + + QgsProject.instance().addMapLayers([mVectorLayer]) + self.layers = [mVectorLayer] + + # create layout with layout map + + # select epsg:2154 + crs = QgsCoordinateReferenceSystem() + crs.createFromSrid(2154) + QgsProject.instance().setCrs(crs) + + self.layout = QgsPrintLayout(QgsProject.instance()) + self.layout.initializeDefaults() + + # fix the renderer, fill with green + props = {"color": "0,127,0"} + fillSymbol = QgsFillSymbol.createSimple(props) + renderer = QgsSingleSymbolRenderer(fillSymbol) + mVectorLayer.setRenderer(renderer) + + # the atlas map + self.atlas_map = QgsLayoutItemMap(self.layout) + self.atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130)) + self.atlas_map.setFrameEnabled(True) + self.atlas_map.setLayers([mVectorLayer]) + self.layout.addLayoutItem(self.atlas_map) + + # the atlas + self.atlas = self.layout.atlas() + self.atlas.setCoverageLayer(mVectorLayer) + self.atlas.setEnabled(True) + + # an overview + self.overview = QgsLayoutItemMap(self.layout) + self.overview.attemptSetSceneRect(QRectF(180, 20, 50, 50)) + self.overview.setFrameEnabled(True) + self.overview.overview().setLinkedMap(self.atlas_map) + self.overview.setLayers([mVectorLayer]) + self.layout.addLayoutItem(self.overview) + nextent = QgsRectangle(49670.718, 6415139.086, 699672.519, 7065140.887) + self.overview.setExtent(nextent) + + # set the fill symbol of the overview map + props2 = {"color": "127,0,0,127"} + fillSymbol2 = QgsFillSymbol.createSimple(props2) + self.overview.overview().setFrameSymbol(fillSymbol2) + + # header label + self.mLabel1 = QgsLayoutItemLabel(self.layout) + self.layout.addLayoutItem(self.mLabel1) + self.mLabel1.setText("[% \"NAME_1\" %] area") + self.mLabel1.setFont(QgsFontUtils.getStandardTestFont()) + self.mLabel1.adjustSizeToText() + self.mLabel1.attemptSetSceneRect(QRectF(150, 5, 60, 15)) + self.mLabel1.setMarginX(1) + self.mLabel1.setMarginY(1) + + # feature number label + self.mLabel2 = QgsLayoutItemLabel(self.layout) + self.layout.addLayoutItem(self.mLabel2) + self.mLabel2.setText("# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]") + self.mLabel2.setFont(QgsFontUtils.getStandardTestFont()) + self.mLabel2.adjustSizeToText() + self.mLabel2.attemptSetSceneRect(QRectF(150, 200, 60, 15)) + self.mLabel2.setMarginX(1) + self.mLabel2.setMarginY(1) + + self.filename_test() + self.autoscale_render_test() + self.fixedscale_render_test() + self.predefinedscales_render_test() + self.hidden_render_test() + self.legend_test() + self.rotation_test() + + shutil.rmtree(tmppath, True) + + def testReadWriteXml(self): + p = QgsProject() + vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp") + vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + self.assertTrue(vector_layer.isValid()) + p.addMapLayer(vector_layer) + + l = QgsPrintLayout(p) + atlas = l.atlas() + atlas.setEnabled(True) + atlas.setHideCoverage(True) + atlas.setFilenameExpression('filename exp') + atlas.setCoverageLayer(vector_layer) + atlas.setPageNameExpression('page name') + atlas.setSortFeatures(True) + atlas.setSortAscending(False) + atlas.setSortExpression('sort exp') + atlas.setFilterFeatures(True) + atlas.setFilterExpression('filter exp') + + doc = QDomDocument("testdoc") + elem = l.writeXml(doc, QgsReadWriteContext()) + + l2 = QgsPrintLayout(p) + self.assertTrue(l2.readXml(elem, doc, QgsReadWriteContext())) + atlas2 = l2.atlas() + self.assertTrue(atlas2.enabled()) + self.assertTrue(atlas2.hideCoverage()) + self.assertEqual(atlas2.filenameExpression(), 'filename exp') + self.assertEqual(atlas2.coverageLayer(), vector_layer) + self.assertEqual(atlas2.pageNameExpression(), 'page name') + self.assertTrue(atlas2.sortFeatures()) + self.assertFalse(atlas2.sortAscending()) + self.assertEqual(atlas2.sortExpression(), 'sort exp') + self.assertTrue(atlas2.filterFeatures()) + self.assertEqual(atlas2.filterExpression(), 'filter exp') + + def testIteration(self): + p = QgsProject() + vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp") + vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + self.assertTrue(vector_layer.isValid()) + p.addMapLayer(vector_layer) + + l = QgsPrintLayout(p) + atlas = l.atlas() + atlas.setEnabled(True) + atlas.setCoverageLayer(vector_layer) + + atlas_feature_changed_spy = QSignalSpy(atlas.featureChanged) + context_changed_spy = QSignalSpy(l.reportContext().changed) + + self.assertTrue(atlas.beginRender()) + self.assertTrue(atlas.first()) + self.assertEqual(len(atlas_feature_changed_spy), 1) + self.assertEqual(len(context_changed_spy), 1) + self.assertEqual(atlas.currentFeatureNumber(), 0) + self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie') + self.assertEqual(l.reportContext().layer(), vector_layer) + f1 = l.reportContext().feature() + + self.assertTrue(atlas.next()) + self.assertEqual(len(atlas_feature_changed_spy), 2) + self.assertEqual(len(context_changed_spy), 2) + self.assertEqual(atlas.currentFeatureNumber(), 1) + self.assertEqual(l.reportContext().feature()[4], 'Bretagne') + f2 = l.reportContext().feature() + + self.assertTrue(atlas.next()) + self.assertEqual(len(atlas_feature_changed_spy), 3) + self.assertEqual(len(context_changed_spy), 3) + self.assertEqual(atlas.currentFeatureNumber(), 2) + self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire') + f3 = l.reportContext().feature() + + self.assertTrue(atlas.next()) + self.assertEqual(len(atlas_feature_changed_spy), 4) + self.assertEqual(len(context_changed_spy), 4) + self.assertEqual(atlas.currentFeatureNumber(), 3) + self.assertEqual(l.reportContext().feature()[4], 'Centre') + f4 = l.reportContext().feature() + + self.assertFalse(atlas.next()) + self.assertTrue(atlas.seekTo(2)) + self.assertEqual(len(atlas_feature_changed_spy), 5) + self.assertEqual(len(context_changed_spy), 5) + self.assertEqual(atlas.currentFeatureNumber(), 2) + self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire') + + self.assertTrue(atlas.last()) + self.assertEqual(len(atlas_feature_changed_spy), 6) + self.assertEqual(len(context_changed_spy), 6) + self.assertEqual(atlas.currentFeatureNumber(), 3) + self.assertEqual(l.reportContext().feature()[4], 'Centre') + + self.assertTrue(atlas.previous()) + self.assertEqual(len(atlas_feature_changed_spy), 7) + self.assertEqual(len(context_changed_spy), 7) + self.assertEqual(atlas.currentFeatureNumber(), 2) + self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire') + + self.assertTrue(atlas.previous()) + self.assertTrue(atlas.previous()) + self.assertEqual(len(atlas_feature_changed_spy), 9) + self.assertFalse(atlas.previous()) + self.assertEqual(len(atlas_feature_changed_spy), 9) + + self.assertTrue(atlas.endRender()) + self.assertEqual(len(atlas_feature_changed_spy), 10) + + self.assertTrue(atlas.seekTo(f1)) + self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie') + self.assertTrue(atlas.seekTo(f4)) + self.assertEqual(l.reportContext().feature()[4], 'Centre') + self.assertTrue(atlas.seekTo(f3)) + self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire') + self.assertTrue(atlas.seekTo(f2)) + self.assertEqual(l.reportContext().feature()[4], 'Bretagne') + self.assertFalse(atlas.seekTo(QgsFeature(5))) + + def testUpdateFeature(self): + p = QgsProject() + vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp") + vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + self.assertTrue(vector_layer.isValid()) + p.addMapLayer(vector_layer) + + l = QgsPrintLayout(p) + atlas = l.atlas() + atlas.setEnabled(True) + atlas.setCoverageLayer(vector_layer) + + self.assertTrue(atlas.beginRender()) + self.assertTrue(atlas.first()) + self.assertEqual(atlas.currentFeatureNumber(), 0) + self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie') + self.assertEqual(l.reportContext().layer(), vector_layer) + + vector_layer.startEditing() + self.assertTrue(vector_layer.changeAttributeValue(l.reportContext().feature().id(), 4, 'Nah, Canberra mate!')) + self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie') + l.atlas().refreshCurrentFeature() + self.assertEqual(l.reportContext().feature()[4], 'Nah, Canberra mate!') + vector_layer.rollBack() + + def testFileName(self): + p = QgsProject() + vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp") + vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + self.assertTrue(vector_layer.isValid()) + p.addMapLayer(vector_layer) + + l = QgsPrintLayout(p) + atlas = l.atlas() + atlas.setEnabled(True) + atlas.setCoverageLayer(vector_layer) + atlas.setFilenameExpression("'output_' || \"NAME_1\"") + + self.assertTrue(atlas.beginRender()) + self.assertEqual(atlas.count(), 4) + atlas.first() + self.assertEqual(atlas.currentFilename(), 'output_Basse-Normandie') + self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Basse-Normandie.png') + self.assertEqual(atlas.filePath('/tmp/output/', '.png'), '/tmp/output/output_Basse-Normandie.png') + self.assertEqual(atlas.filePath('/tmp/output/', 'svg'), '/tmp/output/output_Basse-Normandie.svg') + + atlas.next() + self.assertEqual(atlas.currentFilename(), 'output_Bretagne') + self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Bretagne.png') + atlas.next() + self.assertEqual(atlas.currentFilename(), 'output_Pays de la Loire') + self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Pays de la Loire.png') + atlas.next() + self.assertEqual(atlas.currentFilename(), 'output_Centre') + self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Centre.png') + + # try changing expression, filename should be updated instantly + atlas.setFilenameExpression("'export_' || \"NAME_1\"") + self.assertEqual(atlas.currentFilename(), 'export_Centre') + + atlas.endRender() + + def testNameForPage(self): + p = QgsProject() + vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp") + vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") + self.assertTrue(vector_layer.isValid()) + p.addMapLayer(vector_layer) + + l = QgsPrintLayout(p) + atlas = l.atlas() + atlas.setEnabled(True) + atlas.setCoverageLayer(vector_layer) + atlas.setPageNameExpression("\"NAME_1\"") + + self.assertTrue(atlas.beginRender()) + self.assertEqual(atlas.nameForPage(0), 'Basse-Normandie') + self.assertEqual(atlas.nameForPage(1), 'Bretagne') + self.assertEqual(atlas.nameForPage(2), 'Pays de la Loire') + self.assertEqual(atlas.nameForPage(3), 'Centre') + + def filename_test(self): + self.atlas.setFilenameExpression("'output_' || @atlas_featurenumber") + self.atlas.beginRender() + for i in range(0, self.atlas.count()): + self.atlas.seekTo(i) + expected = "output_%d" % (i + 1) + self.assertEqual(self.atlas.currentFilename(), expected) + self.atlas.endRender() + + def autoscale_render_test(self): + self.atlas_map.setExtent( + QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338)) + + self.atlas_map.setAtlasDriven(True) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) + self.atlas_map.setAtlasMargin(0.10) + + self.atlas.beginRender() + + for i in range(0, 2): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_autoscale%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + self.atlas_map.setAtlasDriven(False) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed) + self.atlas_map.setAtlasMargin(0) + + def fixedscale_render_test(self): + self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) + self.atlas_map.setAtlasDriven(True) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed) + + self.atlas.beginRender() + + for i in range(0, 2): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_fixedscale%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + def predefinedscales_render_test(self): + self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) + self.atlas_map.setAtlasDriven(True) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Predefined) + + scales = [1800000, 5000000] + self.layout.reportContext().setPredefinedScales(scales) + for i, s in enumerate(self.layout.reportContext().predefinedScales()): + self.assertEqual(s, scales[i]) + + self.atlas.beginRender() + + for i in range(0, 2): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_predefinedscales%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + def hidden_render_test(self): + self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed) + self.atlas.setHideCoverage(True) + + self.atlas.beginRender() + + for i in range(0, 2): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_hiding%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + self.atlas.setHideCoverage(False) + + def sorting_render_test(self): + self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed) + self.atlas.setHideCoverage(False) + + self.atlas.setSortFeatures(True) + self.atlas.setSortKeyAttributeIndex(4) # departement name + self.atlas.setSortAscending(False) + + self.atlas.beginRender() + + for i in range(0, 2): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_sorting%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + def filtering_render_test(self): + self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed) + self.atlas.setHideCoverage(False) + + self.atlas.setSortFeatures(False) + + self.atlas.setFilterFeatures(True) + self.atlas.setFeatureFilter("substr(NAME_1,1,1)='P'") # select only 'Pays de la loire' + + self.atlas.beginRender() + + for i in range(0, 1): + self.atlas.seekTo(i) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_filtering%d' % (i + 1), self.layout) + checker.setControlPathPrefix("atlas") + myTestResult, myMessage = checker.testLayout(0, 200) + self.report += checker.report() + + self.assertTrue(myTestResult, myMessage) + self.atlas.endRender() + + def legend_test(self): + self.atlas_map.setAtlasDriven(True) + self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) + self.atlas_map.setAtlasMargin(0.10) + + # add a point layer + ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory") + + pr = ptLayer.dataProvider() + f1 = QgsFeature(1) + f1.initAttributes(2) + f1.setAttribute(0, 1) + f1.setAttribute(1, "Test label 1") + f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-0.638, 48.954))) + f2 = QgsFeature(2) + f2.initAttributes(2) + f2.setAttribute(0, 2) + f2.setAttribute(1, "Test label 2") + f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1.682, 48.550))) + pr.addFeatures([f1, f2]) + + # categorized symbology + r = QgsCategorizedSymbolRenderer("attr", [QgsRendererCategory(1, QgsMarkerSymbol.createSimple({"color": "255,0,0"}), "red"), + QgsRendererCategory(2, QgsMarkerSymbol.createSimple({"color": "0,0,255"}), "blue")]) + ptLayer.setRenderer(r) + + QgsProject.instance().addMapLayer(ptLayer) + + # add the point layer to the map settings + layers = self.layers + layers = [ptLayer] + layers + self.atlas_map.setLayers(layers) + self.overview.setLayers(layers) + + # add a legend + legend = QgsLayoutItemLegend(self.layout) + legend.attemptMove(QgsLayoutPoint(200, 100)) + # sets the legend filter parameter + legend.setLinkedMap(self.atlas_map) + legend.setLegendFilterOutAtlas(True) + self.layout.addLayoutItem(legend) + + self.atlas.beginRender() + + self.atlas.seekTo(0) + self.mLabel1.adjustSizeToText() + + checker = QgsLayoutChecker('atlas_legend', self.layout) + myTestResult, myMessage = checker.testLayout() + self.report += checker.report() + self.assertTrue(myTestResult, myMessage) + + self.atlas.endRender() + + # restore state + self.atlas_map.setLayers([layers[1]]) + self.layout.removeLayoutItem(legend) + QgsProject.instance().removeMapLayer(ptLayer.id()) + + def rotation_test(self): + # We will create a polygon layer with a rotated rectangle. + # Then we will make it the object layer for the atlas, + # rotate the map and test that the bounding rectangle + # is smaller than the bounds without rotation. + polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory') + poly = QgsFeature(polygonLayer.fields()) + points = [(10, 15), (15, 10), (45, 40), (40, 45)] + poly.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(x[0], x[1]) for x in points]])) + polygonLayer.dataProvider().addFeatures([poly]) + QgsProject.instance().addMapLayer(polygonLayer) + + # Recreating the layout locally + composition = QgsPrintLayout(QgsProject.instance()) + composition.initializeDefaults() + + # the atlas map + atlasMap = QgsLayoutItemMap(composition) + atlasMap.attemptSetSceneRect(QRectF(20, 20, 130, 130)) + atlasMap.setFrameEnabled(True) + atlasMap.setLayers([polygonLayer]) + atlasMap.setExtent(QgsRectangle(0, 0, 100, 50)) + composition.addLayoutItem(atlasMap) + + # the atlas + atlas = composition.atlas() + atlas.setCoverageLayer(polygonLayer) + atlas.setEnabled(True) + + atlasMap.setAtlasDriven(True) + atlasMap.setAtlasScalingMode(QgsLayoutItemMap.Auto) + atlasMap.setAtlasMargin(0.0) + + # Testing + atlasMap.setMapRotation(0.0) + atlas.beginRender() + atlas.first() + nonRotatedExtent = QgsRectangle(atlasMap.extent()) + + atlasMap.setMapRotation(45.0) + atlas.first() + rotatedExtent = QgsRectangle(atlasMap.extent()) + + self.assertLess(rotatedExtent.width(), nonRotatedExtent.width() * 0.9) + self.assertLess(rotatedExtent.height(), nonRotatedExtent.height() * 0.9) + + QgsProject.instance().removeMapLayer(polygonLayer) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutexporter.py b/tests/src/python/test_qgslayoutexporter.py new file mode 100644 index 00000000000..25c0e635c8e --- /dev/null +++ b/tests/src/python/test_qgslayoutexporter.py @@ -0,0 +1,871 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutExporter + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '11/12/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA +import sip +import tempfile +import shutil +import os +import subprocess + +from qgis.core import (QgsMultiRenderChecker, + QgsLayoutExporter, + QgsLayout, + QgsProject, + QgsMargins, + QgsLayoutItemShape, + QgsLayoutGuide, + QgsRectangle, + QgsLayoutItemPage, + QgsLayoutItemMap, + QgsLayoutPoint, + QgsLayoutMeasurement, + QgsUnitTypes, + QgsSimpleFillSymbolLayer, + QgsFillSymbol, + QgsVectorLayer, + QgsCoordinateReferenceSystem, + QgsPrintLayout, + QgsSingleSymbolRenderer, + QgsReport) +from qgis.PyQt.QtCore import QSize, QSizeF, QDir, QRectF, Qt +from qgis.PyQt.QtGui import QImage, QPainter +from qgis.PyQt.QtPrintSupport import QPrinter +from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator + +from qgis.testing import start_app, unittest + +from utilities import getExecutablePath, unitTestDataPath + +TEST_DATA_DIR = unitTestDataPath() + +# PDF-to-image utility +# look for Poppler w/ Cairo, then muPDF +# * Poppler w/ Cairo renders correctly +# * Poppler w/o Cairo does not always correctly render vectors in PDF to image +# * muPDF renders correctly, but sightly shifts colors +for util in [ + 'pdftocairo', + # 'mudraw', +]: + PDFUTIL = getExecutablePath(util) + if PDFUTIL: + break + +# noinspection PyUnboundLocalVariable +if not PDFUTIL: + raise Exception('PDF-to-image utility not found on PATH: ' + 'install Poppler (with Cairo)') + + +def pdfToPng(pdf_file_path, rendered_file_path, page, dpi=96): + if PDFUTIL.strip().endswith('pdftocairo'): + filebase = os.path.join( + os.path.dirname(rendered_file_path), + os.path.splitext(os.path.basename(rendered_file_path))[0] + ) + call = [ + PDFUTIL, '-png', '-singlefile', '-r', str(dpi), + '-x', '0', '-y', '0', '-f', str(page), '-l', str(page), + pdf_file_path, filebase + ] + elif PDFUTIL.strip().endswith('mudraw'): + call = [ + PDFUTIL, '-c', 'rgba', + '-r', str(dpi), '-f', str(page), '-l', str(page), + # '-b', '8', + '-o', rendered_file_path, pdf_file_path + ] + else: + return False, '' + + print("exportToPdf call: {0}".format(' '.join(call))) + try: + subprocess.check_call(call) + except subprocess.CalledProcessError as e: + assert False, ("exportToPdf failed!\n" + "cmd: {0}\n" + "returncode: {1}\n" + "message: {2}".format(e.cmd, e.returncode, e.message)) + + +def svgToPng(svg_file_path, rendered_file_path, width): + svgr = QSvgRenderer(svg_file_path) + + height = width / svgr.viewBoxF().width() * svgr.viewBoxF().height() + + image = QImage(width, height, QImage.Format_ARGB32) + image.fill(Qt.transparent) + + p = QPainter(image) + p.setRenderHint(QPainter.Antialiasing, False) + svgr.render(p) + p.end() + + res = image.save(rendered_file_path, 'png') + if not res: + os.unlink(rendered_file_path) + + +start_app() + + +class TestQgsLayoutExporter(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """Run before all tests""" + cls.basetestpath = tempfile.mkdtemp() + cls.dots_per_meter = 96 / 25.4 * 1000 + + def setUp(self): + self.report = "

Python QgsLayoutExporter Tests

\n" + + def tearDown(self): + report_file_path = "%s/qgistest.html" % QDir.tempPath() + with open(report_file_path, 'a') as report_file: + report_file.write(self.report) + + def checkImage(self, name, reference_image, rendered_image, size_tolerance=0): + checker = QgsMultiRenderChecker() + checker.setControlPathPrefix("layout_exporter") + checker.setControlName("expected_layoutexporter_" + reference_image) + checker.setRenderedImage(rendered_image) + checker.setColorTolerance(2) + checker.setSizeTolerance(size_tolerance, size_tolerance) + result = checker.runTest(name, 20) + self.report += checker.report() + print((self.report)) + return result + + def testRenderPage(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + # get width/height, create image and render the composition to it + size = QSize(1122, 794) + output_image = QImage(size, QImage.Format_RGB32) + + output_image.setDotsPerMeterX(self.dots_per_meter) + output_image.setDotsPerMeterY(self.dots_per_meter) + QgsMultiRenderChecker.drawBackground(output_image) + painter = QPainter(output_image) + exporter = QgsLayoutExporter(l) + + # valid page + exporter.renderPage(painter, 0) + painter.end() + + rendered_file_path = os.path.join(self.basetestpath, 'test_renderpage.png') + output_image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('renderpage', 'renderpage', rendered_file_path)) + + def testRenderPageToImage(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + exporter = QgsLayoutExporter(l) + size = QSize(1122, 794) + + # bad page numbers + image = exporter.renderPageToImage(-1, size) + self.assertTrue(image.isNull()) + image = exporter.renderPageToImage(1, size) + self.assertTrue(image.isNull()) + + # good page + image = exporter.renderPageToImage(0, size) + self.assertFalse(image.isNull()) + + rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimagepage.png') + image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('rendertoimagepage', 'rendertoimagepage', rendered_file_path)) + + def testRenderRegion(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add a guide, to ensure it is not included in export + g1 = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(15, QgsUnitTypes.LayoutMillimeters), l.pageCollection().page(0)) + l.guides().addGuide(g1) + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + # get width/height, create image and render the composition to it + size = QSize(560, 509) + output_image = QImage(size, QImage.Format_RGB32) + + output_image.setDotsPerMeterX(self.dots_per_meter) + output_image.setDotsPerMeterY(self.dots_per_meter) + QgsMultiRenderChecker.drawBackground(output_image) + painter = QPainter(output_image) + exporter = QgsLayoutExporter(l) + + exporter.renderRegion(painter, QRectF(5, 10, 110, 100)) + painter.end() + + rendered_file_path = os.path.join(self.basetestpath, 'test_renderregion.png') + output_image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('renderregion', 'renderregion', rendered_file_path)) + + def testRenderRegionToImage(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + exporter = QgsLayoutExporter(l) + size = QSize(560, 509) + + image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), size) + self.assertFalse(image.isNull()) + + rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionsize.png') + image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('rendertoimageregionsize', 'rendertoimageregionsize', rendered_file_path)) + + # using layout dpi + l.renderContext().setDpi(40) + image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100)) + self.assertFalse(image.isNull()) + + rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregiondpi.png') + image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('rendertoimageregiondpi', 'rendertoimageregiondpi', rendered_file_path)) + + # overriding dpi + image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), QSize(), 80) + self.assertFalse(image.isNull()) + + rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionoverridedpi.png') + image.save(rendered_file_path, "PNG") + self.assertTrue(self.checkImage('rendertoimageregionoverridedpi', 'rendertoimageregionoverridedpi', rendered_file_path)) + + def testExportToImage(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + item2 = QgsLayoutItemShape(l) + item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + item2.attemptMove(QgsLayoutPoint(10, 20), page=1) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.cyan) + fill.setStrokeStyle(Qt.NoPen) + item2.setSymbol(fill_symbol) + l.addItem(item2) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.ImageExportSettings() + settings.dpi = 80 + + rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi.png') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + + self.assertTrue(self.checkImage('exporttoimagedpi_page1', 'exporttoimagedpi_page1', rendered_file_path)) + page2_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi_2.png') + self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) + + # crop to contents + settings.cropToContents = True + settings.cropMargins = QgsMargins(10, 20, 30, 40) + + rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped.png') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + + self.assertTrue(self.checkImage('exporttoimagecropped_page1', 'exporttoimagecropped_page1', rendered_file_path)) + page2_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped_2.png') + self.assertTrue(self.checkImage('exporttoimagecropped_page2', 'exporttoimagecropped_page2', page2_path)) + + # specific pages + settings.cropToContents = False + settings.pages = [1] + + rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagepages.png') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + + self.assertFalse(os.path.exists(rendered_file_path)) + page2_path = os.path.join(self.basetestpath, 'test_exporttoimagepages_2.png') + self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) + + # image size + settings.imageSize = QSize(600, 851) + + rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagesize.png') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertFalse(os.path.exists(rendered_file_path)) + page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesize_2.png') + self.assertTrue(self.checkImage('exporttoimagesize_page2', 'exporttoimagesize_page2', page2_path)) + + def testExportToPdf(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + item2 = QgsLayoutItemShape(l) + item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + item2.attemptMove(QgsLayoutPoint(10, 20), page=1) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.cyan) + fill.setStrokeStyle(Qt.NoPen) + item2.setSymbol(fill_symbol) + l.addItem(item2) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.PdfExportSettings() + settings.dpi = 80 + settings.rasterizeWholeImage = False + settings.forceVectorOutput = False + + pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdfdpi.pdf') + self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(pdf_file_path)) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png') + dpi = 80 + pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) + rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png') + pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2) + + self.assertTrue(self.checkImage('exporttopdfdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) + self.assertTrue(self.checkImage('exporttopdfdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) + + def testExportToSvg(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + item2 = QgsLayoutItemShape(l) + item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + item2.attemptMove(QgsLayoutPoint(10, 20), page=1) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.cyan) + fill.setStrokeStyle(Qt.NoPen) + item2.setSymbol(fill_symbol) + l.addItem(item2) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.SvgExportSettings() + settings.dpi = 80 + settings.forceVectorOutput = False + + svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgdpi.svg') + svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi_2.svg') + self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(svg_file_path)) + self.assertTrue(os.path.exists(svg_file_path_2)) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvgdpi.png') + svgToPng(svg_file_path, rendered_page_1, width=936) + rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi2.png') + svgToPng(svg_file_path_2, rendered_page_2, width=467) + + self.assertTrue(self.checkImage('exporttosvgdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) + self.assertTrue(self.checkImage('exporttosvgdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) + + # layered + settings.exportAsLayers = True + + svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvglayered.svg') + svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered_2.svg') + self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(svg_file_path)) + self.assertTrue(os.path.exists(svg_file_path_2)) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvglayered.png') + svgToPng(svg_file_path, rendered_page_1, width=936) + rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered2.png') + svgToPng(svg_file_path_2, rendered_page_2, width=467) + + self.assertTrue(self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) + self.assertTrue(self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) + + def testPrint(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + + # add some items + item1 = QgsLayoutItemShape(l) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + l.addItem(item1) + + item2 = QgsLayoutItemShape(l) + item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + item2.attemptMove(QgsLayoutPoint(10, 20), page=1) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.cyan) + fill.setStrokeStyle(Qt.NoPen) + item2.setSymbol(fill_symbol) + l.addItem(item2) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.PrintExportSettings() + settings.dpi = 80 + settings.rasterizeWholeImage = False + + pdf_file_path = os.path.join(self.basetestpath, 'test_printdpi.pdf') + # make a qprinter directed to pdf + printer = QPrinter() + printer.setOutputFileName(pdf_file_path) + printer.setOutputFormat(QPrinter.PdfFormat) + + self.assertEqual(exporter.print(printer, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(pdf_file_path)) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png') + dpi = 80 + pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) + rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png') + pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2) + + self.assertTrue(self.checkImage('printdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) + self.assertTrue(self.checkImage('printdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) + + def testExportWorldFile(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + + # add some items + map = QgsLayoutItemMap(l) + map.attemptSetSceneRect(QRectF(30, 60, 200, 100)) + extent = QgsRectangle(2000, 2800, 2500, 2900) + map.setExtent(extent) + l.addLayoutItem(map) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.ImageExportSettings() + settings.dpi = 80 + settings.generateWorldFile = False + + rendered_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.png') + world_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.pgw') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(rendered_file_path)) + self.assertFalse(os.path.exists(world_file_path)) + + # with world file + settings.generateWorldFile = True + rendered_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.png') + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(rendered_file_path)) + self.assertTrue(os.path.exists(world_file_path)) + + lines = tuple(open(world_file_path, 'r')) + values = [float(f) for f in lines] + self.assertAlmostEqual(values[0], 0.794117647059, 2) + self.assertAlmostEqual(values[1], 0.0, 2) + self.assertAlmostEqual(values[2], 0.0, 2) + self.assertAlmostEqual(values[3], -0.794251134644, 2) + self.assertAlmostEqual(values[4], 1925.000000000000, 2) + self.assertAlmostEqual(values[5], 3050.000000000000, 2) + + def testExcludePagesImage(self): + l = QgsLayout(QgsProject.instance()) + l.initializeDefaults() + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + + exporter = QgsLayoutExporter(l) + # setup settings + settings = QgsLayoutExporter.ImageExportSettings() + settings.dpi = 80 + settings.generateWorldFile = False + + rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export.png') + details = QgsLayoutExporter.PageExportDetails() + details.directory = self.basetestpath + details.baseName = 'test_exclude_export' + details.extension = 'png' + details.page = 0 + + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertTrue(os.path.exists(exporter.generateFileName(details))) + details.page = 1 + self.assertTrue(os.path.exists(exporter.generateFileName(details))) + + # exclude a page + l.pageCollection().page(0).setExcludeFromExports(True) + rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded.png') + details.baseName = 'test_exclude_export_excluded' + details.page = 0 + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertFalse(os.path.exists(exporter.generateFileName(details))) + details.page = 1 + self.assertTrue(os.path.exists(exporter.generateFileName(details))) + + # exclude second page + l.pageCollection().page(1).setExcludeFromExports(True) + rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded_all.png') + details.baseName = 'test_exclude_export_excluded_all' + details.page = 0 + self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) + self.assertFalse(os.path.exists(exporter.generateFileName(details))) + details.page = 1 + self.assertFalse(os.path.exists(exporter.generateFileName(details))) + + def testPageFileName(self): + l = QgsLayout(QgsProject.instance()) + exporter = QgsLayoutExporter(l) + details = QgsLayoutExporter.PageExportDetails() + details.directory = '/tmp/output' + details.baseName = 'my_maps' + details.extension = 'png' + details.page = 0 + self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps.png') + details.page = 1 + self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps_2.png') + details.page = 2 + self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps_3.png') + + def prepareIteratorLayout(self): + layer_path = os.path.join(TEST_DATA_DIR, 'france_parts.shp') + layer = QgsVectorLayer(layer_path, 'test', "ogr") + + project = QgsProject() + project.addMapLayers([layer]) + # select epsg:2154 + crs = QgsCoordinateReferenceSystem() + crs.createFromSrid(2154) + project.setCrs(crs) + + layout = QgsPrintLayout(project) + layout.initializeDefaults() + + # fix the renderer, fill with green + props = {"color": "0,127,0", "outline_width": "4", "outline_color": '255,255,255'} + fillSymbol = QgsFillSymbol.createSimple(props) + renderer = QgsSingleSymbolRenderer(fillSymbol) + layer.setRenderer(renderer) + + # the atlas map + atlas_map = QgsLayoutItemMap(layout) + atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130)) + atlas_map.setFrameEnabled(True) + atlas_map.setLayers([layer]) + layout.addLayoutItem(atlas_map) + + # the atlas + atlas = layout.atlas() + atlas.setCoverageLayer(layer) + atlas.setEnabled(True) + + atlas_map.setExtent( + QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338)) + + atlas_map.setAtlasDriven(True) + atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) + atlas_map.setAtlasMargin(0.10) + + return project, layout + + def testIteratorToImages(self): + project, layout = self.prepareIteratorLayout() + atlas = layout.atlas() + atlas.setFilenameExpression("'test_exportiteratortoimage_' || \"NAME_1\"") + + # setup settings + settings = QgsLayoutExporter.ImageExportSettings() + settings.dpi = 80 + + result, error = QgsLayoutExporter.exportToImage(atlas, self.basetestpath + '/', 'png', settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + page1_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Basse-Normandie.png') + self.assertTrue(self.checkImage('iteratortoimage1', 'iteratortoimage1', page1_path)) + page2_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Bretagne.png') + self.assertTrue(self.checkImage('iteratortoimage2', 'iteratortoimage2', page2_path)) + page3_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Centre.png') + self.assertTrue(os.path.exists(page3_path)) + page4_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Pays de la Loire.png') + self.assertTrue(os.path.exists(page4_path)) + + def testIteratorToSvgs(self): + project, layout = self.prepareIteratorLayout() + atlas = layout.atlas() + atlas.setFilenameExpression("'test_exportiteratortosvg_' || \"NAME_1\"") + + # setup settings + settings = QgsLayoutExporter.SvgExportSettings() + settings.dpi = 80 + settings.forceVectorOutput = False + + result, error = QgsLayoutExporter.exportToSvg(atlas, self.basetestpath + '/', settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + page1_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.svg') + rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.png') + svgToPng(page1_path, rendered_page_1, width=935) + self.assertTrue(self.checkImage('iteratortosvg1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) + page2_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.svg') + rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.png') + svgToPng(page2_path, rendered_page_2, width=935) + self.assertTrue(self.checkImage('iteratortosvg2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) + page3_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Centre.svg') + self.assertTrue(os.path.exists(page3_path)) + page4_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Pays de la Loire.svg') + self.assertTrue(os.path.exists(page4_path)) + + def testIteratorToPdfs(self): + project, layout = self.prepareIteratorLayout() + atlas = layout.atlas() + atlas.setFilenameExpression("'test_exportiteratortopdf_' || \"NAME_1\"") + + # setup settings + settings = QgsLayoutExporter.PdfExportSettings() + settings.dpi = 80 + settings.rasterizeWholeImage = False + settings.forceVectorOutput = False + + result, error = QgsLayoutExporter.exportToPdfs(atlas, self.basetestpath + '/', settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + page1_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Basse-Normandie.pdf') + rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Basse-Normandie.png') + pdfToPng(page1_path, rendered_page_1, dpi=80, page=1) + self.assertTrue(self.checkImage('iteratortopdf1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) + page2_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Bretagne.pdf') + rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Bretagne.png') + pdfToPng(page2_path, rendered_page_2, dpi=80, page=1) + self.assertTrue(self.checkImage('iteratortopdf2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) + page3_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Centre.pdf') + self.assertTrue(os.path.exists(page3_path)) + page4_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Pays de la Loire.pdf') + self.assertTrue(os.path.exists(page4_path)) + + def testIteratorToPdf(self): + project, layout = self.prepareIteratorLayout() + atlas = layout.atlas() + + # setup settings + settings = QgsLayoutExporter.PdfExportSettings() + settings.dpi = 80 + settings.rasterizeWholeImage = False + settings.forceVectorOutput = False + + pdf_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single.pdf') + result, error = QgsLayoutExporter.exportToPdf(atlas, pdf_path, settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single1.png') + pdfToPng(pdf_path, rendered_page_1, dpi=80, page=1) + self.assertTrue(self.checkImage('iteratortopdfsingle1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) + + rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single2.png') + pdfToPng(pdf_path, rendered_page_2, dpi=80, page=2) + self.assertTrue(self.checkImage('iteratortopdfsingle2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) + + rendered_page_3 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single3.png') + pdfToPng(pdf_path, rendered_page_3, dpi=80, page=3) + self.assertTrue(os.path.exists(rendered_page_3)) + rendered_page_4 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single4.png') + pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4) + self.assertTrue(os.path.exists(rendered_page_4)) + + def testPrintIterator(self): + project, layout = self.prepareIteratorLayout() + atlas = layout.atlas() + + # setup settings + settings = QgsLayoutExporter.PrintExportSettings() + settings.dpi = 80 + settings.rasterizeWholeImage = False + + pdf_path = os.path.join(self.basetestpath, 'test_printiterator.pdf') + # make a qprinter directed to pdf + printer = QPrinter() + printer.setOutputFileName(pdf_path) + printer.setOutputFormat(QPrinter.PdfFormat) + + result, error = QgsLayoutExporter.print(atlas, printer, settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + rendered_page_1 = os.path.join(self.basetestpath, 'test_printiterator1.png') + pdfToPng(pdf_path, rendered_page_1, dpi=80, page=1) + self.assertTrue(self.checkImage('printeriterator1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) + + rendered_page_2 = os.path.join(self.basetestpath, 'test_printiterator2.png') + pdfToPng(pdf_path, rendered_page_2, dpi=80, page=2) + self.assertTrue(self.checkImage('printiterator2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) + + rendered_page_3 = os.path.join(self.basetestpath, 'test_printiterator3.png') + pdfToPng(pdf_path, rendered_page_3, dpi=80, page=3) + self.assertTrue(os.path.exists(rendered_page_3)) + rendered_page_4 = os.path.join(self.basetestpath, 'test_printiterator4.png') + pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4) + self.assertTrue(os.path.exists(rendered_page_4)) + + def testExportReport(self): + p = QgsProject() + r = QgsReport(p) + + # add a header + r.setHeaderEnabled(True) + report_header = QgsLayout(p) + report_header.initializeDefaults() + item1 = QgsLayoutItemShape(report_header) + item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.green) + fill.setStrokeStyle(Qt.NoPen) + item1.setSymbol(fill_symbol) + report_header.addItem(item1) + + r.setHeader(report_header) + + # add a footer + r.setFooterEnabled(True) + report_footer = QgsLayout(p) + report_footer.initializeDefaults() + item2 = QgsLayoutItemShape(report_footer) + item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) + item2.attemptMove(QgsLayoutPoint(10, 20)) + fill = QgsSimpleFillSymbolLayer() + fill_symbol = QgsFillSymbol() + fill_symbol.changeSymbolLayer(0, fill) + fill.setColor(Qt.cyan) + fill.setStrokeStyle(Qt.NoPen) + item2.setSymbol(fill_symbol) + report_footer.addItem(item2) + + r.setFooter(report_footer) + + # setup settings + settings = QgsLayoutExporter.ImageExportSettings() + settings.dpi = 80 + + report_path = os.path.join(self.basetestpath, 'test_report') + result, error = QgsLayoutExporter.exportToImage(r, report_path, 'png', settings) + self.assertEqual(result, QgsLayoutExporter.Success, error) + + page1_path = os.path.join(self.basetestpath, 'test_report_0001.png') + self.assertTrue(self.checkImage('report_page1', 'report_page1', page1_path)) + page2_path = os.path.join(self.basetestpath, 'test_report_0002.png') + self.assertTrue(self.checkImage('report_page2', 'report_page2', page2_path)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutframe.py b/tests/src/python/test_qgslayoutframe.py new file mode 100644 index 00000000000..2a059981cb4 --- /dev/null +++ b/tests/src/python/test_qgslayoutframe.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutFrame. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = '(C) 2017 by Nyall Dawson' +__date__ = '23/10/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.testing import start_app, unittest +from qgis.core import QgsLayoutFrame, QgsLayoutItemHtml + +from test_qgslayoutitem import LayoutItemTestCase + +start_app() + + +class TestQgsLayoutFrame(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.mf = None + + @classmethod + def createItem(cls, layout): + cls.mf = QgsLayoutItemHtml(layout) + return QgsLayoutFrame(layout, cls.mf) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutguides.py b/tests/src/python/test_qgslayoutguides.py index ba5527df660..49460006c32 100644 --- a/tests/src/python/test_qgslayoutguides.py +++ b/tests/src/python/test_qgslayoutguides.py @@ -65,6 +65,11 @@ class TestQgsLayoutGuide(unittest.TestCase): p = QgsProject() l = QgsLayout(p) l.initializeDefaults() # add a page + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + l.pageCollection().addPage(page2) + g = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(0)) g.setLayout(l) g.update() @@ -85,6 +90,19 @@ class TestQgsLayoutGuide(unittest.TestCase): self.assertEqual(g.item().line().y2(), 15) self.assertEqual(g.layoutPosition(), 15) + # guide on page2 + g1 = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(1)) + g1.setLayout(l) + g1.update() + g1.setPosition(QgsLayoutMeasurement(15, QgsUnitTypes.LayoutMillimeters)) + g1.update() + self.assertTrue(g1.item().isVisible()) + self.assertEqual(g1.item().line().x1(), 0) + self.assertEqual(g1.item().line().y1(), 235) + self.assertEqual(g1.item().line().x2(), 148) + self.assertEqual(g1.item().line().y2(), 235) + self.assertEqual(g1.layoutPosition(), 235) + # vertical guide g2 = QgsLayoutGuide(Qt.Vertical, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(0)) g2.setLayout(l) @@ -109,6 +127,17 @@ class TestQgsLayoutGuide(unittest.TestCase): g.update() self.assertFalse(g.item().isVisible()) + # guide on page2 + g3 = QgsLayoutGuide(Qt.Vertical, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(1)) + g3.setLayout(l) + g3.update() + self.assertTrue(g3.item().isVisible()) + self.assertEqual(g3.item().line().x1(), 50) + self.assertEqual(g3.item().line().y1(), 220) + self.assertEqual(g3.item().line().x2(), 50) + self.assertEqual(g3.item().line().y2(), 430) + self.assertEqual(g3.layoutPosition(), 50) + def testCollection(self): p = QgsProject() l = QgsLayout(p) diff --git a/tests/src/python/test_qgslayoutitem.py b/tests/src/python/test_qgslayoutitem.py index 27b080362f2..84469d96cd7 100644 --- a/tests/src/python/test_qgslayoutitem.py +++ b/tests/src/python/test_qgslayoutitem.py @@ -26,13 +26,34 @@ from qgis.core import (QgsProject, QgsLayoutSize, QgsApplication) from qgis.PyQt.QtCore import QRectF -from qgis.PyQt.QtGui import QColor +from qgis.PyQt.QtGui import QColor, QPainter from qgis.PyQt.QtTest import QSignalSpy start_app() +class LayoutItemTestCase(object): + + ''' + This is a collection of generic tests for QgsLayoutItem subclasses. + To make use of it, subclass it and set self.item_class to a QgsLayoutItem subclass you want to test. + ''' + + def make_item(self, layout): + if hasattr(self, 'item_class'): + return self.item_class(layout) + else: + return self.createItem(layout) + + def testRequiresRasterization(self): + l = QgsLayout(QgsProject.instance()) + item = self.make_item(l) + self.assertFalse(item.requiresRasterization()) + item.setBlendMode(QPainter.CompositionMode_SourceIn) + self.assertTrue(item.requiresRasterization()) + + class TestQgsLayoutItem(unittest.TestCase): def testDataDefinedFrameColor(self): diff --git a/tests/src/python/test_qgslayoutlabel.py b/tests/src/python/test_qgslayoutlabel.py index 8feac6dc98c..efda088a412 100644 --- a/tests/src/python/test_qgslayoutlabel.py +++ b/tests/src/python/test_qgslayoutlabel.py @@ -16,14 +16,25 @@ import qgis # NOQA from qgis.testing import start_app, unittest from qgis.PyQt.QtCore import QFileInfo, QDate, QDateTime -from qgis.core import QgsVectorLayer, QgsLayout, QgsLayoutItemLabel, QgsProject +from qgis.core import (QgsVectorLayer, + QgsPrintLayout, + QgsLayout, + QgsLayoutItemLabel, + QgsProject, + QgsLayoutItemPage, + QgsLayoutPoint) from utilities import unitTestDataPath +from test_qgslayoutitem import LayoutItemTestCase start_app() -class TestQgsLayoutItemLabel(unittest.TestCase): +class TestQgsLayoutItemLabel(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemLabel def testCase(self): TEST_DATA_DIR = unitTestDataPath() @@ -32,7 +43,7 @@ class TestQgsLayoutItemLabel(unittest.TestCase): QgsProject.instance().addMapLayers([mVectorLayer]) - layout = QgsLayout(QgsProject.instance()) + layout = QgsPrintLayout(QgsProject.instance()) layout.initializeDefaults() label = QgsLayoutItemLabel(layout) @@ -63,31 +74,28 @@ class TestQgsLayoutItemLabel(unittest.TestCase): assert label.currentText() == "__[NAME_1]42__" def feature_evaluation_test(self, layout, label, mVectorLayer): - pass - # TODO - #atlas = layout.atlasComposition() - #atlas.setCoverageLayer(mVectorLayer) - #atlas.setEnabled(True) - #layout.setAtlasMode(QgsComposition.ExportAtlas) + atlas = layout.atlas() + atlas.setCoverageLayer(mVectorLayer) + atlas.setEnabled(True) - #label.setText("[%\"NAME_1\"||'_ok'%]") - #atlas.beginRender() - #atlas.prepareForFeature(0) - #assert label.currentText() == "Basse-Normandie_ok" + label.setText("[%\"NAME_1\"||'_ok'%]") + atlas.beginRender() + atlas.seekTo(0) + assert label.currentText() == "Basse-Normandie_ok" - #atlas.prepareForFeature(1) - #assert label.currentText() == "Bretagne_ok" + atlas.seekTo(1) + assert label.currentText() == "Bretagne_ok" def page_evaluation_test(self, layout, label, mVectorLayer): - pass - # TODO - #layout.setNumPages(2) - #label.setText("[%@layout_page||'/'||@layout_numpages%]") - #assert label.currentText() == "1/2" + page = QgsLayoutItemPage(layout) + page.setPageSize('A4') + layout.pageCollection().addPage(page) + label.setText("[%@layout_page||'/'||@layout_numpages%]") + assert label.currentText() == "1/2" # move the the second page and re-evaluate - #label.setItemPosition(0, 320) - #assert label.currentText() == "2/2" + label.attemptMove(QgsLayoutPoint(0, 320)) + assert label.currentText() == "2/2" if __name__ == '__main__': diff --git a/tests/src/python/test_qgslayoutlegend.py b/tests/src/python/test_qgslayoutlegend.py index 924e3ac9393..a0883a84e05 100644 --- a/tests/src/python/test_qgslayoutlegend.py +++ b/tests/src/python/test_qgslayoutlegend.py @@ -34,11 +34,17 @@ from utilities import unitTestDataPath from qgslayoutchecker import QgsLayoutChecker import os +from test_qgslayoutitem import LayoutItemTestCase + start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsLayoutItemLegend(unittest.TestCase): +class TestQgsLayoutItemLegend(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemLegend def testInitialSizeSymbolMapUnits(self): """Test initial size of legend with a symbol size in map units""" @@ -70,7 +76,7 @@ class TestQgsLayoutItemLegend(unittest.TestCase): legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') layout.addLayoutItem(legend) - legend.setMap(map) + legend.setLinkedMap(map) checker = QgsLayoutChecker( 'composer_legend_mapunits', layout) @@ -107,7 +113,7 @@ class TestQgsLayoutItemLegend(unittest.TestCase): legend.setTitle('') legend.setLegendFilterByMapEnabled(True) layout.addLayoutItem(legend) - legend.setMap(map) + legend.setLinkedMap(map) map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) @@ -150,7 +156,7 @@ class TestQgsLayoutItemLegend(unittest.TestCase): legend.setResizeToContents(False) layout.addLayoutItem(legend) - legend.setMap(map) + legend.setLinkedMap(map) map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) @@ -193,7 +199,7 @@ class TestQgsLayoutItemLegend(unittest.TestCase): legend.setResizeToContents(False) layout.addLayoutItem(legend) - legend.setMap(map) + legend.setLinkedMap(map) map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) diff --git a/tests/src/python/test_qgslayoutmanager.py b/tests/src/python/test_qgslayoutmanager.py index b2e8fb66e24..f48e49e35eb 100644 --- a/tests/src/python/test_qgslayoutmanager.py +++ b/tests/src/python/test_qgslayoutmanager.py @@ -14,12 +14,13 @@ __revision__ = '$Format:%H$' import qgis # NOQA - from qgis.PyQt.QtXml import QDomDocument -from qgis.core import (QgsComposition, +from qgis.core import (QgsPrintLayout, QgsLayoutManager, - QgsProject) + QgsProject, + QgsReport, + QgsMasterLayoutInterface) from qgis.testing import start_app, unittest from utilities import unitTestDataPath @@ -41,54 +42,54 @@ class TestQgsLayoutManager(unittest.TestCase): """Run after each test.""" pass - def testAddComposition(self): + def testAddLayout(self): project = QgsProject() - composition = QgsComposition(project) - composition.setName('test composition') + layout = QgsPrintLayout(project) + layout.setName('test layout') manager = QgsLayoutManager(project) - composition_about_to_be_added_spy = QSignalSpy(manager.compositionAboutToBeAdded) - composition_added_spy = QSignalSpy(manager.compositionAdded) - self.assertTrue(manager.addComposition(composition)) - self.assertEqual(len(composition_about_to_be_added_spy), 1) - self.assertEqual(composition_about_to_be_added_spy[0][0], 'test composition') - self.assertEqual(len(composition_added_spy), 1) - self.assertEqual(composition_added_spy[0][0], 'test composition') + layout_about_to_be_added_spy = QSignalSpy(manager.layoutAboutToBeAdded) + layout_added_spy = QSignalSpy(manager.layoutAdded) + self.assertTrue(manager.addLayout(layout)) + self.assertEqual(len(layout_about_to_be_added_spy), 1) + self.assertEqual(layout_about_to_be_added_spy[0][0], 'test layout') + self.assertEqual(len(layout_added_spy), 1) + self.assertEqual(layout_added_spy[0][0], 'test layout') # adding it again should fail - self.assertFalse(manager.addComposition(composition)) + self.assertFalse(manager.addLayout(layout)) - # try adding a second composition - composition2 = QgsComposition(project) - composition2.setName('test composition2') - self.assertTrue(manager.addComposition(composition2)) - self.assertEqual(len(composition_added_spy), 2) - self.assertEqual(composition_about_to_be_added_spy[1][0], 'test composition2') - self.assertEqual(len(composition_about_to_be_added_spy), 2) - self.assertEqual(composition_added_spy[1][0], 'test composition2') + # try adding a second layout + layout2 = QgsPrintLayout(project) + layout2.setName('test layout2') + self.assertTrue(manager.addLayout(layout2)) + self.assertEqual(len(layout_added_spy), 2) + self.assertEqual(layout_about_to_be_added_spy[1][0], 'test layout2') + self.assertEqual(len(layout_about_to_be_added_spy), 2) + self.assertEqual(layout_added_spy[1][0], 'test layout2') - # adding a composition with duplicate name should fail - composition3 = QgsComposition(project) - composition3.setName('test composition2') - self.assertFalse(manager.addComposition(composition3)) + # adding a layout with duplicate name should fail + layout3 = QgsPrintLayout(project) + layout3.setName('test layout2') + self.assertFalse(manager.addLayout(layout3)) - def testCompositions(self): + def testLayouts(self): project = QgsProject() manager = QgsLayoutManager(project) - composition = QgsComposition(project) - composition.setName('test composition') - composition2 = QgsComposition(project) - composition2.setName('test composition2') - composition3 = QgsComposition(project) - composition3.setName('test composition3') + layout = QgsPrintLayout(project) + layout.setName('test layout') + layout2 = QgsPrintLayout(project) + layout2.setName('test layout2') + layout3 = QgsPrintLayout(project) + layout3.setName('test layout3') - manager.addComposition(composition) - self.assertEqual(manager.compositions(), [composition]) - manager.addComposition(composition2) - self.assertEqual(set(manager.compositions()), {composition, composition2}) - manager.addComposition(composition3) - self.assertEqual(set(manager.compositions()), {composition, composition2, composition3}) + manager.addLayout(layout) + self.assertEqual(manager.layouts(), [layout]) + manager.addLayout(layout2) + self.assertEqual(set(manager.layouts()), {layout, layout2}) + manager.addLayout(layout3) + self.assertEqual(set(manager.layouts()), {layout, layout2, layout3}) def aboutToBeRemoved(self, name): # composition should still exist at this time @@ -96,30 +97,36 @@ class TestQgsLayoutManager(unittest.TestCase): self.assertTrue(self.manager.compositionByName('test composition')) self.aboutFired = True - def testRemoveComposition(self): + def layoutAboutToBeRemoved(self, name): + # layout should still exist at this time + self.assertEqual(name, 'test layout') + self.assertTrue(self.manager.layoutByName('test layout')) + self.aboutFired = True + + def testRemoveLayout(self): project = QgsProject() - composition = QgsComposition(project) - composition.setName('test composition') + layout = QgsPrintLayout(project) + layout.setName('test layout') self.manager = QgsLayoutManager(project) - composition_removed_spy = QSignalSpy(self.manager.compositionRemoved) - composition_about_to_be_removed_spy = QSignalSpy(self.manager.compositionAboutToBeRemoved) - # tests that composition still exists when compositionAboutToBeRemoved is fired - self.manager.compositionAboutToBeRemoved.connect(self.aboutToBeRemoved) + layout_removed_spy = QSignalSpy(self.manager.layoutRemoved) + layout_about_to_be_removed_spy = QSignalSpy(self.manager.layoutAboutToBeRemoved) + # tests that layout still exists when layoutAboutToBeRemoved is fired + self.manager.layoutAboutToBeRemoved.connect(self.layoutAboutToBeRemoved) # not added, should fail - self.assertFalse(self.manager.removeComposition(composition)) - self.assertEqual(len(composition_removed_spy), 0) - self.assertEqual(len(composition_about_to_be_removed_spy), 0) + self.assertFalse(self.manager.removeLayout(layout)) + self.assertEqual(len(layout_removed_spy), 0) + self.assertEqual(len(layout_about_to_be_removed_spy), 0) - self.assertTrue(self.manager.addComposition(composition)) - self.assertEqual(self.manager.compositions(), [composition]) - self.assertTrue(self.manager.removeComposition(composition)) - self.assertEqual(len(self.manager.compositions()), 0) - self.assertEqual(len(composition_removed_spy), 1) - self.assertEqual(composition_removed_spy[0][0], 'test composition') - self.assertEqual(len(composition_about_to_be_removed_spy), 1) - self.assertEqual(composition_about_to_be_removed_spy[0][0], 'test composition') + self.assertTrue(self.manager.addLayout(layout)) + self.assertEqual(self.manager.layouts(), [layout]) + self.assertTrue(self.manager.removeLayout(layout)) + self.assertEqual(len(self.manager.layouts()), 0) + self.assertEqual(len(layout_removed_spy), 1) + self.assertEqual(layout_removed_spy[0][0], 'test layout') + self.assertEqual(len(layout_about_to_be_removed_spy), 1) + self.assertEqual(layout_about_to_be_removed_spy[0][0], 'test layout') self.assertTrue(self.aboutFired) self.manager = None @@ -127,45 +134,45 @@ class TestQgsLayoutManager(unittest.TestCase): project = QgsProject() manager = QgsLayoutManager(project) - # add a bunch of compositions - composition = QgsComposition(project) - composition.setName('test composition') - composition2 = QgsComposition(project) - composition2.setName('test composition2') - composition3 = QgsComposition(project) - composition3.setName('test composition3') + # add a bunch of layouts + layout = QgsPrintLayout(project) + layout.setName('test layout') + layout2 = QgsPrintLayout(project) + layout2.setName('test layout2') + layout3 = QgsPrintLayout(project) + layout3.setName('test layout3') - manager.addComposition(composition) - manager.addComposition(composition2) - manager.addComposition(composition3) + manager.addLayout(layout) + manager.addLayout(layout2) + manager.addLayout(layout3) - composition_removed_spy = QSignalSpy(manager.compositionRemoved) - composition_about_to_be_removed_spy = QSignalSpy(manager.compositionAboutToBeRemoved) + layout_removed_spy = QSignalSpy(manager.layoutRemoved) + layout_about_to_be_removed_spy = QSignalSpy(manager.layoutAboutToBeRemoved) manager.clear() - self.assertEqual(len(manager.compositions()), 0) - self.assertEqual(len(composition_removed_spy), 3) - self.assertEqual(len(composition_about_to_be_removed_spy), 3) + self.assertEqual(len(manager.layouts()), 0) + self.assertEqual(len(layout_removed_spy), 3) + self.assertEqual(len(layout_about_to_be_removed_spy), 3) - def testCompositionByName(self): + def testLayoutsByName(self): project = QgsProject() manager = QgsLayoutManager(project) - # add a bunch of compositions - composition = QgsComposition(project) - composition.setName('test composition') - composition2 = QgsComposition(project) - composition2.setName('test composition2') - composition3 = QgsComposition(project) - composition3.setName('test composition3') + # add a bunch of layouts + layout = QgsPrintLayout(project) + layout.setName('test layout') + layout2 = QgsPrintLayout(project) + layout2.setName('test layout2') + layout3 = QgsPrintLayout(project) + layout3.setName('test layout3') - manager.addComposition(composition) - manager.addComposition(composition2) - manager.addComposition(composition3) + manager.addLayout(layout) + manager.addLayout(layout2) + manager.addLayout(layout3) - self.assertFalse(manager.compositionByName('asdf')) - self.assertEqual(manager.compositionByName('test composition'), composition) - self.assertEqual(manager.compositionByName('test composition2'), composition2) - self.assertEqual(manager.compositionByName('test composition3'), composition3) + self.assertFalse(manager.layoutByName('asdf')) + self.assertEqual(manager.layoutByName('test layout'), layout) + self.assertEqual(manager.layoutByName('test layout2'), layout2) + self.assertEqual(manager.layoutByName('test layout3'), layout3) def testReadWriteXml(self): """ @@ -174,17 +181,17 @@ class TestQgsLayoutManager(unittest.TestCase): project = QgsProject() manager = QgsLayoutManager(project) - # add a bunch of compositions - composition = QgsComposition(project) - composition.setName('test composition') - composition2 = QgsComposition(project) - composition2.setName('test composition2') - composition3 = QgsComposition(project) - composition3.setName('test composition3') + # add a bunch of layouts + layout = QgsPrintLayout(project) + layout.setName('test layout') + layout2 = QgsPrintLayout(project) + layout2.setName('test layout2') + layout3 = QgsPrintLayout(project) + layout3.setName('test layout3') - manager.addComposition(composition) - manager.addComposition(composition2) - manager.addComposition(composition3) + manager.addLayout(layout) + manager.addLayout(layout2) + manager.addLayout(layout3) # save to xml doc = QDomDocument("testdoc") @@ -196,85 +203,79 @@ class TestQgsLayoutManager(unittest.TestCase): manager2 = QgsLayoutManager(project2) self.assertTrue(manager2.readXml(elem, doc)) - self.assertEqual(len(manager2.compositions()), 3) - names = [c.name() for c in manager2.compositions()] - self.assertEqual(set(names), {'test composition', 'test composition2', 'test composition3'}) + self.assertEqual(len(manager2.layouts()), 3) + names = [c.name() for c in manager2.layouts()] + self.assertCountEqual(names, ['test layout', 'test layout2', 'test layout3']) - def testSaveAsTemplate(self): + def testDuplicateLayout(self): """ - Test saving composition as template + Test duplicating layouts """ project = QgsProject() manager = QgsLayoutManager(project) doc = QDomDocument("testdoc") - self.assertFalse(manager.saveAsTemplate('not in manager', doc)) + self.assertFalse(manager.duplicateLayout(None, 'dest')) - composition = QgsComposition(project) - composition.setName('test composition') - manager.addComposition(composition) - self.assertTrue(manager.saveAsTemplate('test composition', doc)) - - def testDuplicateComposition(self): - """ - Test duplicating compositions - """ - project = QgsProject() - manager = QgsLayoutManager(project) - doc = QDomDocument("testdoc") - self.assertFalse(manager.duplicateComposition('not in manager', 'dest')) - - composition = QgsComposition(project) - composition.setName('test composition') - composition.setPaperSize(100, 200) - manager.addComposition(composition) + layout = QgsPrintLayout(project) + layout.setName('test layout') + layout.initializeDefaults() + manager.addLayout(layout) # duplicate name - self.assertFalse(manager.duplicateComposition('test composition', 'test composition')) + self.assertFalse(manager.duplicateLayout(layout, 'test layout')) + result = manager.duplicateLayout(layout, 'dupe layout') - result = manager.duplicateComposition('test composition', 'dupe composition') self.assertTrue(result) # make sure result in stored in manager - self.assertEqual(result, manager.compositionByName('dupe composition')) - self.assertEqual(result.name(), 'dupe composition') - self.assertEqual(result.paperHeight(), 200) - self.assertEqual(result.paperWidth(), 100) + self.assertEqual(result, manager.layoutByName('dupe layout')) + self.assertEqual(result.name(), 'dupe layout') + self.assertEqual(result.pageCollection().pageCount(), 1) def testGenerateUniqueTitle(self): project = QgsProject() manager = QgsLayoutManager(project) - self.assertEqual(manager.generateUniqueTitle(), 'Composer 1') + self.assertEqual(manager.generateUniqueTitle(QgsMasterLayoutInterface.PrintLayout), 'Layout 1') + self.assertEqual(manager.generateUniqueTitle(QgsMasterLayoutInterface.Report), 'Report 1') - composition = QgsComposition(project) - composition.setName(manager.generateUniqueTitle()) - manager.addComposition(composition) + layout = QgsPrintLayout(project) + layout.setName(manager.generateUniqueTitle()) + manager.addLayout(layout) - self.assertEqual(manager.generateUniqueTitle(), 'Composer 2') - composition2 = QgsComposition(project) - composition2.setName(manager.generateUniqueTitle()) - manager.addComposition(composition2) + self.assertEqual(manager.generateUniqueTitle(), 'Layout 2') + self.assertEqual(manager.generateUniqueTitle(QgsMasterLayoutInterface.Report), 'Report 1') + layout2 = QgsPrintLayout(project) + layout2.setName(manager.generateUniqueTitle()) + manager.addLayout(layout2) + + self.assertEqual(manager.generateUniqueTitle(), 'Layout 3') + + report1 = QgsReport(project) + report1.setName(manager.generateUniqueTitle(QgsMasterLayoutInterface.Report)) + manager.addLayout(report1) + self.assertEqual(manager.generateUniqueTitle(QgsMasterLayoutInterface.Report), 'Report 2') - self.assertEqual(manager.generateUniqueTitle(), 'Composer 3') manager.clear() - self.assertEqual(manager.generateUniqueTitle(), 'Composer 1') + self.assertEqual(manager.generateUniqueTitle(), 'Layout 1') + self.assertEqual(manager.generateUniqueTitle(QgsMasterLayoutInterface.Report), 'Report 1') def testRenameSignal(self): project = QgsProject() manager = QgsLayoutManager(project) - composition = QgsComposition(project) - composition.setName('c1') - manager.addComposition(composition) - composition2 = QgsComposition(project) - composition2.setName('c2') - manager.addComposition(composition2) + layout = QgsPrintLayout(project) + layout.setName('c1') + manager.addLayout(layout) + layout2 = QgsPrintLayout(project) + layout2.setName('c2') + manager.addLayout(layout2) - composition_renamed_spy = QSignalSpy(manager.compositionRenamed) - composition.setName('d1') - self.assertEqual(len(composition_renamed_spy), 1) - self.assertEqual(composition_renamed_spy[0][0], composition) - self.assertEqual(composition_renamed_spy[0][1], 'd1') - composition2.setName('d2') - self.assertEqual(len(composition_renamed_spy), 2) - self.assertEqual(composition_renamed_spy[1][0], composition2) - self.assertEqual(composition_renamed_spy[1][1], 'd2') + layout_renamed_spy = QSignalSpy(manager.layoutRenamed) + layout.setName('d1') + self.assertEqual(len(layout_renamed_spy), 1) + # self.assertEqual(layout_renamed_spy[0][0], layout) + self.assertEqual(layout_renamed_spy[0][1], 'd1') + layout2.setName('d2') + self.assertEqual(len(layout_renamed_spy), 2) + # self.assertEqual(layout_renamed_spy[1][0], layout2) + self.assertEqual(layout_renamed_spy[1][1], 'd2') if __name__ == '__main__': diff --git a/tests/src/python/test_qgslayoutmap.py b/tests/src/python/test_qgslayoutmap.py index 29f71d89434..20aeccd9bae 100644 --- a/tests/src/python/test_qgslayoutmap.py +++ b/tests/src/python/test_qgslayoutmap.py @@ -16,9 +16,9 @@ import qgis # NOQA import os -from qgis.PyQt.QtCore import QFileInfo, QRectF +from qgis.PyQt.QtCore import QFileInfo, QRectF, QDir from qgis.PyQt.QtXml import QDomDocument -from qgis.PyQt.QtGui import QPainter +from qgis.PyQt.QtGui import QPainter, QColor from qgis.core import (QgsLayoutItemMap, QgsRectangle, @@ -34,12 +34,25 @@ from qgis.core import (QgsLayoutItemMap, from qgis.testing import start_app, unittest from utilities import unitTestDataPath from qgslayoutchecker import QgsLayoutChecker +from test_qgslayoutitem import LayoutItemTestCase start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsComposerMap(unittest.TestCase): +class TestQgsLayoutMap(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemMap + + def setUp(self): + self.report = "

Python QgsLayoutItemMap Tests

\n" + + def tearDown(self): + report_file_path = "%s/qgistest.html" % QDir.tempPath() + with open(report_file_path, 'a') as report_file: + report_file.write(self.report) def __init__(self, methodName): """Run once on class initialization.""" @@ -62,7 +75,7 @@ class TestQgsComposerMap(unittest.TestCase): # assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsProject.instance().addMapLayers([self.raster_layer, self.vector_layer]) - # create composition with composer map + # create layout with layout map self.layout = QgsLayout(QgsProject.instance()) self.layout.initializeDefaults() self.map = QgsLayoutItemMap(self.layout) @@ -82,11 +95,12 @@ class TestQgsComposerMap(unittest.TestCase): self.map.setExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.map) + overviewMap.overview().setLinkedMap(self.map) checker = QgsLayoutChecker('composermap_overview', self.layout) checker.setColorTolerance(6) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testLayout() + self.report += checker.report() self.layout.removeLayoutItem(overviewMap) assert myTestResult, myMessage @@ -101,11 +115,12 @@ class TestQgsComposerMap(unittest.TestCase): self.map.setExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.map) + overviewMap.overview().setLinkedMap(self.map) overviewMap.overview().setBlendMode(QPainter.CompositionMode_Multiply) checker = QgsLayoutChecker('composermap_overview_blending', self.layout) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testLayout() + self.report += checker.report() self.layout.removeLayoutItem(overviewMap) assert myTestResult, myMessage @@ -120,11 +135,12 @@ class TestQgsComposerMap(unittest.TestCase): self.map.setExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.map) + overviewMap.overview().setLinkedMap(self.map) overviewMap.overview().setInverted(True) checker = QgsLayoutChecker('composermap_overview_invert', self.layout) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testLayout() + self.report += checker.report() self.layout.removeLayoutItem(overviewMap) assert myTestResult, myMessage @@ -139,17 +155,18 @@ class TestQgsComposerMap(unittest.TestCase): self.map.setExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setExtent(myRectangle2) - overviewMap.overview().setFrameMap(self.map) + overviewMap.overview().setLinkedMap(self.map) overviewMap.overview().setInverted(False) overviewMap.overview().setCentered(True) checker = QgsLayoutChecker('composermap_overview_center', self.layout) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testLayout() + self.report += checker.report() self.layout.removeLayoutItem(overviewMap) assert myTestResult, myMessage def testMapCrs(self): - # create composition with composer map + # create layout with layout map map_settings = QgsMapSettings() map_settings.setLayers([self.vector_layer]) layout = QgsLayout(QgsProject.instance()) @@ -176,6 +193,7 @@ class TestQgsComposerMap(unittest.TestCase): checker = QgsLayoutChecker('composermap_crs3857', layout) checker.setControlPathPrefix("composer_map") result, message = checker.testLayout() + self.report += checker.report() self.assertTrue(result, message) # overwrite CRS @@ -187,6 +205,7 @@ class TestQgsComposerMap(unittest.TestCase): checker = QgsLayoutChecker('composermap_crs4326', layout) checker.setControlPathPrefix("composer_map") result, message = checker.testLayout() + self.report += checker.report() self.assertTrue(result, message) # change back to project CRS @@ -194,44 +213,36 @@ class TestQgsComposerMap(unittest.TestCase): self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) - def testuniqueId(self): - return - doc = QDomDocument() - documentElement = doc.createElement('ComposerItemClipboard') - self.layout.writeXml(documentElement, doc) - self.layout.addItemsFromXml(documentElement, doc) + def testContainsAdvancedEffects(self): + map_settings = QgsMapSettings() + map_settings.setLayers([self.vector_layer]) + layout = QgsLayout(QgsProject.instance()) + map = QgsLayoutItemMap(layout) - # test if both composer maps have different ids - newMap = QgsComposerMap(self.layout, 0, 0, 10, 10) - mapList = self.layout.composerMapItems() + self.assertFalse(map.containsAdvancedEffects()) + self.vector_layer.setBlendMode(QPainter.CompositionMode_Darken) + result = map.containsAdvancedEffects() + self.vector_layer.setBlendMode(QPainter.CompositionMode_SourceOver) + self.assertTrue(result) - for mapIt in mapList: - if mapIt != self.map: - newMap = mapIt - break + def testRasterization(self): + map_settings = QgsMapSettings() + map_settings.setLayers([self.vector_layer]) + layout = QgsLayout(QgsProject.instance()) + map = QgsLayoutItemMap(layout) - oldId = self.map.id() - newId = newMap.id() + self.assertFalse(map.requiresRasterization()) + self.vector_layer.setBlendMode(QPainter.CompositionMode_Darken) + self.assertFalse(map.requiresRasterization()) + self.assertTrue(map.containsAdvancedEffects()) - self.layout.removeComposerItem(newMap) - myMessage = 'old: %s new: %s' % (oldId, newId) - assert oldId != newId, myMessage + map.setBackgroundEnabled(False) + self.assertTrue(map.requiresRasterization()) + map.setBackgroundEnabled(True) + map.setBackgroundColor(QColor(1, 1, 1, 1)) + self.assertTrue(map.requiresRasterization()) - def testWorldFileGeneration(self): - return - myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) - self.map.setNewExtent(myRectangle) - self.map.setMapRotation(30.0) - - self.layout.setGenerateWorldFile(True) - self.layout.setReferenceMap(self.map) - - p = self.layout.computeWorldFileParameters() - pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, - 2.4136013686911886, -4.179969388427311, 3342408.5663611) - ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) - for i in range(0, 6): - assert abs(p[i] - pexpected[i]) < ptolerance[i] + self.vector_layer.setBlendMode(QPainter.CompositionMode_SourceOver) if __name__ == '__main__': diff --git a/tests/src/python/test_qgslayoutmapgrid.py b/tests/src/python/test_qgslayoutmapgrid.py index 74a360450c2..d78edd5641e 100644 --- a/tests/src/python/test_qgslayoutmapgrid.py +++ b/tests/src/python/test_qgslayoutmapgrid.py @@ -33,7 +33,7 @@ start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsComposerMap(unittest.TestCase): +class TestQgsLayoutMapGrid(unittest.TestCase): def testGrid(self): layout = QgsLayout(QgsProject.instance()) diff --git a/tests/src/python/test_qgslayoutpage.py b/tests/src/python/test_qgslayoutpage.py new file mode 100644 index 00000000000..2bdc0b691da --- /dev/null +++ b/tests/src/python/test_qgslayoutpage.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutItemPage. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = '(C) 2017 by Nyall Dawson' +__date__ = '23/10/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.testing import start_app, unittest +from qgis.core import QgsLayoutItemPage + +from test_qgslayoutitem import LayoutItemTestCase + +start_app() + + +class TestQgsLayoutPage(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemPage + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutpagecollection.py b/tests/src/python/test_qgslayoutpagecollection.py index 2d1b793c2e2..cfd7c79391f 100644 --- a/tests/src/python/test_qgslayoutpagecollection.py +++ b/tests/src/python/test_qgslayoutpagecollection.py @@ -22,9 +22,13 @@ from qgis.core import (QgsUnitTypes, QgsLayoutPoint, QgsLayoutObject, QgsProject, + QgsMargins, QgsProperty, + QgsLayoutGuide, + QgsLayoutMeasurement, QgsLayoutPageCollection, QgsSimpleFillSymbolLayer, + QgsLayoutItemShape, QgsFillSymbol, QgsReadWriteContext) from qgis.PyQt.QtCore import Qt, QCoreApplication, QEvent, QPointF, QRectF @@ -187,6 +191,36 @@ class TestQgsLayoutPageCollection(unittest.TestCase): self.assertEqual(len(page_about_to_be_removed_spy), 2) self.assertEqual(page_about_to_be_removed_spy[-1][0], 0) + def testClear(self): + """ + Test clearing the collection + """ + p = QgsProject() + l = QgsLayout(p) + collection = l.pageCollection() + + collection.clear() + + # add a page + page = QgsLayoutItemPage(l) + page.setPageSize('A4') + collection.addPage(page) + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + collection.addPage(page2) + + page_about_to_be_removed_spy = QSignalSpy(collection.pageAboutToBeRemoved) + + # clear + collection.clear() + self.assertEqual(collection.pageCount(), 0) + self.assertEqual(len(page_about_to_be_removed_spy), 2) + + QCoreApplication.sendPostedEvents(None, QEvent.DeferredDelete) + self.assertTrue(sip.isdeleted(page)) + self.assertTrue(sip.isdeleted(page2)) + def testExtendByNewPage(self): """ Test extend by adding new page @@ -216,9 +250,9 @@ class TestQgsLayoutPageCollection(unittest.TestCase): self.assertEqual(collection.pageCount(), 3) self.assertEqual(new_page2.sizeWithUnits(), new_page.sizeWithUnits()) - def testMaxPageWidth(self): + def testMaxPageWidthAndSize(self): """ - Test calculating maximum page width + Test calculating maximum page width and size """ p = QgsProject() l = QgsLayout(p) @@ -229,18 +263,52 @@ class TestQgsLayoutPageCollection(unittest.TestCase): page.setPageSize('A4') collection.addPage(page) self.assertEqual(collection.maximumPageWidth(), 210.0) + self.assertEqual(collection.maximumPageSize().width(), 210.0) + self.assertEqual(collection.maximumPageSize().height(), 297.0) # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A3') collection.addPage(page2) self.assertEqual(collection.maximumPageWidth(), 297.0) + self.assertEqual(collection.maximumPageSize().width(), 297.0) + self.assertEqual(collection.maximumPageSize().height(), 420.0) # add a page with other units page3 = QgsLayoutItemPage(l) page3.setPageSize(QgsLayoutSize(100, 100, QgsUnitTypes.LayoutMeters)) collection.addPage(page3) self.assertEqual(collection.maximumPageWidth(), 100000.0) + self.assertEqual(collection.maximumPageSize().width(), 100000.0) + self.assertEqual(collection.maximumPageSize().height(), 100000.0) + + def testUniformPageSizes(self): + """ + Test detection of uniform page sizes + """ + p = QgsProject() + l = QgsLayout(p) + collection = l.pageCollection() + + self.assertTrue(collection.hasUniformPageSizes()) + + # add a page + page = QgsLayoutItemPage(l) + page.setPageSize('A4') + collection.addPage(page) + self.assertTrue(collection.hasUniformPageSizes()) + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize(QgsLayoutSize(21.0, 29.7, QgsUnitTypes.LayoutCentimeters)) + collection.addPage(page2) + self.assertTrue(collection.hasUniformPageSizes()) + + # add a page with other units + page3 = QgsLayoutItemPage(l) + page3.setPageSize('A5') + collection.addPage(page3) + self.assertFalse(collection.hasUniformPageSizes()) def testReflow(self): """ @@ -297,6 +365,120 @@ class TestQgsLayoutPageCollection(unittest.TestCase): self.assertEqual(page3.pos().x(), 0) self.assertEqual(page3.pos().y(), 130) + def testInsertPageWithItems(self): + p = QgsProject() + l = QgsLayout(p) + collection = l.pageCollection() + + # add a page + page = QgsLayoutItemPage(l) + page.setPageSize('A4') + collection.addPage(page) + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A5') + collection.addPage(page2) + + # item on pages + shape1 = QgsLayoutItemShape(l) + shape1.attemptResize(QgsLayoutSize(90, 50)) + shape1.attemptMove(QgsLayoutPoint(90, 50), page=0) + l.addLayoutItem(shape1) + + shape2 = QgsLayoutItemShape(l) + shape2.attemptResize(QgsLayoutSize(110, 50)) + shape2.attemptMove(QgsLayoutPoint(100, 150), page=1) + l.addLayoutItem(shape2) + + self.assertEqual(shape1.page(), 0) + self.assertEqual(shape2.page(), 1) + + # third page, slotted in middle + page3 = QgsLayoutItemPage(l) + page3.setPageSize('A3') + collection.insertPage(page3, 0) + + # check item position + self.assertEqual(shape1.page(), 1) + self.assertEqual(shape1.pagePositionWithUnits(), QgsLayoutPoint(90, 50)) + self.assertEqual(shape2.page(), 2) + self.assertEqual(shape2.pagePositionWithUnits(), QgsLayoutPoint(100, 150)) + + def testDeletePageWithItems(self): + p = QgsProject() + l = QgsLayout(p) + collection = l.pageCollection() + + # add a page + page = QgsLayoutItemPage(l) + page.setPageSize('A4') + collection.addPage(page) + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A4') + collection.addPage(page2) + page3 = QgsLayoutItemPage(l) + page3.setPageSize('A4') + collection.addPage(page3) + + # item on pages + shape1 = QgsLayoutItemShape(l) + shape1.attemptResize(QgsLayoutSize(90, 50)) + shape1.attemptMove(QgsLayoutPoint(90, 50), page=0) + l.addLayoutItem(shape1) + + shape2 = QgsLayoutItemShape(l) + shape2.attemptResize(QgsLayoutSize(110, 50)) + shape2.attemptMove(QgsLayoutPoint(100, 150), page=2) + l.addLayoutItem(shape2) + + self.assertEqual(shape1.page(), 0) + self.assertEqual(shape2.page(), 2) + + collection.deletePage(1) + + # check item position + self.assertEqual(shape1.page(), 0) + self.assertEqual(shape1.pagePositionWithUnits(), QgsLayoutPoint(90, 50)) + self.assertEqual(shape2.page(), 1) + self.assertEqual(shape2.pagePositionWithUnits(), QgsLayoutPoint(100, 150)) + + def testDeletePageWithItems2(self): + p = QgsProject() + l = QgsLayout(p) + collection = l.pageCollection() + + # add a page + page = QgsLayoutItemPage(l) + page.setPageSize('A4') + collection.addPage(page) + page2 = QgsLayoutItemPage(l) + page2.setPageSize('A4') + collection.addPage(page2) + page3 = QgsLayoutItemPage(l) + page3.setPageSize('A4') + collection.addPage(page3) + + # item on pages + shape1 = QgsLayoutItemShape(l) + shape1.attemptResize(QgsLayoutSize(90, 50)) + shape1.attemptMove(QgsLayoutPoint(90, 50), page=0) + l.addLayoutItem(shape1) + + shape2 = QgsLayoutItemShape(l) + shape2.attemptResize(QgsLayoutSize(110, 50)) + shape2.attemptMove(QgsLayoutPoint(100, 150), page=2) + l.addLayoutItem(shape2) + + self.assertEqual(shape1.page(), 0) + self.assertEqual(shape2.page(), 2) + + collection.deletePage(page2) + + # check item position + self.assertEqual(shape1.page(), 0) + self.assertEqual(shape1.pagePositionWithUnits(), QgsLayoutPoint(90, 50)) + self.assertEqual(shape2.page(), 1) + self.assertEqual(shape2.pagePositionWithUnits(), QgsLayoutPoint(100, 150)) + def testDataDefinedSize(self): p = QgsProject() l = QgsLayout(p) @@ -711,6 +893,68 @@ class TestQgsLayoutPageCollection(unittest.TestCase): self.assertEqual(collection.pageCount(), 1) self.assertEqual(collection.page(0).pageSize().width(), 148) + def testResizeToContents(self): + p = QgsProject() + l = QgsLayout(p) + + shape1 = QgsLayoutItemShape(l) + shape1.attemptResize(QgsLayoutSize(90, 50)) + shape1.attemptMove(QgsLayoutPoint(90, 50)) + shape1.setItemRotation(45, False) + l.addLayoutItem(shape1) + shape2 = QgsLayoutItemShape(l) + shape2.attemptResize(QgsLayoutSize(110, 50)) + shape2.attemptMove(QgsLayoutPoint(100, 150), True, False, 0) + l.addLayoutItem(shape2) + shape3 = QgsLayoutItemShape(l) + l.addLayoutItem(shape3) + shape3.attemptResize(QgsLayoutSize(50, 100)) + shape3.attemptMove(QgsLayoutPoint(210, 250), True, False, 0) + shape4 = QgsLayoutItemShape(l) + l.addLayoutItem(shape4) + shape4.attemptResize(QgsLayoutSize(50, 30)) + shape4.attemptMove(QgsLayoutPoint(10, 340), True, False, 0) + shape4.setVisibility(False) + + # resize with no existing pages + l.pageCollection().resizeToContents(QgsMargins(1, 2, 3, 4), QgsUnitTypes.LayoutCentimeters) + self.assertEqual(l.pageCollection().pageCount(), 1) + + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().width(), 290.3, 2) + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().height(), 380.36, 2) + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().units(), QgsUnitTypes.LayoutMillimeters) + + self.assertAlmostEqual(shape1.positionWithUnits().x(), 90.15, 2) + self.assertAlmostEqual(shape1.positionWithUnits().y(), 20.21, 2) + self.assertAlmostEqual(shape2.positionWithUnits().x(), 100.15, 2) + self.assertAlmostEqual(shape2.positionWithUnits().y(), 120.21, 2) + self.assertAlmostEqual(shape3.positionWithUnits().x(), 210.15, 2) + self.assertAlmostEqual(shape3.positionWithUnits().y(), 220.21, 2) + self.assertAlmostEqual(shape4.positionWithUnits().x(), 10.15, 2) + self.assertAlmostEqual(shape4.positionWithUnits().y(), 310.21, 2) + + # add a second page + page2 = QgsLayoutItemPage(l) + page2.setPageSize("A4", QgsLayoutItemPage.Landscape) + l.pageCollection().addPage(page2) + + # add some guides + g1 = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(2.5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(0)) + l.guides().addGuide(g1) + g2 = QgsLayoutGuide(Qt.Vertical, QgsLayoutMeasurement(4.5, QgsUnitTypes.LayoutCentimeters), l.pageCollection().page(0)) + l.guides().addGuide(g2) + + # second page should be removed + l.pageCollection().resizeToContents(QgsMargins(0, 0, 0, 0), QgsUnitTypes.LayoutCentimeters) + self.assertEqual(l.pageCollection().pageCount(), 1) + + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().width(), 250.3, 2) + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().height(), 320.36, 2) + self.assertAlmostEqual(l.pageCollection().page(0).sizeWithUnits().units(), QgsUnitTypes.LayoutMillimeters) + + self.assertAlmostEqual(g1.position().length(), 0.5, 2) + self.assertAlmostEqual(g2.position().length(), 3.5, 2) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgslayoutpicture.py b/tests/src/python/test_qgslayoutpicture.py index eed16932129..f57876599d4 100644 --- a/tests/src/python/test_qgslayoutpicture.py +++ b/tests/src/python/test_qgslayoutpicture.py @@ -30,15 +30,18 @@ from qgis.core import (QgsLayoutItemPicture, from qgis.testing import start_app, unittest from utilities import unitTestDataPath from qgslayoutchecker import QgsLayoutChecker +from test_qgslayoutitem import LayoutItemTestCase start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsLayoutPicture(unittest.TestCase): +class TestQgsLayoutPicture(unittest.TestCase, LayoutItemTestCase): @classmethod def setUpClass(cls): + cls.item_class = QgsLayoutItemPicture + # Bring up a simple HTTP server, for remote picture tests os.chdir(unitTestDataPath() + '') handler = http.server.SimpleHTTPRequestHandler @@ -101,8 +104,8 @@ class TestQgsLayoutPicture(unittest.TestCase): picture = QgsLayoutItemPicture(layout) layout.addLayoutItem(picture) - picture.setRotationMap(map.uuid()) - self.assertEqual(picture.rotationMap(), map.uuid()) + picture.setLinkedMap(map) + self.assertEqual(picture.linkedMap(), map) picture.setNorthMode(QgsLayoutItemPicture.GridNorth) map.setMapRotation(45) @@ -126,8 +129,8 @@ class TestQgsLayoutPicture(unittest.TestCase): picture = QgsLayoutItemPicture(layout) layout.addLayoutItem(picture) - picture.setRotationMap(map.uuid()) - self.assertEqual(picture.rotationMap(), map.uuid()) + picture.setLinkedMap(map) + self.assertEqual(picture.linkedMap(), map) picture.setNorthMode(QgsLayoutItemPicture.TrueNorth) self.assertAlmostEqual(picture.pictureRotation(), 37.20, 1) diff --git a/tests/src/python/test_qgslayoutpolygon.py b/tests/src/python/test_qgslayoutpolygon.py index 8df5142de21..8a0194cbdf8 100644 --- a/tests/src/python/test_qgslayoutpolygon.py +++ b/tests/src/python/test_qgslayoutpolygon.py @@ -29,12 +29,17 @@ from qgis.testing import (start_app, ) from utilities import unitTestDataPath from qgslayoutchecker import QgsLayoutChecker +from test_qgslayoutitem import LayoutItemTestCase start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsLayoutPolygon(unittest.TestCase): +class TestQgsLayoutPolygon(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemPolygon def __init__(self, methodName): """Run once on class initialization.""" diff --git a/tests/src/python/test_qgslayoutpolyline.py b/tests/src/python/test_qgslayoutpolyline.py index d54cb7e2287..39edd304630 100644 --- a/tests/src/python/test_qgslayoutpolyline.py +++ b/tests/src/python/test_qgslayoutpolyline.py @@ -29,12 +29,17 @@ from qgis.testing import (start_app, ) from utilities import unitTestDataPath from qgslayoutchecker import QgsLayoutChecker +from test_qgslayoutitem import LayoutItemTestCase start_app() TEST_DATA_DIR = unitTestDataPath() -class TestQgsLayoutPolyline(unittest.TestCase): +class TestQgsLayoutPolyline(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemPolyline def __init__(self, methodName): """Run once on class initialization.""" diff --git a/tests/src/python/test_qgslayoutscalebar.py b/tests/src/python/test_qgslayoutscalebar.py new file mode 100644 index 00000000000..185bba1c142 --- /dev/null +++ b/tests/src/python/test_qgslayoutscalebar.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutItemScaleBar. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = '(C) 2017 by Nyall Dawson' +__date__ = '23/10/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.testing import start_app, unittest +from qgis.core import QgsLayoutItemScaleBar + +from test_qgslayoutitem import LayoutItemTestCase + +start_app() + + +class TestQgsLayoutScaleBar(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemScaleBar + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutshape.py b/tests/src/python/test_qgslayoutshape.py new file mode 100644 index 00000000000..c1a74793781 --- /dev/null +++ b/tests/src/python/test_qgslayoutshape.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLayoutItemShape. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = '(C) 2017 by Nyall Dawson' +__date__ = '23/10/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.testing import start_app, unittest +from qgis.core import QgsLayoutItemShape + +from test_qgslayoutitem import LayoutItemTestCase + +start_app() + + +class TestQgsLayoutShape(unittest.TestCase, LayoutItemTestCase): + + @classmethod + def setUpClass(cls): + cls.item_class = QgsLayoutItemShape + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgslayoutview.py b/tests/src/python/test_qgslayoutview.py index ae53a7e95f7..5edd2e05dab 100644 --- a/tests/src/python/test_qgslayoutview.py +++ b/tests/src/python/test_qgslayoutview.py @@ -13,17 +13,22 @@ __copyright__ = 'Copyright 2017, The QGIS Project' __revision__ = '$Format:%H$' import qgis # NOQA - +import sip from qgis.core import (QgsProject, QgsLayout, QgsUnitTypes, QgsLayoutItemPicture, + QgsLayoutItemLabel, + QgsLayoutItemHtml, + QgsLayoutItemRegistry, + QgsLayoutFrame, QgsLayoutPoint, QgsLayoutSize, QgsLayoutAligner) from qgis.gui import QgsLayoutView -from qgis.PyQt.QtCore import QRectF +from qgis.PyQt.QtCore import QRectF, QMimeData, QByteArray from qgis.PyQt.QtGui import QTransform +from qgis.PyQt.QtWidgets import QApplication from qgis.PyQt.QtTest import QSignalSpy from qgis.testing import start_app, unittest @@ -634,6 +639,160 @@ class TestQgsLayoutView(unittest.TestCase): self.assertEqual(item2.sizeWithUnits(), QgsLayoutSize(19, 19, QgsUnitTypes.LayoutMillimeters)) self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.8, QgsUnitTypes.LayoutCentimeters)) + def testDeleteItems(self): + p = QgsProject() + l = QgsLayout(p) + + # add some items + item1 = QgsLayoutItemLabel(l) + item1.setText('label 1') + l.addLayoutItem(item1) + item2 = QgsLayoutItemLabel(l) + item2.setText('label 2') + l.addLayoutItem(item2) + item3 = QgsLayoutItemLabel(l) + item3.setText('label 2') + l.addLayoutItem(item3) + + view = QgsLayoutView() + view.setCurrentLayout(l) + count_before = len(l.items()) + view.deleteSelectedItems() + self.assertEqual(len(l.items()), count_before) + + item2.setSelected(True) + view.deleteSelectedItems() + self.assertEqual(len(l.items()), count_before - 1) + self.assertIn(item1, l.items()) + self.assertIn(item3, l.items()) + view.deleteItems([item3]) + self.assertEqual(len(l.items()), count_before - 2) + self.assertIn(item1, l.items()) + + def testCopyPaste(self): + p = QgsProject() + l = QgsLayout(p) + + # clear clipboard + mime_data = QMimeData() + mime_data.setData("text/xml", QByteArray()) + clipboard = QApplication.clipboard() + clipboard.setMimeData(mime_data) + + # add an item + item1 = QgsLayoutItemLabel(l) + item1.setText('label 1') + l.addLayoutItem(item1) + item1.setSelected(True) + item2 = QgsLayoutItemLabel(l) + item2.setText('label 2') + l.addLayoutItem(item2) + item2.setSelected(True) + + # multiframes + multiframe1 = QgsLayoutItemHtml(l) + multiframe1.setHtml('mf1') + l.addMultiFrame(multiframe1) + frame1 = QgsLayoutFrame(l, multiframe1) + frame1.setId('frame1a') + multiframe1.addFrame(frame1) + frame1b = QgsLayoutFrame(l, multiframe1) + frame1b.setId('frame1b') + multiframe1.addFrame(frame1b) # not selected + frame1c = QgsLayoutFrame(l, multiframe1) + frame1c.setId('frame1b') + multiframe1.addFrame(frame1c) # not selected + + multiframe2 = QgsLayoutItemHtml(l) + multiframe2.setHtml('mf2') + l.addMultiFrame(multiframe2) + frame2 = QgsLayoutFrame(l, multiframe2) + frame2.setId('frame2') + multiframe2.addFrame(frame2) + + frame1.setSelected(True) + frame2.setSelected(True) + + view = QgsLayoutView() + view.setCurrentLayout(l) + self.assertFalse(view.hasItemsInClipboard()) + + view.copySelectedItems(QgsLayoutView.ClipboardCopy) + self.assertTrue(view.hasItemsInClipboard()) + + pasted = view.pasteItems(QgsLayoutView.PasteModeCursor) + self.assertEqual(len(pasted), 4) + + new_multiframes = [m for m in l.multiFrames() if m not in [multiframe1, multiframe2]] + self.assertEqual(len(new_multiframes), 2) + + self.assertIn(pasted[0], l.items()) + self.assertIn(pasted[1], l.items()) + labels = [p for p in pasted if p.type() == QgsLayoutItemRegistry.LayoutLabel] + self.assertIn(sip.cast(labels[0], QgsLayoutItemLabel).text(), ('label 1', 'label 2')) + self.assertIn(sip.cast(labels[1], QgsLayoutItemLabel).text(), ('label 1', 'label 2')) + frames = [p for p in pasted if p.type() == QgsLayoutItemRegistry.LayoutFrame] + pasted_frame1 = sip.cast(frames[0], QgsLayoutFrame) + pasted_frame2 = sip.cast(frames[1], QgsLayoutFrame) + self.assertIn(pasted_frame1.multiFrame(), new_multiframes) + self.assertIn(new_multiframes[0].frames()[0].uuid(), (pasted_frame1.uuid(), pasted_frame2.uuid())) + self.assertIn(pasted_frame2.multiFrame(), new_multiframes) + self.assertIn(new_multiframes[1].frames()[0].uuid(), (pasted_frame1.uuid(), pasted_frame2.uuid())) + + self.assertEqual(frame1.multiFrame(), multiframe1) + self.assertCountEqual(multiframe1.frames(), [frame1, frame1b, frame1c]) + self.assertEqual(frame1b.multiFrame(), multiframe1) + self.assertEqual(frame1c.multiFrame(), multiframe1) + self.assertEqual(frame2.multiFrame(), multiframe2) + self.assertCountEqual(multiframe2.frames(), [frame2]) + + # copy specific item + view.copyItems([item2], QgsLayoutView.ClipboardCopy) + l2 = QgsLayout(p) + view2 = QgsLayoutView() + view2.setCurrentLayout(l2) + pasted = view2.pasteItems(QgsLayoutView.PasteModeCursor) + self.assertEqual(len(pasted), 1) + self.assertIn(pasted[0], l2.items()) + self.assertEqual(sip.cast(pasted[0], QgsLayoutItemLabel).text(), 'label 2') + + def testCutPaste(self): + p = QgsProject() + l = QgsLayout(p) + + # clear clipboard + mime_data = QMimeData() + mime_data.setData("text/xml", QByteArray()) + clipboard = QApplication.clipboard() + clipboard.setMimeData(mime_data) + + # add an item + item1 = QgsLayoutItemLabel(l) + item1.setText('label 1') + l.addLayoutItem(item1) + item1.setSelected(True) + item2 = QgsLayoutItemLabel(l) + item2.setText('label 2') + l.addLayoutItem(item2) + item2.setSelected(True) + + view = QgsLayoutView() + view.setCurrentLayout(l) + self.assertFalse(view.hasItemsInClipboard()) + + len_before = len(l.items()) + view.copySelectedItems(QgsLayoutView.ClipboardCut) + self.assertTrue(view.hasItemsInClipboard()) + self.assertEqual(len(l.items()), len_before - 2) + + pasted = view.pasteItems(QgsLayoutView.PasteModeCursor) + self.assertEqual(len(pasted), 2) + self.assertEqual(len(l.items()), len_before) + self.assertIn(pasted[0], l.items()) + self.assertIn(pasted[1], l.items()) + self.assertIn(sip.cast(pasted[0], QgsLayoutItemLabel).text(), ('label 1', 'label 2')) + self.assertIn(sip.cast(pasted[1], QgsLayoutItemLabel).text(), ('label 1', 'label 2')) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsmaplayer.py b/tests/src/python/test_qgsmaplayer.py index f700fe0e463..1be12d9a2fa 100644 --- a/tests/src/python/test_qgsmaplayer.py +++ b/tests/src/python/test_qgsmaplayer.py @@ -13,6 +13,7 @@ __copyright__ = 'Copyright 2017, The QGIS Project' __revision__ = '$Format:%H$' import qgis # NOQA +import tempfile from qgis.core import (QgsReadWriteContext, QgsVectorLayer, @@ -88,6 +89,22 @@ class TestQgsMapLayer(unittest.TestCase): self.assertTrue(layer2.hasAutoRefreshEnabled()) self.assertEqual(layer2.autoRefreshInterval(), 56) + def testReadWriteMetadata(self): + layer = QgsVectorLayer("Point?field=fldtxt:string", "layer", "memory") + m = layer.metadata() + # Only abstract, more tests are done in test_qgslayermetadata.py + m.setAbstract('My abstract') + layer.setMetadata(m) + self.assertTrue(layer.metadata().abstract(), 'My abstract') + destination = tempfile.NamedTemporaryFile(suffix='.qmd').name + message, status = layer.saveNamedMetadata(destination) + self.assertTrue(status, message) + + layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer", "memory") + message, status = layer2.loadNamedMetadata(destination) + self.assertTrue(status) + self.assertTrue(layer2.metadata().abstract(), 'My abstract') + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsmaprenderer.py b/tests/src/python/test_qgsmaprenderer.py index 1a1658febed..e59a226dcf7 100644 --- a/tests/src/python/test_qgsmaprenderer.py +++ b/tests/src/python/test_qgsmaprenderer.py @@ -129,6 +129,7 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) settings = QgsMapSettings() settings.setExtent(QgsRectangle(5, 25, 25, 45)) @@ -170,6 +171,7 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) settings = QgsMapSettings() settings.setExtent(QgsRectangle(5, 25, 25, 45)) @@ -192,6 +194,7 @@ class TestQgsMapRenderer(unittest.TestCase): layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") layer2.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer2.setLabelsEnabled(True) settings.setLayers([layer, layer2]) # second job should not be able to use label cache, since a new layer was added @@ -214,6 +217,7 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) settings = QgsMapSettings() settings.setExtent(QgsRectangle(5, 25, 25, 45)) @@ -257,10 +261,12 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") layer2.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer2.setLabelsEnabled(True) settings = QgsMapSettings() settings.setExtent(QgsRectangle(5, 25, 25, 45)) @@ -302,6 +308,7 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") @@ -346,6 +353,7 @@ class TestQgsMapRenderer(unittest.TestCase): labelSettings = QgsPalLayerSettings() labelSettings.fieldName = "fldtxt" layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings)) + layer.setLabelsEnabled(True) layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") @@ -355,6 +363,7 @@ class TestQgsMapRenderer(unittest.TestCase): format2.setBlendMode(QPainter.CompositionMode_SourceIn) labelSettings2.setFormat(format2) layer2.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings2)) + layer2.setLabelsEnabled(True) settings = QgsMapSettings() settings.setExtent(QgsRectangle(5, 25, 25, 45)) diff --git a/tests/src/python/test_qgsnewgeopackagelayerdialog.py b/tests/src/python/test_qgsnewgeopackagelayerdialog.py index 41aefec87c8..d5138360e15 100644 --- a/tests/src/python/test_qgsnewgeopackagelayerdialog.py +++ b/tests/src/python/test_qgsnewgeopackagelayerdialog.py @@ -21,7 +21,7 @@ from qgis.PyQt.QtWidgets import QLineEdit, QDialogButtonBox, QTreeWidget, QCombo from qgis.PyQt.QtTest import QTest from qgis.core import QgsProject, QgsSettings, QgsWkbTypes -from qgis.gui import QgsNewGeoPackageLayerDialog +from qgis.gui import QgsNewGeoPackageLayerDialog, QgsFileWidget from qgis.testing import start_app, unittest @@ -63,7 +63,7 @@ class TestPyQgsNewGeoPackageLayerDialog(unittest.TestCase): dialog = QgsNewGeoPackageLayerDialog() dialog.setProperty("hideDialogs", True) - mDatabaseEdit = dialog.findChild(QLineEdit, "mDatabaseEdit") + mDatabase = dialog.findChild(QgsFileWidget, "mDatabase") buttonBox = dialog.findChild(QDialogButtonBox, "buttonBox") ok_button = buttonBox.button(QDialogButtonBox.Ok) mTableNameEdit = dialog.findChild(QLineEdit, "mTableNameEdit") @@ -86,7 +86,7 @@ class TestPyQgsNewGeoPackageLayerDialog(unittest.TestCase): self.assertFalse(ok_button.isEnabled()) dbname = os.path.join(self.basetestpath, 'test.gpkg') - mDatabaseEdit.setText(dbname) + mDatabase.setFilePath(dbname) self.assertEqual(mTableNameEdit.text(), 'test') self.assertEqual(mLayerIdentifierEdit.text(), 'test') self.assertTrue(ok_button.isEnabled()) @@ -261,7 +261,7 @@ class TestPyQgsNewGeoPackageLayerDialog(unittest.TestCase): QgsProject.instance().removeAllMapLayers() # Try invalid path - mDatabaseEdit.setText('/this/is/invalid/test.gpkg') + mDatabase.setFilePath('/this/is/invalid/test.gpkg') self.accepted = False QTest.mouseClick(ok_button, Qt.LeftButton) self.assertFalse(self.accepted) diff --git a/tests/src/python/test_qgspallabeling_composer.py b/tests/src/python/test_qgspallabeling_composer.py deleted file mode 100644 index 454a32a3cd5..00000000000 --- a/tests/src/python/test_qgspallabeling_composer.py +++ /dev/null @@ -1,485 +0,0 @@ -# -*- coding: utf-8 -*- -"""QGIS unit tests for QgsPalLabeling: label rendering output via composer - -From build dir, run: ctest -R PyQgsPalLabelingComposer -V - -See /tests/testdata/labeling/README.rst for description. - -.. note:: This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -""" - -__author__ = 'Larry Shaffer' -__date__ = '2014/02/21' -__copyright__ = 'Copyright 2013, The QGIS Project' -# This will get replaced with a git SHA1 when you do a git archive -__revision__ = '$Format:%H$' - -import qgis # NOQA - -import sys -import os -import subprocess - -from qgis.PyQt.QtCore import QRect, QRectF, QSize, QSizeF, qDebug, QThreadPool -from qgis.PyQt.QtGui import QImage, QColor, QPainter -from qgis.PyQt.QtPrintSupport import QPrinter -from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator - -from qgis.core import QgsComposition, QgsMapSettings, QgsProject, QgsComposerMap, QgsVectorLayerSimpleLabeling - - -from utilities import ( - getTempfilePath, - getExecutablePath, - mapSettingsString -) - -from test_qgspallabeling_base import TestQgsPalLabeling, runSuite -from test_qgspallabeling_tests import ( - TestPointBase, - TestLineBase, - suiteTests -) - -# PDF-to-image utility -# look for Poppler w/ Cairo, then muPDF -# * Poppler w/ Cairo renders correctly -# * Poppler w/o Cairo does not always correctly render vectors in PDF to image -# * muPDF renders correctly, but sightly shifts colors -for util in [ - 'pdftocairo', - # 'mudraw', -]: - PDFUTIL = getExecutablePath(util) - if PDFUTIL: - break - -# noinspection PyUnboundLocalVariable -if not PDFUTIL: - raise Exception('PDF-to-image utility not found on PATH: ' - 'install Poppler (with Cairo)') - - -# output kind enum -# noinspection PyClassHasNoInit -class OutputKind(): - Img, Svg, Pdf = list(range(3)) - - -# noinspection PyShadowingNames -class TestComposerBase(TestQgsPalLabeling): - - layer = None - """:type: QgsVectorLayer""" - - @classmethod - def setUpClass(cls): - if not cls._BaseSetup: - TestQgsPalLabeling.setUpClass() - # the blue background (set via layer style) to match renderchecker's - TestQgsPalLabeling.loadFeatureLayer('background', True) - cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) - - @classmethod - def tearDownClass(cls): - """Run after all tests""" - TestQgsPalLabeling.tearDownClass() - cls.removeMapLayer(cls.layer) - cls.layer = None - # avoid crash on finish, probably related to https://bugreports.qt.io/browse/QTBUG-35760 - QThreadPool.globalInstance().waitForDone() - - def setUp(self): - """Run before each test.""" - super(TestComposerBase, self).setUp() - self._TestImage = '' - # ensure per test map settings stay encapsulated - self._TestMapSettings = self.cloneMapSettings(self._MapSettings) - self._Mismatch = 0 - self._ColorTol = 0 - self._Mismatches.clear() - self._ColorTols.clear() - - def _set_up_composition(self, width, height, dpi, engine_settings): - # set up composition and add map - self._c = QgsComposition(QgsProject.instance()) - """:type: QgsComposition""" - # self._c.setUseAdvancedEffects(False) - self._c.setPrintResolution(dpi) - # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi - paperw = width * 25.4 / dpi - paperh = height * 25.4 / dpi - self._c.setPaperSize(paperw, paperh) - # NOTE: do not use QgsComposerMap(self._c, 0, 0, paperw, paperh) since - # it only takes integers as parameters and the composition will grow - # larger based upon union of item scene rectangles and a slight buffer - # see end of QgsComposition::compositionBounds() - # add map as small graphics item first, then set its scene QRectF later - self._cmap = QgsComposerMap(self._c, 10, 10, 10, 10) - """:type: QgsComposerMap""" - self._cmap.setFrameEnabled(False) - self._cmap.setLayers(self._TestMapSettings.layers()) - self._c.addComposerMap(self._cmap) - # now expand map to fill page and set its extent - self._cmap.setSceneRect(QRectF(0, 0, paperw, paperw)) - self._cmap.setNewExtent(self.aoiExtent()) - # self._cmap.updateCachedImage() - self._c.setPlotStyle(QgsComposition.Print) - # composition takes labeling engine settings from project - QgsProject.instance().setLabelingEngineSettings(engine_settings) - - # noinspection PyUnusedLocal - def _get_composer_image(self, width, height, dpi): - image = QImage(QSize(width, height), - self._TestMapSettings.outputImageFormat()) - image.fill(QColor(152, 219, 249).rgb()) - image.setDotsPerMeterX(dpi / 25.4 * 1000) - image.setDotsPerMeterY(dpi / 25.4 * 1000) - - p = QPainter(image) - p.setRenderHint( - QPainter.Antialiasing, - self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) - ) - self._c.renderPage(p, 0) - p.end() - - # image = self._c.printPageAsRaster(0) - # """:type: QImage""" - - if image.isNull(): - return False, '' - - filepath = getTempfilePath('png') - res = image.save(filepath, 'png') - if not res: - os.unlink(filepath) - filepath = '' - - return res, filepath - - def _get_composer_svg_image(self, width, height, dpi): - # from qgscomposer.cpp, QgsComposer::on_mActionExportAsSVG_triggered, - # near end of function - svgpath = getTempfilePath('svg') - temp_size = os.path.getsize(svgpath) - - svg_g = QSvgGenerator() - # noinspection PyArgumentList - svg_g.setTitle(QgsProject.instance().title()) - svg_g.setFileName(svgpath) - svg_g.setSize(QSize(width, height)) - svg_g.setViewBox(QRect(0, 0, width, height)) - svg_g.setResolution(dpi) - - sp = QPainter(svg_g) - self._c.renderPage(sp, 0) - sp.end() - - if temp_size == os.path.getsize(svgpath): - return False, '' - - image = QImage(width, height, self._TestMapSettings.outputImageFormat()) - image.fill(QColor(152, 219, 249).rgb()) - image.setDotsPerMeterX(dpi / 25.4 * 1000) - image.setDotsPerMeterY(dpi / 25.4 * 1000) - - svgr = QSvgRenderer(svgpath) - p = QPainter(image) - p.setRenderHint( - QPainter.Antialiasing, - self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) - ) - p.setRenderHint(QPainter.TextAntialiasing) - svgr.render(p) - p.end() - - filepath = getTempfilePath('png') - res = image.save(filepath, 'png') - if not res: - os.unlink(filepath) - filepath = '' - # TODO: remove .svg file as well? - - return res, filepath - - def _get_composer_pdf_image(self, width, height, dpi): - pdfpath = getTempfilePath('pdf') - temp_size = os.path.getsize(pdfpath) - - p = QPrinter() - p.setOutputFormat(QPrinter.PdfFormat) - p.setOutputFileName(pdfpath) - p.setPaperSize(QSizeF(self._c.paperWidth(), self._c.paperHeight()), - QPrinter.Millimeter) - p.setFullPage(True) - p.setColorMode(QPrinter.Color) - p.setResolution(self._c.printResolution()) - - pdf_p = QPainter(p) - # page_mm = p.pageRect(QPrinter.Millimeter) - # page_px = p.pageRect(QPrinter.DevicePixel) - # self._c.render(pdf_p, page_px, page_mm) - self._c.renderPage(pdf_p, 0) - pdf_p.end() - - if temp_size == os.path.getsize(pdfpath): - return False, '' - - filepath = getTempfilePath('png') - # Poppler (pdftocairo or pdftoppm): - # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase - # muPDF (mudraw): - # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf - if PDFUTIL.strip().endswith('pdftocairo'): - filebase = os.path.join( - os.path.dirname(filepath), - os.path.splitext(os.path.basename(filepath))[0] - ) - call = [ - PDFUTIL, '-png', '-singlefile', '-r', str(dpi), - '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), - pdfpath, filebase - ] - elif PDFUTIL.strip().endswith('mudraw'): - call = [ - PDFUTIL, '-c', 'rgba', - '-r', str(dpi), '-w', str(width), '-h', str(height), - # '-b', '8', - '-o', filepath, pdfpath - ] - else: - return False, '' - - qDebug("_get_composer_pdf_image call: {0}".format(' '.join(call))) - res = False - try: - subprocess.check_call(call) - res = True - except subprocess.CalledProcessError as e: - qDebug("_get_composer_pdf_image failed!\n" - "cmd: {0}\n" - "returncode: {1}\n" - "message: {2}".format(e.cmd, e.returncode, e.message)) - - if not res: - os.unlink(filepath) - filepath = '' - - return res, filepath - - def get_composer_output(self, kind): - ms = self._TestMapSettings - osize = ms.outputSize() - width, height, dpi = osize.width(), osize.height(), ms.outputDpi() - self._set_up_composition(width, height, dpi, ms.labelingEngineSettings()) - if kind == OutputKind.Svg: - return self._get_composer_svg_image(width, height, dpi) - elif kind == OutputKind.Pdf: - return self._get_composer_pdf_image(width, height, dpi) - else: # OutputKind.Img - return self._get_composer_image(width, height, dpi) - - # noinspection PyUnusedLocal - def checkTest(self, **kwargs): - self.layer.setLabeling(QgsVectorLayerSimpleLabeling(self.lyr)) - - ms = self._MapSettings # class settings - settings_type = 'Class' - if self._TestMapSettings is not None: - ms = self._TestMapSettings # per test settings - settings_type = 'Test' - if 'PAL_VERBOSE' in os.environ: - qDebug('MapSettings type: {0}'.format(settings_type)) - qDebug(mapSettingsString(ms)) - - res_m, self._TestImage = self.get_composer_output(self._TestKind) - self.assertTrue(res_m, 'Failed to retrieve/save output from composer') - self.saveControlImage(self._TestImage) - mismatch = 0 - if 'PAL_NO_MISMATCH' not in os.environ: - # some mismatch expected - mismatch = self._Mismatch if self._Mismatch else 20 - if self._TestGroup in self._Mismatches: - mismatch = self._Mismatches[self._TestGroup] - colortol = 0 - if 'PAL_NO_COLORTOL' not in os.environ: - colortol = self._ColorTol if self._ColorTol else 0 - if self._TestGroup in self._ColorTols: - colortol = self._ColorTols[self._TestGroup] - self.assertTrue(*self.renderCheck(mismatch=mismatch, - colortol=colortol, - imgpath=self._TestImage)) - - -class TestComposerPointBase(TestComposerBase): - - @classmethod - def setUpClass(cls): - TestComposerBase.setUpClass() - cls.layer = TestQgsPalLabeling.loadFeatureLayer('point') - - -class TestComposerImagePoint(TestComposerPointBase, TestPointBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerImagePoint, self).setUp() - self._TestKind = OutputKind.Img - self.configTest('pal_composer', 'sp_img') - - -class TestComposerImageVsCanvasPoint(TestComposerPointBase, TestPointBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerImageVsCanvasPoint, self).setUp() - self._TestKind = OutputKind.Img - self.configTest('pal_canvas', 'sp') - - -class TestComposerSvgPoint(TestComposerPointBase, TestPointBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerSvgPoint, self).setUp() - self._TestKind = OutputKind.Svg - self.configTest('pal_composer', 'sp_svg') - - -class TestComposerSvgVsComposerPoint(TestComposerPointBase, TestPointBase): - - """ - Compare only to composer image, which is already compared to canvas point - """ - - def setUp(self): - """Run before each test.""" - super(TestComposerSvgVsComposerPoint, self).setUp() - self._TestKind = OutputKind.Svg - self.configTest('pal_composer', 'sp_img') - self._ColorTol = 4 - - -class TestComposerPdfPoint(TestComposerPointBase, TestPointBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerPdfPoint, self).setUp() - self._TestKind = OutputKind.Pdf - self.configTest('pal_composer', 'sp_pdf') - - -class TestComposerPdfVsComposerPoint(TestComposerPointBase, TestPointBase): - - """ - Compare only to composer image, which is already compared to canvas point - """ - - def setUp(self): - """Run before each test.""" - super(TestComposerPdfVsComposerPoint, self).setUp() - self._TestKind = OutputKind.Pdf - self.configTest('pal_composer', 'sp_img') - self._Mismatch = 50 - self._ColorTol = 18 - - -class TestComposerLineBase(TestComposerBase): - - @classmethod - def setUpClass(cls): - TestComposerBase.setUpClass() - cls.layer = TestQgsPalLabeling.loadFeatureLayer('line') - - -class TestComposerImageLine(TestComposerLineBase, TestLineBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerImageLine, self).setUp() - self._TestKind = OutputKind.Img - self.configTest('pal_composer_line', 'sp_img') - - -class TestComposerImageVsCanvasLine(TestComposerLineBase, TestLineBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerImageVsCanvasLine, self).setUp() - self._TestKind = OutputKind.Img - self.configTest('pal_canvas_line', 'sp') - - -class TestComposerSvgLine(TestComposerLineBase, TestLineBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerSvgLine, self).setUp() - self._TestKind = OutputKind.Svg - self.configTest('pal_composer_line', 'sp_svg') - - -class TestComposerSvgVsComposerLine(TestComposerLineBase, TestLineBase): - - """ - Compare only to composer image, which is already compared to canvas line - """ - - def setUp(self): - """Run before each test.""" - super(TestComposerSvgVsComposerLine, self).setUp() - self._TestKind = OutputKind.Svg - self.configTest('pal_composer_line', 'sp_img') - self._ColorTol = 4 - - -class TestComposerPdfLine(TestComposerLineBase, TestLineBase): - - def setUp(self): - """Run before each test.""" - super(TestComposerPdfLine, self).setUp() - self._TestKind = OutputKind.Pdf - self.configTest('pal_composer_line', 'sp_pdf') - - -class TestComposerPdfVsComposerLine(TestComposerLineBase, TestLineBase): - - """ - Compare only to composer image, which is already compared to canvas line - """ - - def setUp(self): - """Run before each test.""" - super(TestComposerPdfVsComposerLine, self).setUp() - self._TestKind = OutputKind.Pdf - self.configTest('pal_composer_line', 'sp_img') - self._Mismatch = 50 - self._ColorTol = 18 - - -if __name__ == '__main__': - # NOTE: unless PAL_SUITE env var is set all test class methods will be run - # SEE: test_qgspallabeling_tests.suiteTests() to define suite - st = suiteTests() - sp_i = ['TestComposerImagePoint.' + t for t in st['sp_suite']] - sp_ivs = ['TestComposerImageVsCanvasPoint.' + t for t in st['sp_vs_suite']] - sp_s = ['TestComposerSvgPoint.' + t for t in st['sp_suite']] - sp_svs = ['TestComposerSvgVsComposerPoint.' + t for t in st['sp_vs_suite']] - sp_p = ['TestComposerPdfPoint.' + t for t in st['sp_suite']] - sp_pvs = ['TestComposerPdfVsComposerPoint.' + t for t in st['sp_vs_suite']] - suite = [] - - # extended separately for finer control of PAL_SUITE (comment-out undesired) - suite.extend(sp_i) - suite.extend(sp_ivs) - suite.extend(sp_s) - suite.extend(sp_svs) - suite.extend(sp_p) - suite.extend(sp_pvs) - - res = runSuite(sys.modules[__name__], suite) - sys.exit(not res.wasSuccessful()) diff --git a/tests/src/python/test_qgspallabeling_layout.py b/tests/src/python/test_qgspallabeling_layout.py new file mode 100644 index 00000000000..8f79da0dfc4 --- /dev/null +++ b/tests/src/python/test_qgspallabeling_layout.py @@ -0,0 +1,495 @@ +# -*- coding: utf-8 -*- +"""QGIS unit tests for QgsPalLabeling: label rendering output via layout + +From build dir, run: ctest -R PyQgsPalLabelingLayout -V + +See /tests/testdata/labeling/README.rst for description. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" + +__author__ = 'Larry Shaffer' +__date__ = '2014/02/21' +__copyright__ = 'Copyright 2013, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +import sys +import os +import subprocess + +from qgis.PyQt.QtCore import QRect, QRectF, QSize, QSizeF, qDebug, QThreadPool +from qgis.PyQt.QtGui import QImage, QColor, QPainter +from qgis.PyQt.QtPrintSupport import QPrinter +from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator + +from qgis.core import (QgsLayout, + QgsLayoutItemPage, + QgsLayoutSize, + QgsLayoutItemMap, + QgsLayoutExporter, + QgsMapSettings, + QgsProject, + QgsVectorLayerSimpleLabeling) + + +from utilities import ( + getTempfilePath, + getExecutablePath, + mapSettingsString +) + +from test_qgspallabeling_base import TestQgsPalLabeling, runSuite +from test_qgspallabeling_tests import ( + TestPointBase, + TestLineBase, + suiteTests +) + +# PDF-to-image utility +# look for Poppler w/ Cairo, then muPDF +# * Poppler w/ Cairo renders correctly +# * Poppler w/o Cairo does not always correctly render vectors in PDF to image +# * muPDF renders correctly, but sightly shifts colors +for util in [ + 'pdftocairo', + # 'mudraw', +]: + PDFUTIL = getExecutablePath(util) + if PDFUTIL: + break + +# noinspection PyUnboundLocalVariable +if not PDFUTIL: + raise Exception('PDF-to-image utility not found on PATH: ' + 'install Poppler (with Cairo)') + + +# output kind enum +# noinspection PyClassHasNoInit +class OutputKind(): + Img, Svg, Pdf = list(range(3)) + + +# noinspection PyShadowingNames +class TestLayoutBase(TestQgsPalLabeling): + + layer = None + """:type: QgsVectorLayer""" + + @classmethod + def setUpClass(cls): + if not cls._BaseSetup: + TestQgsPalLabeling.setUpClass() + # the blue background (set via layer style) to match renderchecker's + TestQgsPalLabeling.loadFeatureLayer('background', True) + cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) + + @classmethod + def tearDownClass(cls): + """Run after all tests""" + TestQgsPalLabeling.tearDownClass() + cls.removeMapLayer(cls.layer) + cls.layer = None + # avoid crash on finish, probably related to https://bugreports.qt.io/browse/QTBUG-35760 + QThreadPool.globalInstance().waitForDone() + + def setUp(self): + """Run before each test.""" + super(TestLayoutBase, self).setUp() + self._TestImage = '' + # ensure per test map settings stay encapsulated + self._TestMapSettings = self.cloneMapSettings(self._MapSettings) + self._Mismatch = 0 + self._ColorTol = 0 + self._Mismatches.clear() + self._ColorTols.clear() + + def _set_up_composition(self, width, height, dpi, engine_settings): + # set up layout and add map + self._c = QgsLayout(QgsProject.instance()) + """:type: QgsLayout""" + # self._c.setUseAdvancedEffects(False) + self._c.renderContext().setDpi(dpi) + # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi + paperw = width * 25.4 / dpi + paperh = height * 25.4 / dpi + page = QgsLayoutItemPage(self._c) + page.attemptResize(QgsLayoutSize(paperw, paperh)) + self._c.pageCollection().addPage(page) + # NOTE: do not use QgsLayoutItemMap(self._c, 0, 0, paperw, paperh) since + # it only takes integers as parameters and the composition will grow + # larger based upon union of item scene rectangles and a slight buffer + # see end of QgsComposition::compositionBounds() + # add map as small graphics item first, then set its scene QRectF later + self._cmap = QgsLayoutItemMap(self._c) + self._cmap.attemptSetSceneRect(QRectF(10, 10, 10, 10)) + """:type: QgsLayoutItemMap""" + self._cmap.setFrameEnabled(False) + self._cmap.setLayers(self._TestMapSettings.layers()) + self._c.addLayoutItem(self._cmap) + # now expand map to fill page and set its extent + self._cmap.attemptSetSceneRect(QRectF(0, 0, paperw, paperw)) + self._cmap.setExtent(self.aoiExtent()) + # self._cmap.updateCachedImage() + # composition takes labeling engine settings from project + QgsProject.instance().setLabelingEngineSettings(engine_settings) + + # noinspection PyUnusedLocal + def _get_layout_image(self, width, height, dpi): + image = QImage(QSize(width, height), + self._TestMapSettings.outputImageFormat()) + image.fill(QColor(152, 219, 249).rgb()) + image.setDotsPerMeterX(dpi / 25.4 * 1000) + image.setDotsPerMeterY(dpi / 25.4 * 1000) + + p = QPainter(image) + p.setRenderHint( + QPainter.Antialiasing, + self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) + ) + exporter = QgsLayoutExporter(self._c) + exporter.renderPage(p, 0) + p.end() + + # image = self._c.printPageAsRaster(0) + # """:type: QImage""" + + if image.isNull(): + return False, '' + + filepath = getTempfilePath('png') + res = image.save(filepath, 'png') + if not res: + os.unlink(filepath) + filepath = '' + + return res, filepath + + def _get_layout_svg_image(self, width, height, dpi): + svgpath = getTempfilePath('svg') + temp_size = os.path.getsize(svgpath) + + svg_g = QSvgGenerator() + # noinspection PyArgumentList + svg_g.setTitle(QgsProject.instance().title()) + svg_g.setFileName(svgpath) + svg_g.setSize(QSize(width, height)) + svg_g.setViewBox(QRect(0, 0, width, height)) + svg_g.setResolution(dpi) + + sp = QPainter(svg_g) + exporter = QgsLayoutExporter(self._c) + exporter.renderPage(sp, 0) + sp.end() + + if temp_size == os.path.getsize(svgpath): + return False, '' + + image = QImage(width, height, self._TestMapSettings.outputImageFormat()) + image.fill(QColor(152, 219, 249).rgb()) + image.setDotsPerMeterX(dpi / 25.4 * 1000) + image.setDotsPerMeterY(dpi / 25.4 * 1000) + + svgr = QSvgRenderer(svgpath) + p = QPainter(image) + p.setRenderHint( + QPainter.Antialiasing, + self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) + ) + p.setRenderHint(QPainter.TextAntialiasing) + svgr.render(p) + p.end() + + filepath = getTempfilePath('png') + res = image.save(filepath, 'png') + if not res: + os.unlink(filepath) + filepath = '' + # TODO: remove .svg file as well? + + return res, filepath + + def _get_layout_pdf_image(self, width, height, dpi): + pdfpath = getTempfilePath('pdf') + temp_size = os.path.getsize(pdfpath) + + p = QPrinter() + p.setOutputFormat(QPrinter.PdfFormat) + p.setOutputFileName(pdfpath) + p.setPaperSize(QSizeF(self._c.pageCollection().page(0).sizeWithUnits().width(), self._c.pageCollection().page(0).sizeWithUnits().height()), + QPrinter.Millimeter) + p.setFullPage(True) + p.setColorMode(QPrinter.Color) + p.setResolution(self._c.renderContext().dpi()) + + pdf_p = QPainter(p) + # page_mm = p.pageRect(QPrinter.Millimeter) + # page_px = p.pageRect(QPrinter.DevicePixel) + # self._c.render(pdf_p, page_px, page_mm) + exporter = QgsLayoutExporter(self._c) + exporter.renderPage(pdf_p, 0) + pdf_p.end() + + if temp_size == os.path.getsize(pdfpath): + return False, '' + + filepath = getTempfilePath('png') + # Poppler (pdftocairo or pdftoppm): + # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase + # muPDF (mudraw): + # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf + if PDFUTIL.strip().endswith('pdftocairo'): + filebase = os.path.join( + os.path.dirname(filepath), + os.path.splitext(os.path.basename(filepath))[0] + ) + call = [ + PDFUTIL, '-png', '-singlefile', '-r', str(dpi), + '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), + pdfpath, filebase + ] + elif PDFUTIL.strip().endswith('mudraw'): + call = [ + PDFUTIL, '-c', 'rgba', + '-r', str(dpi), '-w', str(width), '-h', str(height), + # '-b', '8', + '-o', filepath, pdfpath + ] + else: + return False, '' + + qDebug("_get_layout_pdf_image call: {0}".format(' '.join(call))) + res = False + try: + subprocess.check_call(call) + res = True + except subprocess.CalledProcessError as e: + qDebug("_get_layout_pdf_image failed!\n" + "cmd: {0}\n" + "returncode: {1}\n" + "message: {2}".format(e.cmd, e.returncode, e.message)) + + if not res: + os.unlink(filepath) + filepath = '' + + return res, filepath + + def get_layout_output(self, kind): + ms = self._TestMapSettings + osize = ms.outputSize() + width, height, dpi = osize.width(), osize.height(), ms.outputDpi() + self._set_up_composition(width, height, dpi, ms.labelingEngineSettings()) + if kind == OutputKind.Svg: + return self._get_layout_svg_image(width, height, dpi) + elif kind == OutputKind.Pdf: + return self._get_layout_pdf_image(width, height, dpi) + else: # OutputKind.Img + return self._get_layout_image(width, height, dpi) + + # noinspection PyUnusedLocal + def checkTest(self, **kwargs): + self.layer.setLabeling(QgsVectorLayerSimpleLabeling(self.lyr)) + + ms = self._MapSettings # class settings + settings_type = 'Class' + if self._TestMapSettings is not None: + ms = self._TestMapSettings # per test settings + settings_type = 'Test' + if 'PAL_VERBOSE' in os.environ: + qDebug('MapSettings type: {0}'.format(settings_type)) + qDebug(mapSettingsString(ms)) + + res_m, self._TestImage = self.get_layout_output(self._TestKind) + self.assertTrue(res_m, 'Failed to retrieve/save output from layout') + self.saveControlImage(self._TestImage) + mismatch = 0 + if 'PAL_NO_MISMATCH' not in os.environ: + # some mismatch expected + mismatch = self._Mismatch if self._Mismatch else 20 + if self._TestGroup in self._Mismatches: + mismatch = self._Mismatches[self._TestGroup] + colortol = 0 + if 'PAL_NO_COLORTOL' not in os.environ: + colortol = self._ColorTol if self._ColorTol else 0 + if self._TestGroup in self._ColorTols: + colortol = self._ColorTols[self._TestGroup] + self.assertTrue(*self.renderCheck(mismatch=mismatch, + colortol=colortol, + imgpath=self._TestImage)) + + +class TestLayoutPointBase(TestLayoutBase): + + @classmethod + def setUpClass(cls): + TestLayoutBase.setUpClass() + cls.layer = TestQgsPalLabeling.loadFeatureLayer('point') + + +class TestLayoutImagePoint(TestLayoutPointBase, TestPointBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutImagePoint, self).setUp() + self._TestKind = OutputKind.Img + self.configTest('pal_composer', 'sp_img') + + +class TestLayoutImageVsCanvasPoint(TestLayoutPointBase, TestPointBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutImageVsCanvasPoint, self).setUp() + self._TestKind = OutputKind.Img + self.configTest('pal_canvas', 'sp') + + +class TestLayoutSvgPoint(TestLayoutPointBase, TestPointBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutSvgPoint, self).setUp() + self._TestKind = OutputKind.Svg + self.configTest('pal_composer', 'sp_svg') + + +class TestLayoutSvgVsLayoutPoint(TestLayoutPointBase, TestPointBase): + + """ + Compare only to layout image, which is already compared to canvas point + """ + + def setUp(self): + """Run before each test.""" + super(TestLayoutSvgVsLayoutPoint, self).setUp() + self._TestKind = OutputKind.Svg + self.configTest('pal_composer', 'sp_img') + self._ColorTol = 4 + + +class TestLayoutPdfPoint(TestLayoutPointBase, TestPointBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutPdfPoint, self).setUp() + self._TestKind = OutputKind.Pdf + self.configTest('pal_composer', 'sp_pdf') + + +class TestLayoutPdfVsLayoutPoint(TestLayoutPointBase, TestPointBase): + + """ + Compare only to layout image, which is already compared to canvas point + """ + + def setUp(self): + """Run before each test.""" + super(TestLayoutPdfVsLayoutPoint, self).setUp() + self._TestKind = OutputKind.Pdf + self.configTest('pal_composer', 'sp_img') + self._Mismatch = 50 + self._ColorTol = 18 + + +class TestLayoutLineBase(TestLayoutBase): + + @classmethod + def setUpClass(cls): + TestLayoutBase.setUpClass() + cls.layer = TestQgsPalLabeling.loadFeatureLayer('line') + + +class TestLayoutImageLine(TestLayoutLineBase, TestLineBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutImageLine, self).setUp() + self._TestKind = OutputKind.Img + self.configTest('pal_composer_line', 'sp_img') + + +class TestLayoutImageVsCanvasLine(TestLayoutLineBase, TestLineBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutImageVsCanvasLine, self).setUp() + self._TestKind = OutputKind.Img + self.configTest('pal_canvas_line', 'sp') + + +class TestLayoutSvgLine(TestLayoutLineBase, TestLineBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutSvgLine, self).setUp() + self._TestKind = OutputKind.Svg + self.configTest('pal_composer_line', 'sp_svg') + + +class TestLayoutSvgVsLayoutLine(TestLayoutLineBase, TestLineBase): + + """ + Compare only to layout image, which is already compared to canvas line + """ + + def setUp(self): + """Run before each test.""" + super(TestLayoutSvgVsLayoutLine, self).setUp() + self._TestKind = OutputKind.Svg + self.configTest('pal_composer_line', 'sp_img') + self._ColorTol = 4 + + +class TestLayoutPdfLine(TestLayoutLineBase, TestLineBase): + + def setUp(self): + """Run before each test.""" + super(TestLayoutPdfLine, self).setUp() + self._TestKind = OutputKind.Pdf + self.configTest('pal_composer_line', 'sp_pdf') + + +class TestLayoutPdfVsLayoutLine(TestLayoutLineBase, TestLineBase): + + """ + Compare only to layout image, which is already compared to canvas line + """ + + def setUp(self): + """Run before each test.""" + super(TestLayoutPdfVsLayoutLine, self).setUp() + self._TestKind = OutputKind.Pdf + self.configTest('pal_composer_line', 'sp_img') + self._Mismatch = 50 + self._ColorTol = 18 + + +if __name__ == '__main__': + # NOTE: unless PAL_SUITE env var is set all test class methods will be run + # SEE: test_qgspallabeling_tests.suiteTests() to define suite + st = suiteTests() + sp_i = ['TestLayoutImagePoint.' + t for t in st['sp_suite']] + sp_ivs = ['TestLayoutImageVsCanvasPoint.' + t for t in st['sp_vs_suite']] + sp_s = ['TestLayoutSvgPoint.' + t for t in st['sp_suite']] + sp_svs = ['TestLayoutSvgVsLayoutPoint.' + t for t in st['sp_vs_suite']] + sp_p = ['TestLayoutPdfPoint.' + t for t in st['sp_suite']] + sp_pvs = ['TestLayoutPdfVsLayoutPoint.' + t for t in st['sp_vs_suite']] + suite = [] + + # extended separately for finer control of PAL_SUITE (comment-out undesired) + suite.extend(sp_i) + suite.extend(sp_ivs) + suite.extend(sp_s) + suite.extend(sp_svs) + suite.extend(sp_p) + suite.extend(sp_pvs) + + res = runSuite(sys.modules[__name__], suite) + sys.exit(not res.wasSuccessful()) diff --git a/tests/src/python/test_qgspallabeling_tests.py b/tests/src/python/test_qgspallabeling_tests.py index 6df62260d0e..681015e0c7d 100644 --- a/tests/src/python/test_qgspallabeling_tests.py +++ b/tests/src/python/test_qgspallabeling_tests.py @@ -314,7 +314,7 @@ class TestLineBase(object): def suiteTests(): """ Use to define which tests are run when PAL_SUITE is set. - Use sp_vs_suite for comparison of server and composer outputs to canvas + Use sp_vs_suite for comparison of server and layout outputs to canvas """ sp_suite = [ # 'test_default_label', diff --git a/tests/src/python/test_qgsrasterfilewriter.py b/tests/src/python/test_qgsrasterfilewriter.py index ec62e8a6a2c..5e0d27f3d6a 100644 --- a/tests/src/python/test_qgsrasterfilewriter.py +++ b/tests/src/python/test_qgsrasterfilewriter.py @@ -114,6 +114,32 @@ class TestQgsRasterFileWriter(unittest.TestCase): self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GTiff'), ['tiff', 'tif']) self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GPKG'), ['gpkg']) + def testSupportedFiltersAndFormat(self): + # test with formats in recommended order + formats = QgsRasterFileWriter.supportedFiltersAndFormats(QgsRasterFileWriter.SortRecommended) + self.assertEqual(formats[0].filterString, 'GeoTIFF (*.tif *.TIF *.tiff *.TIFF)') + self.assertEqual(formats[0].driverName, 'GTiff') + self.assertTrue('netCDF' in [f.driverName for f in formats]) + + # alphabetical sorting + formats2 = QgsRasterFileWriter.supportedFiltersAndFormats(QgsRasterFileWriter.RasterFormatOptions()) + self.assertTrue(formats2[0].driverName < formats2[1].driverName) + self.assertCountEqual([f.driverName for f in formats], [f.driverName for f in formats2]) + self.assertNotEqual(formats2[0].driverName, 'GTiff') + + def testSupportedFormatExtensions(self): + formats = QgsRasterFileWriter.supportedFormatExtensions() + self.assertTrue('tif' in formats) + self.assertFalse('exe' in formats) + self.assertEqual(formats[0], 'tif') + self.assertTrue('nc' in formats) + + # alphabetical sorting + formats2 = QgsRasterFileWriter.supportedFormatExtensions(QgsRasterFileWriter.RasterFormatOptions()) + self.assertTrue(formats2[1] < formats2[2]) + self.assertCountEqual(formats, formats2) + self.assertNotEqual(formats2[0], 'tif') + def testImportIntoGpkg(self): # init target file test_gpkg = tempfile.mktemp(suffix='.gpkg', dir=self.testDataDir) diff --git a/tests/src/python/test_qgsrelation.py b/tests/src/python/test_qgsrelation.py index 892eb4f9e11..7945ce0ca6d 100644 --- a/tests/src/python/test_qgsrelation.py +++ b/tests/src/python/test_qgsrelation.py @@ -34,15 +34,15 @@ def createReferencingLayer(): "referencinglayer", "memory") pr = layer.dataProvider() f1 = QgsFeature() - f1.setFields(layer.pendingFields()) + f1.setFields(layer.fields()) f1.setAttributes(["test1", 123]) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) f2 = QgsFeature() - f2.setFields(layer.pendingFields()) + f2.setFields(layer.fields()) f2.setAttributes(["test2", 123]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(101, 201))) f3 = QgsFeature() - f3.setFields(layer.pendingFields()) + f3.setFields(layer.fields()) f3.setAttributes(["foobar'bar", 124]) f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(101, 201))) assert pr.addFeatures([f1, f2, f3]) @@ -55,15 +55,15 @@ def createReferencedLayer(): "referencedlayer", "memory") pr = layer.dataProvider() f1 = QgsFeature() - f1.setFields(layer.pendingFields()) + f1.setFields(layer.fields()) f1.setAttributes(["foo", 123, 321]) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 1))) f2 = QgsFeature() - f2.setFields(layer.pendingFields()) + f2.setFields(layer.fields()) f2.setAttributes(["bar", 456, 654]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2))) f3 = QgsFeature() - f3.setFields(layer.pendingFields()) + f3.setFields(layer.fields()) f3.setAttributes(["foobar'bar", 789, 554]) f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 3))) assert pr.addFeatures([f1, f2, f3]) diff --git a/tests/src/python/test_qgsrendercontext.py b/tests/src/python/test_qgsrendercontext.py index e487f882780..803fb1763fa 100644 --- a/tests/src/python/test_qgsrendercontext.py +++ b/tests/src/python/test_qgsrendercontext.py @@ -20,7 +20,8 @@ from qgis.core import (QgsRenderContext, QgsRectangle, QgsPointXY, QgsCoordinateReferenceSystem, QgsMapUnitScale, - QgsUnitTypes) + QgsUnitTypes, + QgsProject) from qgis.PyQt.QtCore import QSize from qgis.PyQt.QtGui import QPainter, QImage from qgis.testing import start_app, unittest @@ -65,7 +66,7 @@ class TestQgsRenderContext(unittest.TestCase): length_wsg84_mapunits = 0.00001473026350140572 meters_test = 2.40 da_wsg84 = QgsDistanceArea() - da_wsg84.setSourceCrs(crs_wsg84) + da_wsg84.setSourceCrs(crs_wsg84, QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) length_meter_mapunits = da_wsg84.measureLineProjected(point_berlin_wsg84, 1.0, (math.pi / 2)) diff --git a/tests/src/python/test_qgsrenderer.py b/tests/src/python/test_qgsrenderer.py index e105defc977..ce1d9bae1ff 100644 --- a/tests/src/python/test_qgsrenderer.py +++ b/tests/src/python/test_qgsrenderer.py @@ -31,11 +31,11 @@ def createReferencingLayer(): "referencinglayer", "memory") pr = layer.dataProvider() f1 = QgsFeature() - f1.setFields(layer.pendingFields()) + f1.setFields(layer.fields()) f1.setAttributes(["test1", 123]) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) f2 = QgsFeature() - f2.setFields(layer.pendingFields()) + f2.setFields(layer.fields()) f2.setAttributes(["test2", 123]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(101, 201))) assert pr.addFeatures([f1, f2]) diff --git a/tests/src/python/test_qgsreport.py b/tests/src/python/test_qgsreport.py new file mode 100644 index 00000000000..bfcf3ccd608 --- /dev/null +++ b/tests/src/python/test_qgsreport.py @@ -0,0 +1,1059 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsReport + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '29/12/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsProject, + QgsLayout, + QgsReport, + QgsReportSectionLayout, + QgsReportSectionFieldGroup, + QgsVectorLayer, + QgsField, + QgsFeature, + QgsReadWriteContext, + QgsUnitTypes) +from qgis.testing import start_app, unittest +from qgis.PyQt.QtXml import QDomDocument + +start_app() + + +class TestQgsReport(unittest.TestCase): + + def testGettersSetters(self): + p = QgsProject() + r = QgsReport(p) + + self.assertEqual(r.layoutProject(), p) + self.assertEqual(r.project(), p) + + r.setHeaderEnabled(True) + self.assertTrue(r.headerEnabled()) + + header = QgsLayout(p) + r.setHeader(header) + self.assertEqual(r.header(), header) + + r.setFooterEnabled(True) + self.assertTrue(r.footerEnabled()) + + footer = QgsLayout(p) + r.setFooter(footer) + self.assertEqual(r.footer(), footer) + + def testchildSections(self): + p = QgsProject() + r = QgsReport(p) + self.assertEqual(r.childCount(), 0) + self.assertEqual(r.childSections(), []) + self.assertIsNone(r.childSection(-1)) + self.assertIsNone(r.childSection(1)) + self.assertIsNone(r.childSection(0)) + + # try deleting non-existent childSections + r.removeChildAt(-1) + r.removeChildAt(0) + r.removeChildAt(100) + r.removeChild(None) + + # append child + child1 = QgsReportSectionLayout() + self.assertIsNone(child1.project()) + r.appendChild(child1) + self.assertEqual(r.childCount(), 1) + self.assertEqual(r.childSections(), [child1]) + self.assertEqual(r.childSection(0), child1) + self.assertEqual(child1.parentSection(), r) + self.assertEqual(child1.row(), 0) + self.assertEqual(child1.project(), p) + child2 = QgsReportSectionLayout() + r.appendChild(child2) + self.assertEqual(r.childCount(), 2) + self.assertEqual(r.childSections(), [child1, child2]) + self.assertEqual(r.childSection(1), child2) + self.assertEqual(child2.parentSection(), r) + self.assertEqual(child2.row(), 1) + + def testInsertChild(self): + p = QgsProject() + r = QgsReport(p) + + child1 = QgsReportSectionLayout() + r.insertChild(11, child1) + self.assertEqual(r.childCount(), 1) + self.assertEqual(r.childSections(), [child1]) + self.assertEqual(child1.parentSection(), r) + self.assertEqual(child1.row(), 0) + child2 = QgsReportSectionLayout() + r.insertChild(-1, child2) + self.assertEqual(r.childCount(), 2) + self.assertEqual(r.childSections(), [child2, child1]) + self.assertEqual(child2.parentSection(), r) + self.assertEqual(child2.row(), 0) + self.assertEqual(child1.row(), 1) + + def testRemoveChild(self): + p = QgsProject() + r = QgsReport(p) + + child1 = QgsReportSectionLayout() + r.appendChild(child1) + child2 = QgsReportSectionLayout() + r.appendChild(child2) + + r.removeChildAt(-1) + r.removeChildAt(100) + r.removeChild(None) + self.assertEqual(r.childCount(), 2) + self.assertEqual(r.childSections(), [child1, child2]) + + r.removeChildAt(1) + self.assertEqual(r.childCount(), 1) + self.assertEqual(r.childSections(), [child1]) + + r.removeChild(child1) + self.assertEqual(r.childCount(), 0) + self.assertEqual(r.childSections(), []) + + def testClone(self): + p = QgsProject() + r = QgsReport(p) + + child1 = QgsReportSectionLayout() + child1.setHeaderEnabled(True) + r.appendChild(child1) + child2 = QgsReportSectionLayout() + child2.setFooterEnabled(True) + r.appendChild(child2) + + cloned = r.clone() + self.assertEqual(cloned.childCount(), 2) + self.assertTrue(cloned.childSection(0).headerEnabled()) + self.assertFalse(cloned.childSection(0).footerEnabled()) + self.assertEqual(cloned.childSection(0).parentSection(), cloned) + self.assertFalse(cloned.childSection(1).headerEnabled()) + self.assertTrue(cloned.childSection(1).footerEnabled()) + self.assertEqual(cloned.childSection(1).parentSection(), cloned) + + def testReportSectionLayout(self): + r = QgsReportSectionLayout() + p = QgsProject() + body = QgsLayout(p) + r.setBody(body) + self.assertEqual(r.body(), body) + + def testIteration(self): + p = QgsProject() + r = QgsReport(p) + + # empty report + self.assertTrue(r.beginRender()) + self.assertFalse(r.next()) + + # add a header + r.setHeaderEnabled(True) + report_header = QgsLayout(p) + r.setHeader(report_header) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertFalse(r.next()) + + # add a footer + r.setFooterEnabled(True) + report_footer = QgsLayout(p) + r.setFooter(report_footer) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertFalse(r.next()) + + # add a child + child1 = QgsReportSectionLayout() + child1_body = QgsLayout(p) + child1.setBody(child1_body) + r.appendChild(child1) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertFalse(r.next()) + + # header and footer on child + child1_header = QgsLayout(p) + child1.setHeader(child1_header) + child1.setHeaderEnabled(True) + child1_footer = QgsLayout(p) + child1.setFooter(child1_footer) + child1.setFooterEnabled(True) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_footer) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertFalse(r.next()) + + # add another child + child2 = QgsReportSectionLayout() + child2_header = QgsLayout(p) + child2.setHeader(child2_header) + child2.setHeaderEnabled(True) + child2_footer = QgsLayout(p) + child2.setFooter(child2_footer) + child2.setFooterEnabled(True) + r.appendChild(child2) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_footer) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertFalse(r.next()) + + # add a child to child2 + child2a = QgsReportSectionLayout() + child2a_header = QgsLayout(p) + child2a.setHeader(child2a_header) + child2a.setHeaderEnabled(True) + child2a_footer = QgsLayout(p) + child2a.setFooter(child2a_footer) + child2a.setFooterEnabled(True) + child2.appendChild(child2a) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0001.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_header) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0002.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.filePath('/tmp/myreport', '.png'), '/tmp/myreport_0003.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_footer) + self.assertEqual(r.filePath('/tmp/myreport', 'jpg'), '/tmp/myreport_0004.jpg') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0005.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2a_header) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0006.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2a_footer) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0007.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0008.png') + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertEqual(r.filePath('/tmp/myreport', 'png'), '/tmp/myreport_0009.png') + self.assertFalse(r.next()) + + def testFieldGroup(self): + # create a layer + ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=country:string(20)&field=state:string(20)&field=town:string(20)", "points", "memory") + + attributes = [ + ['Australia', 'QLD', 'Brisbane'], + ['Australia', 'QLD', 'Emerald'], + ['NZ', 'state1', 'town1'], + ['Australia', 'VIC', 'Melbourne'], + ['NZ', 'state1', 'town2'], + ['Australia', 'QLD', 'Beerburrum'], + ['Australia', 'VIC', 'Geelong'], + ['NZ', 'state2', 'town2'], + ['PNG', 'state1', 'town1'], + ['Australia', 'NSW', 'Sydney'] + ] + + pr = ptLayer.dataProvider() + for a in attributes: + f = QgsFeature() + f.initAttributes(3) + f.setAttribute(0, a[0]) + f.setAttribute(1, a[1]) + f.setAttribute(2, a[2]) + self.assertTrue(pr.addFeature(f)) + + p = QgsProject() + r = QgsReport(p) + + # add a child + child1 = QgsReportSectionFieldGroup() + child1_body = QgsLayout(p) + child1.setLayer(ptLayer) + child1.setBody(child1_body) + child1.setBodyEnabled(True) + child1.setField('country') + r.appendChild(child1) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertFalse(r.next()) + + # another group + # remove body from child1 + child1.setBodyEnabled(False) + + child2 = QgsReportSectionFieldGroup() + child2_body = QgsLayout(p) + child2.setLayer(ptLayer) + child2.setBody(child2_body) + child2.setBodyEnabled(True) + child2.setField('state') + child1.appendChild(child2) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertFalse(r.next()) + + # another group + # remove body from child1 + child2.setBodyEnabled(False) + + child3 = QgsReportSectionFieldGroup() + child3_body = QgsLayout(p) + child3.setLayer(ptLayer) + child3.setBody(child3_body) + child3.setBodyEnabled(True) + child3.setField('town') + child3.setSortAscending(False) + child2.appendChild(child3) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertFalse(r.next()) + + # add headers/footers + child3_header = QgsLayout(p) + child3.setHeader(child3_header) + child3.setHeaderEnabled(True) + child3_footer = QgsLayout(p) + child3.setFooter(child3_footer) + child3.setFooterEnabled(True) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertFalse(r.next()) + + # header/footer for section2 + child2_header = QgsLayout(p) + child2.setHeader(child2_header) + child2.setHeaderEnabled(True) + child2_footer = QgsLayout(p) + child2.setFooter(child2_footer) + child2.setFooterEnabled(True) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['PNG', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['PNG', 'state1']) + self.assertFalse(r.next()) + + # child 1 and report header/footer + child1_header = QgsLayout(p) + child1.setHeader(child1_header) + child1.setHeaderEnabled(True) + child1_footer = QgsLayout(p) + child1.setFooter(child1_footer) + child1.setFooterEnabled(True) + report_header = QgsLayout(p) + r.setHeader(report_header) + r.setHeaderEnabled(True) + report_footer = QgsLayout(p) + r.setFooter(report_footer) + r.setFooterEnabled(True) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:1], ['Australia']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW', 'Sydney']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_header) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['PNG', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:2], ['PNG', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_footer) + self.assertEqual(r.layout().reportContext().feature().attributes()[:1], ['PNG']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), report_footer) + self.assertFalse(r.next()) + + def testFieldGroupSectionVisibility(self): + states = QgsVectorLayer("Point?crs=epsg:4326&field=country:string(20)&field=state:string(20)", "points", "memory") + + p = QgsProject() + r = QgsReport(p) + + # add a child + child1 = QgsReportSectionFieldGroup() + child1.setLayer(states) + child1.setField('country') + child1_header = QgsLayout(p) + child1.setHeader(child1_header) + child1.setHeaderEnabled(True) + child1_footer = QgsLayout(p) + child1.setFooter(child1_footer) + child1.setFooterEnabled(True) + r.appendChild(child1) + + # check that no header was rendered when no features are found + self.assertTrue(r.beginRender()) + self.assertFalse(r.next()) + + child1.setHeaderVisibility(QgsReportSectionFieldGroup.AlwaysInclude) + child1.setFooterVisibility(QgsReportSectionFieldGroup.AlwaysInclude) + + # check that the header is included when no features are found + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_header) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_footer) + + def testFieldGroupMultiLayer(self): + # create a layer + states = QgsVectorLayer("Point?crs=epsg:4326&field=country:string(20)&field=state:string(20)", "points", "memory") + + attributes = [ + ['Australia', 'QLD'], + ['NZ', 'state1'], + ['Australia', 'VIC'], + ['NZ', 'state2'], + ['PNG', 'state3'], + ['Australia', 'NSW'] + ] + + pr = states.dataProvider() + for a in attributes: + f = QgsFeature() + f.initAttributes(2) + f.setAttribute(0, a[0]) + f.setAttribute(1, a[1]) + self.assertTrue(pr.addFeature(f)) + + places = QgsVectorLayer("Point?crs=epsg:4326&field=state:string(20)&field=town:string(20)", "points", "memory") + + attributes = [ + ['QLD', 'Brisbane'], + ['QLD', 'Emerald'], + ['state1', 'town1'], + ['VIC', 'Melbourne'], + ['state1', 'town2'], + ['QLD', 'Beerburrum'], + ['VIC', 'Geelong'], + ['state3', 'town1'] + ] + + pr = places.dataProvider() + for a in attributes: + f = QgsFeature() + f.initAttributes(2) + f.setAttribute(0, a[0]) + f.setAttribute(1, a[1]) + self.assertTrue(pr.addFeature(f)) + + p = QgsProject() + r = QgsReport(p) + + # add a child + child1 = QgsReportSectionFieldGroup() + child1_body = QgsLayout(p) + child1.setLayer(states) + child1.setBody(child1_body) + child1.setBodyEnabled(True) + child1.setField('country') + r.appendChild(child1) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child1_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state3']) + self.assertFalse(r.next()) + + # another group + # remove body from child1 + child1.setBodyEnabled(False) + + child2 = QgsReportSectionFieldGroup() + child2_body = QgsLayout(p) + child2.setLayer(states) + child2.setBody(child2_body) + child2.setBodyEnabled(True) + child2.setField('state') + child1.appendChild(child2) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state3']) + self.assertFalse(r.next()) + + # another group + + child3 = QgsReportSectionFieldGroup() + child3_body = QgsLayout(p) + child3.setLayer(places) + child3.setBody(child3_body) + child3.setBodyEnabled(True) + child3.setField('town') + child3.setSortAscending(False) + child2.appendChild(child3) + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state3']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state3', 'town1']) + self.assertFalse(r.next()) + + # add headers/footers + child3_header = QgsLayout(p) + child3.setHeader(child3_header) + child3.setHeaderEnabled(True) + child3_footer = QgsLayout(p) + child3.setFooter(child3_footer) + child3.setFooterEnabled(True) + + self.assertTrue(r.beginRender()) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'NSW']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'QLD']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Emerald']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Brisbane']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['QLD', 'Beerburrum']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['Australia', 'VIC']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Melbourne']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['VIC', 'Geelong']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state1', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['NZ', 'state2']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child2_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['PNG', 'state3']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_header) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state3', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_body) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state3', 'town1']) + self.assertTrue(r.next()) + self.assertEqual(r.layout(), child3_footer) + self.assertEqual(r.layout().reportContext().feature().attributes(), ['state3', 'town1']) + self.assertFalse(r.next()) + + def testReadWriteXml(self): + p = QgsProject() + ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=country:string(20)&field=state:string(20)&field=town:string(20)", "points", "memory") + p.addMapLayer(ptLayer) + + r = QgsReport(p) + r.setName('my report') + # add a header + r.setHeaderEnabled(True) + report_header = QgsLayout(p) + report_header.setUnits(QgsUnitTypes.LayoutInches) + r.setHeader(report_header) + # add a footer + r.setFooterEnabled(True) + report_footer = QgsLayout(p) + report_footer.setUnits(QgsUnitTypes.LayoutMeters) + r.setFooter(report_footer) + + # add some subsections + child1 = QgsReportSectionLayout() + child1_body = QgsLayout(p) + child1_body.setUnits(QgsUnitTypes.LayoutPoints) + child1.setBody(child1_body) + + child2 = QgsReportSectionLayout() + child2_body = QgsLayout(p) + child2_body.setUnits(QgsUnitTypes.LayoutPixels) + child2.setBody(child2_body) + child1.appendChild(child2) + + child2a = QgsReportSectionFieldGroup() + child2a_body = QgsLayout(p) + child2a_body.setUnits(QgsUnitTypes.LayoutInches) + child2a.setBody(child2a_body) + child2a.setField('my field') + child2a.setLayer(ptLayer) + child1.appendChild(child2a) + + r.appendChild(child1) + + doc = QDomDocument("testdoc") + elem = r.writeLayoutXml(doc, QgsReadWriteContext()) + + r2 = QgsReport(p) + self.assertTrue(r2.readLayoutXml(elem, doc, QgsReadWriteContext())) + self.assertEqual(r2.name(), 'my report') + self.assertTrue(r2.headerEnabled()) + self.assertEqual(r2.header().units(), QgsUnitTypes.LayoutInches) + self.assertTrue(r2.footerEnabled()) + self.assertEqual(r2.footer().units(), QgsUnitTypes.LayoutMeters) + + self.assertEqual(r2.childCount(), 1) + self.assertEqual(r2.childSection(0).body().units(), QgsUnitTypes.LayoutPoints) + self.assertEqual(r2.childSection(0).childCount(), 2) + self.assertEqual(r2.childSection(0).childSection(0).body().units(), QgsUnitTypes.LayoutPixels) + self.assertEqual(r2.childSection(0).childSection(1).body().units(), QgsUnitTypes.LayoutInches) + self.assertEqual(r2.childSection(0).childSection(1).field(), 'my field') + self.assertEqual(r2.childSection(0).childSection(1).layer(), ptLayer) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgssearchwidgetwrapper.py b/tests/src/python/test_qgssearchwidgetwrapper.py index c5c5e54cd6f..66349ab3e1e 100644 --- a/tests/src/python/test_qgssearchwidgetwrapper.py +++ b/tests/src/python/test_qgssearchwidgetwrapper.py @@ -134,8 +134,8 @@ class PyQgsValueMapSearchWidgetWrapper(unittest.TestCase): layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "test", "memory") w = QgsValueMapSearchWidgetWrapper(layer, 0) - config = {"val1": 1, - "val2": 200} + config = {"map": [{"val1": 1}, + {"val2": 200}]} w.setConfig(config) c = w.widget() diff --git a/tests/src/python/test_qgsserver.py b/tests/src/python/test_qgsserver.py index 3f63ea82ca8..887019e02ac 100644 --- a/tests/src/python/test_qgsserver.py +++ b/tests/src/python/test_qgsserver.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """QGIS Unit tests for QgsServer. +Set the env var ENCODED_OUTPUT to enable printing the base64 encoded image diff + FIXME: keep here only generic server tests and move specific services tests to test_qgsserver_.py @@ -72,6 +74,7 @@ class QgsServerTestBase(unittest.TestCase): for expected_line in expected_lines: expected_line = expected_line.strip() response_line = response_lines[line_no - 1].strip() + response_line = response_line.replace(b'e+6', b'e+06') # Compare tag if re.match(RE_ELEMENT, expected_line): expected_elements = re.findall(RE_ELEMENT, expected_line) @@ -119,6 +122,7 @@ class QgsServerTestBase(unittest.TestCase): self.projectAnnotationPath = os.path.join(d, "project_with_annotations.qgs") self.projectStatePath = os.path.join(d, "project_state.qgs") self.projectUseLayerIdsPath = os.path.join(d, "project_use_layerids.qgs") + self.projectGroupsPath = os.path.join(d, "project_groups.qgs") # Clean env just to be sure env_vars = ['QUERY_STRING', 'QGIS_PROJECT_FILE'] @@ -189,17 +193,23 @@ class QgsServerTestBase(unittest.TestCase): with open(os.path.join(tempfile.gettempdir(), image + "_result.png"), "rb") as rendered_file: encoded_rendered_file = base64.b64encode(rendered_file.read()) - message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.png" % ( - report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image - ) + if not os.environ.get('ENCODED_OUTPUT'): + message = "Image is wrong\: rendered file %s/%s_result.png" % (tempfile.gettempdir(), image) + else: + message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.png" % ( + report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image + ) # If the failure is in image sizes the diff file will not exists. if os.path.exists(os.path.join(tempfile.gettempdir(), image + "_result_diff.png")): with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file: - encoded_diff_file = base64.b64encode(diff_file.read()) - message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % ( - encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image - ) + if not os.environ.get('ENCODED_OUTPUT'): + message = "Image is wrong\: diff file %s/%s_result_diff.png" % (tempfile.gettempdir(), image) + else: + encoded_diff_file = base64.b64encode(diff_file.read()) + message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % ( + encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image + ) self.assertTrue(test, message) diff --git a/tests/src/python/test_qgsserver_accesscontrol.py b/tests/src/python/test_qgsserver_accesscontrol.py index 6299c50d617..550730e6f96 100644 --- a/tests/src/python/test_qgsserver_accesscontrol.py +++ b/tests/src/python/test_qgsserver_accesscontrol.py @@ -980,7 +980,7 @@ class TestQgsServerAccessControl(unittest.TestCase): test we reuse the projectsubsetstring reference images as we are using filter requests to set the same filter " pkuid in (7,8) " as the project subsetstring uses for its test. """ - query_string = "&".join(["%s=%s" % i for i in list({ + query_string = "&".join(["%s=%s" % i for i in { "MAP": urllib.parse.quote(self.projectPath), "SERVICE": "WMS", "VERSION": "1.1.1", @@ -993,12 +993,12 @@ class TestQgsServerAccessControl(unittest.TestCase): "HEIGHT": "500", "WIDTH": "500", "SRS": "EPSG:3857" - }.items())]) + }.items()]) response, headers = self._get_fullaccess(query_string) self._img_diff_error(response, headers, "WMS_GetMap_projectsubstring") - query_string = "&".join(["%s=%s" % i for i in list({ + query_string = "&".join(["%s=%s" % i for i in { "MAP": urllib.parse.quote(self.projectPath), "SERVICE": "WMS", "VERSION": "1.1.1", @@ -1011,11 +1011,32 @@ class TestQgsServerAccessControl(unittest.TestCase): "HEIGHT": "500", "WIDTH": "500", "SRS": "EPSG:3857" - }.items())]) + }.items()]) response, headers = self._get_restricted(query_string) self._img_diff_error(response, headers, "Restricted_WMS_GetMap_projectsubstring") + filter = "pkuid7" \ + "pkuid8" \ + "" + query_string = "&".join(["%s=%s" % i for i in { + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Hello_Filter_SubsetString", + "FILTER": filter, + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-6318936.5,5696513,16195283.5", + "HEIGHT": "500", + "WIDTH": "500", + "SRS": "EPSG:3857" + }.items()]) + + response, headers = self._get_restricted(query_string) + self._img_diff_error(response, headers, "Restricted_WMS_GetMap_projectsubstring_OGC") + def test_wms_getmap_projectsubsetstring(self): """ test that project set layer subsetStrings are honored""" query_string = "&".join(["%s=%s" % i for i in list({ diff --git a/tests/src/python/test_qgsserver_security.py b/tests/src/python/test_qgsserver_security.py index e0410f0463e..359b969c6cf 100644 --- a/tests/src/python/test_qgsserver_security.py +++ b/tests/src/python/test_qgsserver_security.py @@ -283,6 +283,37 @@ class TestQgsServerSecurity(QgsServerTestBase): self.handle_request_wfs_getfeature_filter(filter_xml) self.assertTrue(self.is_point_table_still_exist()) + def test_wms_getmap_filter_stacked(self): + """ + The aim is to execute some staked queries within the 'Literal' + and 'PropertyName' field. Here the 'drop' function is used but it + could be done with create, insert, ... + + But due to the implementation, these filters quoted before being sent to the DB. + + It's typically the kind of SQL injection which has been fixed in + mapserver several years ago: + https://trac.osgeo.org/mapserver/ticket/3874 + """ + + # ogc:Literal / ogc:PropertyIsEqualTo + literal = "4')); drop table point --" + filter_xml = "pkuid{0}".format(literal) + self.handle_request_wms_getmap(filter=filter_xml) + self.assertTrue(self.is_point_table_still_exist()) + + # ogc:Literal / ogc:PropertyIsLike + literal = "4')); drop table point --" + filter_xml = "pkuid{0}".format(literal) + self.handle_request_wms_getmap(filter=filter_xml) + self.assertTrue(self.is_point_table_still_exist()) + + # ogc:PropertyName / ogc:PropertyIsLike + propname = "name = 'a')); drop table point --" + filter_xml = "{0}4".format(propname) + self.handle_request_wms_getmap(filter=filter_xml) + self.assertTrue(self.is_point_table_still_exist()) + def test_wms_getmap_sld_stacked(self): """ The aim is to execute some staked queries within the 'Literal' @@ -297,7 +328,7 @@ class TestQgsServerSecurity(QgsServerTestBase): literal = "4')); drop table point --" sld = " point point Single symbol pkuid {0} circle 5e86a10000007".format(literal) - self.handle_request_wms_getmap(sld) + self.handle_request_wms_getmap(sld=sld) self.assertTrue(self.is_point_table_still_exist()) def check_service_exception_report(self, d): @@ -311,7 +342,7 @@ class TestQgsServerSecurity(QgsServerTestBase): return False def handle_request_wfs_getfeature_filter(self, filter_xml): - qs = "?" + "&".join(["%s=%s" % i for i in list({ + qs = "?" + "&".join(["%s=%s" % i for i in { "MAP": urllib.parse.quote(self.project), "SERVICE": "WFS", "VERSION": "1.1.1", @@ -319,12 +350,12 @@ class TestQgsServerSecurity(QgsServerTestBase): "TYPENAME": "point", "STYLES": "", "CRS": "EPSG:32613", - "FILTER": filter_xml}.items())]) + "FILTER": filter_xml}.items()]) return self._execute_request(qs) def handle_request_wms_getfeatureinfo(self, filter_sql): - qs = "?" + "&".join(["%s=%s" % i for i in list({ + qs = "?" + "&".join(["%s=%s" % i for i in { "MAP": urllib.parse.quote(self.project), "SERVICE": "WMS", "VERSION": "1.1.1", @@ -337,12 +368,12 @@ class TestQgsServerSecurity(QgsServerTestBase): "WIDTH": "500", "BBOX": "606171,4822867,612834,4827375", "CRS": "EPSG:32613", - "FILTER": filter_sql}.items())]) + "FILTER": filter_sql}.items()]) return self._result(self._execute_request(qs)) - def handle_request_wms_getmap(self, sld): - qs = "?" + "&".join(["%s=%s" % i for i in list({ + def handle_request_wms_getmap(self, sld=None, filter=None): + params = { "MAP": urllib.parse.quote(self.project), "SERVICE": "WMS", "VERSION": "1.0.0", @@ -354,8 +385,13 @@ class TestQgsServerSecurity(QgsServerTestBase): "HEIGHT": "500", "WIDTH": "500", "BBOX": "606171,4822867,612834,4827375", - "CRS": "EPSG:32613", - "SLD": sld}.items())]) + "CRS": "EPSG:32613" + } + if sld is not None: + params["SLD"] = sld + if filter is not None: + params["FILTER"] = urllib.parse.quote(filter) + qs = "?" + "&".join(["%s=%s" % i for i in params.items()]) return self._result(self._execute_request(qs)) diff --git a/tests/src/python/test_qgsserver_wfst.py b/tests/src/python/test_qgsserver_wfst.py index 839c89de269..f23f08b1d94 100644 --- a/tests/src/python/test_qgsserver_wfst.py +++ b/tests/src/python/test_qgsserver_wfst.py @@ -236,15 +236,15 @@ class TestWFST(unittest.TestCase): layer_name = 'test_point' layer = self._getLayer(layer_name) wfs_layer = self._getWFSLayer(layer_name) - feat1 = QgsFeature(wfs_layer.pendingFields()) + feat1 = QgsFeature(wfs_layer.fields()) feat1['id'] = 11 feat1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(9, 45))) - feat2 = QgsFeature(wfs_layer.pendingFields()) + feat2 = QgsFeature(wfs_layer.fields()) feat2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(9.5, 45.5))) feat2['id'] = 12 old_features = [feat1, feat2] # Change feat1 - new_feat1 = QgsFeature(wfs_layer.pendingFields()) + new_feat1 = QgsFeature(wfs_layer.fields()) new_feat1['id'] = 121 new_feat1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 46))) new_features = [new_feat1, feat2] @@ -258,21 +258,21 @@ class TestWFST(unittest.TestCase): layer_name = 'test_point' layer = self._getLayer(layer_name) wfs_layer = self._getWFSLayer(layer_name) - feat1 = QgsFeature(wfs_layer.pendingFields()) + feat1 = QgsFeature(wfs_layer.fields()) feat1['id'] = 11 feat1['name'] = 'name 11' feat1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(9, 45))) - feat2 = QgsFeature(wfs_layer.pendingFields()) + feat2 = QgsFeature(wfs_layer.fields()) feat2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(9.5, 45.5))) feat2['id'] = 12 feat2['name'] = 'name 12' old_features = [feat1, feat2] # Change feat1 and feat2 - new_feat1 = QgsFeature(wfs_layer.pendingFields()) + new_feat1 = QgsFeature(wfs_layer.fields()) new_feat1['id'] = 121 new_feat1['name'] = 'name 121' new_feat1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 46))) - new_feat2 = QgsFeature(wfs_layer.pendingFields()) + new_feat2 = QgsFeature(wfs_layer.fields()) new_feat2['id'] = 122 new_feat2['name'] = 'name 122' new_feat2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10.5, 47))) @@ -286,17 +286,17 @@ class TestWFST(unittest.TestCase): layer_name = 'test_polygon' layer = self._getLayer(layer_name) wfs_layer = self._getWFSLayer(layer_name) - feat1 = QgsFeature(wfs_layer.pendingFields()) + feat1 = QgsFeature(wfs_layer.fields()) feat1['id'] = 11 feat1['name'] = 'name 11' feat1.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPointXY(9, 45), QgsPointXY(10, 46)))) - feat2 = QgsFeature(wfs_layer.pendingFields()) + feat2 = QgsFeature(wfs_layer.fields()) feat2.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPointXY(9.5, 45.5), QgsPointXY(10.5, 46.5)))) feat2['id'] = 12 feat2['name'] = 'name 12' old_features = [feat1, feat2] # Change feat1 - new_feat1 = QgsFeature(wfs_layer.pendingFields()) + new_feat1 = QgsFeature(wfs_layer.fields()) new_feat1['id'] = 121 new_feat1['name'] = 'name 121' new_feat1.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPointXY(10, 46), QgsPointXY(11.5, 47.5)))) @@ -310,17 +310,17 @@ class TestWFST(unittest.TestCase): layer_name = 'test_linestring' layer = self._getLayer(layer_name) wfs_layer = self._getWFSLayer(layer_name) - feat1 = QgsFeature(wfs_layer.pendingFields()) + feat1 = QgsFeature(wfs_layer.fields()) feat1['id'] = 11 feat1['name'] = 'name 11' feat1.setGeometry(QgsGeometry.fromPolylineXY([QgsPointXY(9, 45), QgsPointXY(10, 46)])) - feat2 = QgsFeature(wfs_layer.pendingFields()) + feat2 = QgsFeature(wfs_layer.fields()) feat2.setGeometry(QgsGeometry.fromPolylineXY([QgsPointXY(9.5, 45.5), QgsPointXY(10.5, 46.5)])) feat2['id'] = 12 feat2['name'] = 'name 12' old_features = [feat1, feat2] # Change feat1 - new_feat1 = QgsFeature(wfs_layer.pendingFields()) + new_feat1 = QgsFeature(wfs_layer.fields()) new_feat1['id'] = 121 new_feat1['name'] = 'name 121' new_feat1.setGeometry(QgsGeometry.fromPolylineXY([QgsPointXY(9.8, 45.8), QgsPointXY(10.8, 46.8)])) diff --git a/tests/src/python/test_qgsserver_wms_getmap.py b/tests/src/python/test_qgsserver_wms_getmap.py index 781ad83c60b..80671e23e5f 100644 --- a/tests/src/python/test_qgsserver_wms_getmap.py +++ b/tests/src/python/test_qgsserver_wms_getmap.py @@ -697,6 +697,27 @@ class TestQgsServerWMSGetMap(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetMap_Filter3") + def test_wms_getmap_filter_ogc(self): + filter = "name" + \ + "eurasia" + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country,Hello", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857", + "FILTER": filter + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_Filter_OGC") + def test_wms_getmap_selection(self): qs = "?" + "&".join(["%s=%s" % i for i in list({ "MAP": urllib.parse.quote(self.projectPath), @@ -868,6 +889,57 @@ class TestQgsServerWMSGetMap(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetMap_SLDRestored") + def test_wms_getmap_group(self): + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectGroupsPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "Country,Country_Labels,Country_Diagrams", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r_individual, _ = self._result(self._execute_request(qs)) + + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectGroupsPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "LAYERS": "CountryGroup", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r_group, _ = self._result(self._execute_request(qs)) + self.assertEqual(r_individual, r_group, 'Individual layers query and group layers query results should be identical') + + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectGroupsPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetMap", + "SLD": " CountryGroup ", + "STYLES": "", + "FORMAT": "image/png", + "BBOX": "-16817707,-4710778,5696513,14587125", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:3857" + }.items())]) + + r_group_sld, _ = self._result(self._execute_request(qs)) + self.assertEqual(r_individual, r_group_sld, 'Individual layers query and SLD group layers query results should be identical') + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsserver_wms_getprint.py b/tests/src/python/test_qgsserver_wms_getprint.py index aa1ae88a116..505265fbf85 100644 --- a/tests/src/python/test_qgsserver_wms_getprint.py +++ b/tests/src/python/test_qgsserver_wms_getprint.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -"""QGIS Unit tests for QgsServer WMS. +"""QGIS Unit tests for QgsServer WMS GetPrint. -From build dir, run: ctest -R PyQgsServerWMS -V +From build dir, run: ctest -R PyQgsServerWMSGetPrint -V .. note:: This program is free software; you can redistribute it and/or modify @@ -33,6 +33,7 @@ import osgeo.gdal # NOQA from test_qgsserver import QgsServerTestBase from qgis.core import QgsProject +from qgis.server import QgsServerRequest # Strip path and content length because path may vary RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+' @@ -302,7 +303,7 @@ class TestQgsServerWMSGetPrint(QgsServerTestBase): "REQUEST": "GetPrint", "TEMPLATE": "layoutA4", "FORMAT": "png", - "map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031", + "map0%3AEXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031", "map0:LAYERS": "Country,Hello", "CRS": "EPSG:3857", "SELECTION": "Country: 4", @@ -313,14 +314,15 @@ class TestQgsServerWMSGetPrint(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetPrint_Opacity") - qs = "?" + "&".join(["%s=%s" % i for i in list({ + def test_wms_getprint_opacity_post(self): + qs = "&".join(["%s=%s" % i for i in list({ "MAP": urllib.parse.quote(self.projectPath), "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetPrint", "TEMPLATE": "layoutA4", "FORMAT": "png", - "map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031", + "map0%3AEXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031", "map0:LAYERS": "Country,Hello", "CRS": "EPSG:3857", "SELECTION": "Country: 4", @@ -328,7 +330,7 @@ class TestQgsServerWMSGetPrint(QgsServerTestBase): "OPACITIES": "125%2C125" }.items())]) - r, h = self._result(self._execute_request(qs)) + r, h = self._result(self._execute_request('', QgsServerRequest.PostMethod, data=qs.encode('utf-8'))) self._img_diff_error(r, h, "WMS_GetPrint_Opacity") def test_wms_getprint_highlight(self): @@ -389,6 +391,26 @@ class TestQgsServerWMSGetPrint(QgsServerTestBase): r, h = self._result(self._execute_request(qs)) self._img_diff_error(r, h, "WMS_GetPrint_LabelRemoved") + def test_wms_getprint_two_maps(self): + """Test map0 and map1 apply to the correct maps""" + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetPrint", + "TEMPLATE": "layoutA4twoMaps", + "FORMAT": "png", + "map0:EXTENT": "11863620.20301065221428871,-5848927.97872077487409115,19375243.89574331790208817,138857.97204941", + "map0:LAYERS": "Country", + "map1:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031", + "map1:LAYERS": "Country,Hello", + "CRS": "EPSG:3857", + "IDTEXTBOX": "", + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetPrint_TwoMaps") + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsshortcutsmanager.py b/tests/src/python/test_qgsshortcutsmanager.py index ed758dab9fa..d28e9a66b39 100644 --- a/tests/src/python/test_qgsshortcutsmanager.py +++ b/tests/src/python/test_qgsshortcutsmanager.py @@ -90,6 +90,11 @@ class TestQgsShortcutsManager(unittest.TestCase): action2 = QAction('action2', None) action2.setShortcut('y') self.assertTrue(s.registerAction(action2, 'B')) + self.assertCountEqual(s.listActions(), [action1, action2]) + + # try re-registering an existing action - should fail, but leave action registered + self.assertFalse(s.registerAction(action2, 'B')) + self.assertCountEqual(s.listActions(), [action1, action2]) # actions should have been set to default sequences self.assertEqual(action1.shortcut().toString(), 'A') diff --git a/tests/src/python/test_qgssymbollayer_readsld.py b/tests/src/python/test_qgssymbollayer_readsld.py index 56021c753d4..f5cc515f676 100644 --- a/tests/src/python/test_qgssymbollayer_readsld.py +++ b/tests/src/python/test_qgssymbollayer_readsld.py @@ -71,7 +71,7 @@ def createLayerWithOnePoint(): f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) assert pr.addFeatures([f]) - assert layer.pendingFeatureCount() == 1 + assert layer.featureCount() == 1 return layer diff --git a/tests/src/python/test_qgsvectorfilewriter.py b/tests/src/python/test_qgsvectorfilewriter.py index 7f4c70993d2..3101ec37d83 100644 --- a/tests/src/python/test_qgsvectorfilewriter.py +++ b/tests/src/python/test_qgsvectorfilewriter.py @@ -25,6 +25,7 @@ from qgis.core import (QgsVectorLayer, QgsCoordinateReferenceSystem, QgsVectorFileWriter, QgsFeatureRequest, + QgsProject, QgsWkbTypes, QgsRectangle, QgsCoordinateTransform @@ -221,7 +222,7 @@ class TestQgsVectorFileWriter(unittest.TestCase): options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'ESRI Shapefile' options.filterExtent = QgsRectangle(-12511460, 3045157, -10646621, 4683497) - options.ct = QgsCoordinateTransform(source_layer.crs(), QgsCoordinateReferenceSystem.fromEpsgId(3785)) + options.ct = QgsCoordinateTransform(source_layer.crs(), QgsCoordinateReferenceSystem.fromEpsgId(3785), QgsProject.instance()) dest_file_name = os.path.join(str(QDir.tempPath()), 'extent_transform.shp') write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( @@ -842,6 +843,14 @@ class TestQgsVectorFileWriter(unittest.TestCase): self.assertEqual(QgsVectorFileWriter.driverForExtension('not a format'), '') self.assertEqual(QgsVectorFileWriter.driverForExtension(''), '') + def testSupportsFeatureStyles(self): + self.assertFalse(QgsVectorFileWriter.supportsFeatureStyles('ESRI Shapefile')) + self.assertFalse(QgsVectorFileWriter.supportsFeatureStyles('not a driver')) + self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('DXF')) + self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('KML')) + self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo File')) + self.assertTrue(QgsVectorFileWriter.supportsFeatureStyles('MapInfo MIF')) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py old mode 100644 new mode 100755 index c6d76242df5..d5a819cb290 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -63,18 +63,19 @@ from qgis.gui import (QgsAttributeTableModel, from qgis.testing import start_app, unittest from featuresourcetestbase import FeatureSourceTestCase from utilities import unitTestDataPath + start_app() def createEmptyLayer(): layer = QgsVectorLayer("Point", "addfeat", "memory") - assert layer.pendingFeatureCount() == 0 + assert layer.featureCount() == 0 return layer def createEmptyLayerWithFields(): layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") - assert layer.pendingFeatureCount() == 0 + assert layer.featureCount() == 0 return layer @@ -86,7 +87,7 @@ def createLayerWithOnePoint(): f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) assert pr.addFeatures([f]) - assert layer.pendingFeatureCount() == 1 + assert layer.featureCount() == 1 return layer @@ -101,7 +102,7 @@ def createLayerWithTwoPoints(): f2.setAttributes(["test2", 457]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) assert pr.addFeatures([f, f2]) - assert layer.pendingFeatureCount() == 2 + assert layer.featureCount() == 2 return layer @@ -147,7 +148,7 @@ def createJoinLayer(): f4.setAttributes(["a", 458, 19]) f4.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 2))) assert pr.addFeatures([f1, f2, f3, f4]) - assert joinLayer.pendingFeatureCount() == 4 + assert joinLayer.featureCount() == 4 return joinLayer @@ -245,7 +246,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2))) def checkAfter(): - self.assertEqual(layer.pendingFeatureCount(), 1) + self.assertEqual(layer.featureCount(), 1) # check select+nextFeature f = next(layer.getFeatures()) @@ -256,7 +257,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(f2.geometry().asPoint(), QgsPointXY(1, 2)) def checkBefore(): - self.assertEqual(layer.pendingFeatureCount(), 0) + self.assertEqual(layer.featureCount(), 0) # check select+nextFeature with self.assertRaises(StopIteration): @@ -301,7 +302,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): feat2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(11, 12))) def checkAfter(): - self.assertEqual(layer.pendingFeatureCount(), 2) + self.assertEqual(layer.featureCount(), 2) # check select+nextFeature it = layer.getFeatures() @@ -317,7 +318,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(f2_1.geometry().asPoint(), QgsPointXY(11, 12)) def checkBefore(): - self.assertEqual(layer.pendingFeatureCount(), 0) + self.assertEqual(layer.featureCount(), 0) # check select+nextFeature with self.assertRaises(StopIteration): @@ -353,6 +354,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): checkAfter() self.assertEqual(layer.dataProvider().featureCount(), 2) + # DELETE FEATURE def test_DeleteFeature(self): @@ -360,7 +362,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): fid = 1 def checkAfter(): - self.assertEqual(layer.pendingFeatureCount(), 0) + self.assertEqual(layer.featureCount(), 0) # check select+nextFeature with self.assertRaises(StopIteration): @@ -371,7 +373,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): next(layer.getFeatures(QgsFeatureRequest(fid))) def checkBefore(): - self.assertEqual(layer.pendingFeatureCount(), 1) + self.assertEqual(layer.featureCount(), 1) # check select+nextFeature fi = layer.getFeatures() @@ -418,14 +420,14 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2))) def checkBefore(): - self.assertEqual(layer.pendingFeatureCount(), 0) + self.assertEqual(layer.featureCount(), 0) # check select+nextFeature with self.assertRaises(StopIteration): next(layer.getFeatures()) def checkAfter1(): - self.assertEqual(layer.pendingFeatureCount(), 1) + self.assertEqual(layer.featureCount(), 1) def checkAfter2(): checkBefore() # should be the same state: no features @@ -552,7 +554,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): newF.setAttributes(["hello", 42]) def checkAfter(): - self.assertEqual(len(layer.pendingFields()), 2) + self.assertEqual(len(layer.fields()), 2) # check feature fi = layer.getFeatures() f = next(fi) @@ -686,7 +688,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): newF.setAttributes(["hello", 42]) def checkAfter(): - self.assertEqual(len(layer.pendingFields()), 2) + self.assertEqual(len(layer.fields()), 2) # check feature f = next(layer.getFeatures()) self.assertEqual(f.geometry().asPoint(), QgsPointXY(2, 2)) @@ -721,16 +723,65 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): # print "COMMIT ERRORS:" # for item in list(layer.commitErrors()): print item + # updateFeature + + def testUpdateFeature(self): + layer = createLayerWithFivePoints() + features = [f for f in layer.getFeatures()] + + # try to change feature without editing mode + self.assertFalse(layer.updateFeature(features[0])) + + layer.startEditing() + + # no matching feature + f = QgsFeature(1123) + self.assertFalse(layer.updateFeature(f)) + + # change geometry and attributes + f = features[0] + f.setAttributes(['new', 321]) + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-200, -200))) + self.assertTrue(layer.updateFeature(f)) + + new_feature = next(layer.getFeatures(QgsFeatureRequest(f.id()))) + self.assertEqual(new_feature.attributes(), ['new', 321]) + self.assertEqual(new_feature.geometry().asPoint(), QgsPointXY(-200, -200)) + + # add feature with no geometry + f6 = QgsFeature() + f6.setAttributes(["test6", 555]) + self.assertTrue(layer.dataProvider().addFeatures([f6])) + features = [f for f in layer.getFeatures()] + + # update feature with no geometry -> have geometry + f = features[-1] + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-350, -250))) + self.assertTrue(layer.updateFeature(f)) + new_feature = next(layer.getFeatures(QgsFeatureRequest(f.id()))) + self.assertEqual(new_feature.attributes(), ['test6', 555]) + self.assertTrue(new_feature.hasGeometry()) + self.assertEqual(new_feature.geometry().asPoint(), QgsPointXY(-350, -250)) + + # update feature from geometry -> no geometry + f = features[1] + f.clearGeometry() + self.assertTrue(layer.updateFeature(f)) + new_feature = next(layer.getFeatures(QgsFeatureRequest(f.id()))) + self.assertEqual(new_feature.attributes(), ['test2', 457]) + self.assertFalse(new_feature.hasGeometry()) + # ADD ATTRIBUTE def test_AddAttribute(self): layer = createLayerWithOnePoint() fld1 = QgsField("fld1", QVariant.Int, "integer") - #fld2 = QgsField("fld2", QVariant.Int, "integer") + + # fld2 = QgsField("fld2", QVariant.Int, "integer") def checkBefore(): # check fields - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(len(flds), 2) self.assertEqual(flds[0].name(), "fldtxt") self.assertEqual(flds[1].name(), "fldint") @@ -744,7 +795,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): def checkAfter(): # check fields - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(len(flds), 3) self.assertEqual(flds[0].name(), "fldtxt") self.assertEqual(flds[1].name(), "fldint") @@ -799,13 +850,13 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): fld1 = QgsField("fld1", QVariant.Int, "integer") def checkBefore(): - self.assertEqual(len(layer.pendingFields()), 2) + self.assertEqual(len(layer.fields()), 2) # check feature with self.assertRaises(StopIteration): next(layer.getFeatures()) def checkAfter(): - self.assertEqual(len(layer.pendingFields()), 3) + self.assertEqual(len(layer.fields()), 3) # check feature f = next(layer.getFeatures()) attrs = f.attributes() @@ -861,7 +912,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertFalse(layer.deleteAttribute(0)) def checkBefore(): - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(len(flds), 3) self.assertEqual(flds[0].name(), "fldtxt") self.assertEqual(flds[1].name(), "fldint") @@ -881,12 +932,12 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertTrue(layer.deleteAttribute(0)) def checkAfterOneDelete(): - flds = layer.pendingFields() + flds = layer.fields() # for fld in flds: print "FLD", fld.name() self.assertEqual(len(flds), 2) self.assertEqual(flds[0].name(), "fldint") self.assertEqual(flds[1].name(), "flddouble") - self.assertEqual(layer.pendingAllAttributesList(), [0, 1]) + self.assertEqual(layer.attributeList(), [0, 1]) f = next(layer.getFeatures()) attrs = f.attributes() @@ -900,8 +951,8 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertTrue(layer.deleteAttribute(0)) def checkAfterTwoDeletes(): - self.assertEqual(layer.pendingAllAttributesList(), [0]) - flds = layer.pendingFields() + self.assertEqual(layer.attributeList(), [0]) + flds = layer.fields() # for fld in flds: print "FLD", fld.name() self.assertEqual(len(flds), 1) self.assertEqual(flds[0].name(), "flddouble") @@ -933,7 +984,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): fld1 = QgsField("fld1", QVariant.Int, "integer") def checkAfter(): # layer should be unchanged - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(len(flds), 2) self.assertEqual(flds[0].name(), "fldtxt") self.assertEqual(flds[1].name(), "fldint") @@ -979,13 +1030,13 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): newF.setAttributes(["hello", 42]) def checkBefore(): - self.assertEqual(len(layer.pendingFields()), 2) + self.assertEqual(len(layer.fields()), 2) # check feature with self.assertRaises(StopIteration): next(layer.getFeatures()) def checkAfter1(): - self.assertEqual(len(layer.pendingFields()), 2) + self.assertEqual(len(layer.fields()), 2) # check feature f = next(layer.getFeatures()) attrs = f.attributes() @@ -994,7 +1045,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(attrs[1], 42) def checkAfter2(): - self.assertEqual(len(layer.pendingFields()), 1) + self.assertEqual(len(layer.fields()), 1) # check feature f = next(layer.getFeatures()) attrs = f.attributes() @@ -1149,7 +1200,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): checkFieldNames(['fldtxt', 'fldint']) self.assertTrue(layer.renameAttribute(1, 'fldint2')) checkFieldNames(['fldtxt', 'fldint2']) - #add an attribute + # add an attribute self.assertTrue(layer.addAttribute(QgsField("flddouble", QVariant.Double, "double"))) checkFieldNames(['fldtxt', 'fldint2', 'flddouble']) # rename it @@ -1212,7 +1263,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertTrue(layer.renameAttribute(2, 'flddouble2')) checkFieldNames(['fldtxt2', 'fldint', 'flddouble2']) - #delete an attribute + # delete an attribute self.assertTrue(layer.deleteAttribute(0)) checkFieldNames(['fldint', 'flddouble2']) # rename remaining @@ -1220,7 +1271,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): checkFieldNames(['fldint2', 'flddouble2']) self.assertTrue(layer.renameAttribute(1, 'flddouble3')) checkFieldNames(['fldint2', 'flddouble3']) - #delete an attribute + # delete an attribute self.assertTrue(layer.deleteAttribute(0)) checkFieldNames(['flddouble3']) self.assertTrue(layer.renameAttribute(0, 'flddouble4')) @@ -1241,16 +1292,16 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer.undoStack().undo() checkFieldNames(['fldtxt', 'fldint', 'flddouble']) - #layer.undoStack().redo() - #checkFieldNames(['fldtxt2', 'fldint']) - #layer.undoStack().redo() - #checkFieldNames(['fldint']) + # layer.undoStack().redo() + # checkFieldNames(['fldtxt2', 'fldint']) + # layer.undoStack().redo() + # checkFieldNames(['fldint']) def test_RenameExpressionField(self): layer = createLayerWithOnePoint() exp_field_idx = layer.addExpressionField('1+1', QgsField('math_is_hard', QVariant.Int)) - #rename and check + # rename and check self.assertTrue(layer.renameAttribute(exp_field_idx, 'renamed')) self.assertEqual(layer.fields()[exp_field_idx].name(), 'renamed') f = next(layer.getFeatures()) @@ -1259,7 +1310,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): def test_fields(self): layer = createLayerWithOnePoint() - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(flds.indexFromName("fldint"), 1) self.assertEqual(flds.indexFromName("fldXXX"), -1) @@ -1328,7 +1379,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer.addJoin(join2) - flds = layer.pendingFields() + flds = layer.fields() self.assertEqual(len(flds), 6) self.assertEqual(flds[2].name(), "joinlayer_x") self.assertEqual(flds[3].name(), "joinlayer_z") @@ -1511,7 +1562,8 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): f1_id = next(layer.getFeatures()).id() self.assertTrue(layer.changeAttributeValue(f1_id, 0, 'coconut')) # note - this isn't 100% accurate, since orange no longer exists - but it avoids looping through all features - self.assertEqual(set(layer.uniqueStringsMatching(0, 'n')), set(['orange', 'BanaNa', 'waterMelon', 'pineapple', 'coconut'])) + self.assertEqual(set(layer.uniqueStringsMatching(0, 'n')), + set(['orange', 'BanaNa', 'waterMelon', 'pineapple', 'coconut'])) def testMinValue(self): """ test retrieving minimum values """ @@ -1590,7 +1642,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertFalse(layer.deleteFeature(-333)) # we do not check for existence of the feature id if it's # not newly added feature - #self.assertFalse(layer.deleteFeature(333)) + # self.assertFalse(layer.deleteFeature(333)) # CHANGE GEOMETRY @@ -1636,7 +1688,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): def test_ExpressionField(self): layer = createLayerWithOnePoint() - cnt = layer.pendingFields().count() + cnt = layer.fields().count() idx = layer.addExpressionField('5', QgsField('test', QVariant.LongLong)) @@ -1656,7 +1708,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer.removeExpressionField(idx) - self.assertEqual(layer.pendingFields().count(), cnt) + self.assertEqual(layer.fields().count(), cnt) # expression field which references itself idx = layer.addExpressionField('sum(test2)', QgsField('test2', QVariant.LongLong)) @@ -1664,7 +1716,7 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertEqual(fet['test2'], NULL) def test_ExpressionFieldEllipsoidLengthCalculation(self): - #create a temporary layer + # create a temporary layer temp_layer = QgsVectorLayer("LineString?crs=epsg:3111&field=pk:int", "vl", "memory") self.assertTrue(temp_layer.isValid()) f1 = QgsFeature(temp_layer.dataProvider().fields(), 1) @@ -1692,12 +1744,14 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertAlmostEqual(f['length'], expected, 3) def test_ExpressionFieldEllipsoidAreaCalculation(self): - #create a temporary layer + # create a temporary layer temp_layer = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int", "vl", "memory") self.assertTrue(temp_layer.isValid()) f1 = QgsFeature(temp_layer.dataProvider().fields(), 1) f1.setAttribute("pk", 1) - f1.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(2484588, 2425722), QgsPointXY(2482767, 2398853), QgsPointXY(2520109, 2397715), QgsPointXY(2520792, 2425494), QgsPointXY(2484588, 2425722)]])) + f1.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(2484588, 2425722), QgsPointXY(2482767, 2398853), + QgsPointXY(2520109, 2397715), QgsPointXY(2520792, 2425494), + QgsPointXY(2484588, 2425722)]])) temp_layer.dataProvider().addFeatures([f1]) # set project CRS and ellipsoid @@ -1726,11 +1780,11 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): features = layer.getFeatures(QgsFeatureRequest().setFilterExpression('"test" = 6')) - assert(len(list(features)) == 0) + assert (len(list(features)) == 0) features = layer.getFeatures(QgsFeatureRequest().setFilterExpression('"test" = 5')) - assert(len(list(features)) == 1) + assert (len(list(features)) == 1) def testSelectByIds(self): """ Test selecting by ID""" @@ -2007,14 +2061,28 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer.setDefaultValueDefinition(0, QgsDefaultValue("'test'")) self.assertTrue(layer.defaultValueDefinition(0)) self.assertEqual(layer.defaultValueDefinition(0).expression(), "'test'") + self.assertFalse(layer.defaultValueDefinition(0).applyOnUpdate()) self.assertFalse(layer.defaultValueDefinition(1)) + self.assertFalse(layer.defaultValueDefinition(1).applyOnUpdate()) self.assertFalse(layer.defaultValueDefinition(2)) + self.assertFalse(layer.defaultValueDefinition(2).applyOnUpdate()) self.assertEqual(layer.fields().at(0).defaultValueDefinition().expression(), "'test'") layer.setDefaultValueDefinition(1, QgsDefaultValue("2+2")) self.assertEqual(layer.defaultValueDefinition(0).expression(), "'test'") + self.assertFalse(layer.defaultValueDefinition(0).applyOnUpdate()) self.assertEqual(layer.defaultValueDefinition(1).expression(), "2+2") + self.assertFalse(layer.defaultValueDefinition(1).applyOnUpdate()) self.assertFalse(layer.defaultValueDefinition(2)) + self.assertFalse(layer.defaultValueDefinition(2).applyOnUpdate()) + self.assertEqual(layer.fields().at(0).defaultValueDefinition().expression(), "'test'") + self.assertEqual(layer.fields().at(1).defaultValueDefinition().expression(), "2+2") + + layer.setDefaultValueDefinition(1, QgsDefaultValue("2+2", True)) + self.assertEqual(layer.defaultValueDefinition(0).expression(), "'test'") + self.assertFalse(layer.defaultValueDefinition(0).applyOnUpdate()) + self.assertEqual(layer.defaultValueDefinition(1).expression(), "2+2") + self.assertTrue(layer.defaultValueDefinition(1).applyOnUpdate()) self.assertEqual(layer.fields().at(0).defaultValueDefinition().expression(), "'test'") self.assertEqual(layer.fields().at(1).defaultValueDefinition().expression(), "2+2") @@ -2150,14 +2218,16 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull) layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique) self.assertEqual(layer.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull) - self.assertEqual(layer.fieldConstraints(1), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) + self.assertEqual(layer.fieldConstraints(1), + QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) self.assertFalse(layer.fieldConstraints(2)) self.assertEqual(layer.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull) self.assertEqual(layer.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginLayer) self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintStrengthHard) - self.assertEqual(layer.fields().at(1).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) + self.assertEqual(layer.fields().at(1).constraints().constraints(), + QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) self.assertEqual(layer.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginLayer) self.assertEqual(layer.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique), @@ -2209,13 +2279,15 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): layer3 = createLayerWithOnePoint() self.assertTrue(layer3.readXml(elem, QgsReadWriteContext())) self.assertEqual(layer3.fieldConstraints(0), QgsFieldConstraints.ConstraintNotNull) - self.assertEqual(layer3.fieldConstraints(1), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) + self.assertEqual(layer3.fieldConstraints(1), + QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) self.assertEqual(layer3.fields().at(0).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull) self.assertEqual(layer3.fields().at(0).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginLayer) self.assertEqual(layer.fields().at(0).constraints().constraintStrength(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintStrengthHard) - self.assertEqual(layer3.fields().at(1).constraints().constraints(), QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) + self.assertEqual(layer3.fields().at(1).constraints().constraints(), + QgsFieldConstraints.ConstraintNotNull | QgsFieldConstraints.ConstraintUnique) self.assertEqual(layer3.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginLayer) self.assertEqual(layer3.fields().at(1).constraints().constraintOrigin(QgsFieldConstraints.ConstraintUnique), @@ -2557,7 +2629,8 @@ class TestQgsVectorLayer(unittest.TestCase, FeatureSourceTestCase): self.assertAlmostEqual(virtual_values[4], -65.32, 2) # repeat, with reprojection on request - request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785')) + request = QgsFeatureRequest().setDestinationCrs(QgsCoordinateReferenceSystem('epsg:3785'), + QgsProject.instance().transformContext()) features = [f for f in layer.getFeatures(request)] # virtual field value should not change, even though geometry has self.assertAlmostEqual(features[0]['virtual'], -71.123, 2) diff --git a/tests/src/python/test_qgsvectorlayereditbuffer.py b/tests/src/python/test_qgsvectorlayereditbuffer.py index eaf6d17bc11..465c16714ca 100644 --- a/tests/src/python/test_qgsvectorlayereditbuffer.py +++ b/tests/src/python/test_qgsvectorlayereditbuffer.py @@ -40,7 +40,7 @@ def createLayerWithOnePoint(): f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) assert pr.addFeatures([f]) - assert layer.pendingFeatureCount() == 1 + assert layer.featureCount() == 1 return layer diff --git a/tests/src/python/test_qgsvectorlayerutils.py b/tests/src/python/test_qgsvectorlayerutils.py index 20b2c7d6799..0a1168dc022 100644 --- a/tests/src/python/test_qgsvectorlayerutils.py +++ b/tests/src/python/test_qgsvectorlayerutils.py @@ -39,7 +39,7 @@ def createLayerWithOnePoint(): f = QgsFeature() f.setAttributes(["test", 123]) assert pr.addFeatures([f]) - assert layer.pendingFeatureCount() == 1 + assert layer.featureCount() == 1 return layer diff --git a/tests/testdata/control_images/25d_renderer/expected_25d_composer/expected_25d_composer_mask.png b/tests/testdata/control_images/25d_renderer/expected_25d_composer/expected_25d_composer_mask.png new file mode 100644 index 00000000000..176fd7d8283 Binary files /dev/null and b/tests/testdata/control_images/25d_renderer/expected_25d_composer/expected_25d_composer_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_autoscale1/expected_atlas_autoscale1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_autoscale1/expected_atlas_autoscale1_mask.png index 4123b16682d..920776f72f6 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_autoscale1/expected_atlas_autoscale1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_autoscale1/expected_atlas_autoscale1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_autoscale2/expected_atlas_autoscale2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_autoscale2/expected_atlas_autoscale2_mask.png index eed690181d6..b8e968eb553 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_autoscale2/expected_atlas_autoscale2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_autoscale2/expected_atlas_autoscale2_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_filtering1/expected_atlas_filtering1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_filtering1/expected_atlas_filtering1_mask.png index f83b78a02ab..7724225763f 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_filtering1/expected_atlas_filtering1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_filtering1/expected_atlas_filtering1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_fixedscale1/expected_atlas_fixedscale1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_fixedscale1/expected_atlas_fixedscale1_mask.png index 384a04c9219..c9617a146e6 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_fixedscale1/expected_atlas_fixedscale1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_fixedscale1/expected_atlas_fixedscale1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_fixedscale2/expected_atlas_fixedscale2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_fixedscale2/expected_atlas_fixedscale2_mask.png index 81476e821e7..6ba6c62bc9b 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_fixedscale2/expected_atlas_fixedscale2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_fixedscale2/expected_atlas_fixedscale2_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_hiding1/expected_atlas_hiding1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_hiding1/expected_atlas_hiding1_mask.png index 385c7ecfe0b..d49f55a2246 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_hiding1/expected_atlas_hiding1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_hiding1/expected_atlas_hiding1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_hiding2/expected_atlas_hiding2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_hiding2/expected_atlas_hiding2_mask.png index a4cfcb66968..42d3f3a9089 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_hiding2/expected_atlas_hiding2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_hiding2/expected_atlas_hiding2_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_predefinedscales1/expected_atlas_predefinedscales1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_predefinedscales1/expected_atlas_predefinedscales1_mask.png index 16fa3dd387c..1cffbb42e6f 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_predefinedscales1/expected_atlas_predefinedscales1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_predefinedscales1/expected_atlas_predefinedscales1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_predefinedscales2/expected_atlas_predefinedscales2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_predefinedscales2/expected_atlas_predefinedscales2_mask.png index 5b96ecc9635..3280671a537 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_predefinedscales2/expected_atlas_predefinedscales2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_predefinedscales2/expected_atlas_predefinedscales2_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_sorting1/expected_atlas_sorting1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_sorting1/expected_atlas_sorting1_mask.png index fd82e50a425..a5699e83999 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_sorting1/expected_atlas_sorting1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_sorting1/expected_atlas_sorting1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_sorting2/expected_atlas_sorting2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_sorting2/expected_atlas_sorting2_mask.png index 412dd671f4a..c986bfd62d0 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_sorting2/expected_atlas_sorting2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_sorting2/expected_atlas_sorting2_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_two_maps1/expected_atlas_two_maps1_mask.png b/tests/testdata/control_images/atlas/expected_atlas_two_maps1/expected_atlas_two_maps1_mask.png index b643e8396b9..56f51f7408e 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_two_maps1/expected_atlas_two_maps1_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_two_maps1/expected_atlas_two_maps1_mask.png differ diff --git a/tests/testdata/control_images/atlas/expected_atlas_two_maps2/expected_atlas_two_maps2_mask.png b/tests/testdata/control_images/atlas/expected_atlas_two_maps2/expected_atlas_two_maps2_mask.png index ce6754b7d65..b2b910d2d1b 100644 Binary files a/tests/testdata/control_images/atlas/expected_atlas_two_maps2/expected_atlas_two_maps2_mask.png and b/tests/testdata/control_images/atlas/expected_atlas_two_maps2/expected_atlas_two_maps2_mask.png differ diff --git a/tests/testdata/control_images/composer_effects/expected_composereffects_blend/expected_composereffects_blend_mask.png b/tests/testdata/control_images/composer_effects/expected_composereffects_blend/expected_composereffects_blend_mask.png index 411d102f091..d4729a0fc5e 100644 Binary files a/tests/testdata/control_images/composer_effects/expected_composereffects_blend/expected_composereffects_blend_mask.png and b/tests/testdata/control_images/composer_effects/expected_composereffects_blend/expected_composereffects_blend_mask.png differ diff --git a/tests/testdata/control_images/composer_effects/expected_composereffects_transparency/expected_composereffects_transparency_mask.png b/tests/testdata/control_images/composer_effects/expected_composereffects_transparency/expected_composereffects_transparency_mask.png index 4da98757358..a02e6434e32 100644 Binary files a/tests/testdata/control_images/composer_effects/expected_composereffects_transparency/expected_composereffects_transparency_mask.png and b/tests/testdata/control_images/composer_effects/expected_composereffects_transparency/expected_composereffects_transparency_mask.png differ diff --git a/tests/testdata/control_images/composer_html/expected_composerhtml_setfeature/travis/expected_composerhtml_setfeature_mask.png b/tests/testdata/control_images/composer_html/expected_composerhtml_setfeature/travis/expected_composerhtml_setfeature_mask.png new file mode 100644 index 00000000000..33941be4fd4 Binary files /dev/null and b/tests/testdata/control_images/composer_html/expected_composerhtml_setfeature/travis/expected_composerhtml_setfeature_mask.png differ diff --git a/tests/testdata/control_images/composer_items/expected_composerrotation_mapitemrotation/expected_composerrotation_mapitemrotation_mask.png b/tests/testdata/control_images/composer_items/expected_composerrotation_mapitemrotation/expected_composerrotation_mapitemrotation_mask.png index b36388b1724..9897ca0818a 100644 Binary files a/tests/testdata/control_images/composer_items/expected_composerrotation_mapitemrotation/expected_composerrotation_mapitemrotation_mask.png and b/tests/testdata/control_images/composer_items/expected_composerrotation_mapitemrotation/expected_composerrotation_mapitemrotation_mask.png differ diff --git a/tests/testdata/control_images/composer_items/expected_composerrotation_maprotation/expected_composerrotation_maprotation_mask.png b/tests/testdata/control_images/composer_items/expected_composerrotation_maprotation/expected_composerrotation_maprotation_mask.png index b529c1372d2..7d7e43af643 100644 Binary files a/tests/testdata/control_images/composer_items/expected_composerrotation_maprotation/expected_composerrotation_maprotation_mask.png and b/tests/testdata/control_images/composer_items/expected_composerrotation_maprotation/expected_composerrotation_maprotation_mask.png differ diff --git a/tests/testdata/control_images/composer_items/expected_composerrotation_shape/expected_composerrotation_shape_mask.png b/tests/testdata/control_images/composer_items/expected_composerrotation_shape/expected_composerrotation_shape_mask.png new file mode 100644 index 00000000000..1f612d13897 Binary files /dev/null and b/tests/testdata/control_images/composer_items/expected_composerrotation_shape/expected_composerrotation_shape_mask.png differ diff --git a/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label.png b/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label.png new file mode 100644 index 00000000000..de914612304 Binary files /dev/null and b/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label.png differ diff --git a/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label_mask.png b/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label_mask.png new file mode 100644 index 00000000000..75c5f1c7b36 Binary files /dev/null and b/tests/testdata/control_images/composer_items/expected_layoutrotation_label/expected_layoutrotation_label_mask.png differ diff --git a/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized.png b/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized.png new file mode 100644 index 00000000000..4730b65c011 Binary files /dev/null and b/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized.png differ diff --git a/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized_mask.png b/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized_mask.png new file mode 100644 index 00000000000..3a95170e1c3 Binary files /dev/null and b/tests/testdata/control_images/composer_map/expected_layoutmap_rasterized/expected_layoutmap_rasterized_mask.png differ diff --git a/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/expected_composermap_grid.png b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/default/expected_composermap_grid.png similarity index 100% rename from tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/expected_composermap_grid.png rename to tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/default/expected_composermap_grid.png diff --git a/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/expected_composermap_grid_mask.png b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/default/expected_composermap_grid_mask.png similarity index 100% rename from tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/expected_composermap_grid_mask.png rename to tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/default/expected_composermap_grid_mask.png diff --git a/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid.png b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid.png new file mode 100644 index 00000000000..1669c18c254 Binary files /dev/null and b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid.png differ diff --git a/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid_mask.png b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid_mask.png new file mode 100644 index 00000000000..ac406bfd9e3 Binary files /dev/null and b/tests/testdata/control_images/composer_mapgrid/expected_composermap_grid/fedora/expected_composermap_grid_mask.png differ diff --git a/tests/testdata/control_images/composer_mapgrid/expected_composermap_gridreprojected/expected_composermap_gridreprojected_mask.png b/tests/testdata/control_images/composer_mapgrid/expected_composermap_gridreprojected/expected_composermap_gridreprojected_mask.png index efe6000fb7d..c713bff29ce 100644 Binary files a/tests/testdata/control_images/composer_mapgrid/expected_composermap_gridreprojected/expected_composermap_gridreprojected_mask.png and b/tests/testdata/control_images/composer_mapgrid/expected_composermap_gridreprojected/expected_composermap_gridreprojected_mask.png differ diff --git a/tests/testdata/control_images/composer_mapoverview/expected_composermap_overview_blending/expected_composermap_overview_blending_mask.png b/tests/testdata/control_images/composer_mapoverview/expected_composermap_overview_blending/expected_composermap_overview_blending_mask.png index b4dbaa65078..c2ebaf17b9e 100644 Binary files a/tests/testdata/control_images/composer_mapoverview/expected_composermap_overview_blending/expected_composermap_overview_blending_mask.png and b/tests/testdata/control_images/composer_mapoverview/expected_composermap_overview_blending/expected_composermap_overview_blending_mask.png differ diff --git a/tests/testdata/control_images/composer_paper/expected_composerpaper_default/expected_composerpaper_default_mask.png b/tests/testdata/control_images/composer_paper/expected_composerpaper_default/expected_composerpaper_default_mask.png new file mode 100644 index 00000000000..ccb84479251 Binary files /dev/null and b/tests/testdata/control_images/composer_paper/expected_composerpaper_default/expected_composerpaper_default_mask.png differ diff --git a/tests/testdata/control_images/composer_paper/expected_composerpaper_markerborder/layout/expected_composerpaper_markerborder.png b/tests/testdata/control_images/composer_paper/expected_composerpaper_markerborder/layout/expected_composerpaper_markerborder.png index 90c3c8da75a..9316afcfbc3 100644 Binary files a/tests/testdata/control_images/composer_paper/expected_composerpaper_markerborder/layout/expected_composerpaper_markerborder.png and b/tests/testdata/control_images/composer_paper/expected_composerpaper_markerborder/layout/expected_composerpaper_markerborder.png differ diff --git a/tests/testdata/control_images/composer_shapes/expected_composershapes_ellipse/expected_composershapes_ellipse_mask.png b/tests/testdata/control_images/composer_shapes/expected_composershapes_ellipse/expected_composershapes_ellipse_mask.png index 9a2c62942e4..2c6b58978db 100644 Binary files a/tests/testdata/control_images/composer_shapes/expected_composershapes_ellipse/expected_composershapes_ellipse_mask.png and b/tests/testdata/control_images/composer_shapes/expected_composershapes_ellipse/expected_composershapes_ellipse_mask.png differ diff --git a/tests/testdata/control_images/composer_shapes/expected_composershapes_roundedrect/expected_composershapes_roundedrect_mask.png b/tests/testdata/control_images/composer_shapes/expected_composershapes_roundedrect/expected_composershapes_roundedrect_mask.png index 445c459af6c..72c23ec10d5 100644 Binary files a/tests/testdata/control_images/composer_shapes/expected_composershapes_roundedrect/expected_composershapes_roundedrect_mask.png and b/tests/testdata/control_images/composer_shapes/expected_composershapes_roundedrect/expected_composershapes_roundedrect_mask.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerAtlas_0/expected_importComposerAtlas_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerAtlas_0/expected_importComposerAtlas_0.png new file mode 100644 index 00000000000..8a0225684c1 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerAtlas_0/expected_importComposerAtlas_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateArrow_0/expected_importComposerTemplateArrow_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateArrow_0/expected_importComposerTemplateArrow_0.png new file mode 100644 index 00000000000..c47ebe949cd Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateArrow_0/expected_importComposerTemplateArrow_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateAttributeTable_0/expected_importComposerTemplateAttributeTable_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateAttributeTable_0/expected_importComposerTemplateAttributeTable_0.png new file mode 100644 index 00000000000..6ac0b779faf Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateAttributeTable_0/expected_importComposerTemplateAttributeTable_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateHtml_0/expected_importComposerTemplateHtml_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateHtml_0/expected_importComposerTemplateHtml_0.png new file mode 100644 index 00000000000..99c4c6fbf55 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateHtml_0/expected_importComposerTemplateHtml_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0.png new file mode 100644 index 00000000000..42a4ea29f81 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0_mask.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0_mask.png new file mode 100644 index 00000000000..32a6fd92d91 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLabel_0/expected_importComposerTemplateLabel_0_mask.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLegend_0/expected_importComposerTemplateLegend_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLegend_0/expected_importComposerTemplateLegend_0.png new file mode 100644 index 00000000000..7f7977ff5f3 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateLegend_0/expected_importComposerTemplateLegend_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0.png new file mode 100644 index 00000000000..7e2c1106b70 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0_mask.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0_mask.png new file mode 100644 index 00000000000..44fa1220df4 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateMap_0/expected_importComposerTemplateMap_0_mask.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePicture_0/expected_importComposerTemplatePicture_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePicture_0/expected_importComposerTemplatePicture_0.png new file mode 100644 index 00000000000..558be239cfc Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePicture_0/expected_importComposerTemplatePicture_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolygon_0/expected_importComposerTemplatePolygon_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolygon_0/expected_importComposerTemplatePolygon_0.png new file mode 100644 index 00000000000..9f04d97d93d Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolygon_0/expected_importComposerTemplatePolygon_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolyline_0/expected_importComposerTemplatePolyline_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolyline_0/expected_importComposerTemplatePolyline_0.png new file mode 100644 index 00000000000..d892401ccac Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplatePolyline_0/expected_importComposerTemplatePolyline_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateScaleBar_0/expected_importComposerTemplateScaleBar_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateScaleBar_0/expected_importComposerTemplateScaleBar_0.png new file mode 100644 index 00000000000..8f3c7c0eaf3 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateScaleBar_0/expected_importComposerTemplateScaleBar_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateShape_0/expected_importComposerTemplateShape_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateShape_0/expected_importComposerTemplateShape_0.png new file mode 100644 index 00000000000..dc7225e5205 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplateShape_0/expected_importComposerTemplateShape_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0.png new file mode 100644 index 00000000000..d8306062d6f Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0_mask.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0_mask.png new file mode 100644 index 00000000000..a214b3f6978 Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_0/expected_importComposerTemplate_0_mask.png differ diff --git a/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_1/expected_importComposerTemplate_1.png b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_1/expected_importComposerTemplate_1.png new file mode 100644 index 00000000000..cdb9a41b53a Binary files /dev/null and b/tests/testdata/control_images/compositionconverter/expected_importComposerTemplate_1/expected_importComposerTemplate_1.png differ diff --git a/tests/testdata/control_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset_mask.png b/tests/testdata/control_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset_mask.png index 56685765af6..8683f199cb4 100644 Binary files a/tests/testdata/control_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset_mask.png and b/tests/testdata/control_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset_mask.png differ diff --git a/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial.png b/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial.png new file mode 100644 index 00000000000..1e2261f83b5 Binary files /dev/null and b/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial.png differ diff --git a/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial_mask.png b/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial_mask.png new file mode 100644 index 00000000000..c953c2e1340 Binary files /dev/null and b/tests/testdata/control_images/labelingengine/expected_label_rotate_hide_partial/expected_label_rotate_hide_partial_mask.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page1/expected_layoutexporter_exporttoimagecropped_page1.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page1/expected_layoutexporter_exporttoimagecropped_page1.png new file mode 100644 index 00000000000..f7d5421ade7 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page1/expected_layoutexporter_exporttoimagecropped_page1.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page2/expected_layoutexporter_exporttoimagecropped_page2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page2/expected_layoutexporter_exporttoimagecropped_page2.png new file mode 100644 index 00000000000..b7812b816f8 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagecropped_page2/expected_layoutexporter_exporttoimagecropped_page2.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page1/expected_layoutexporter_exporttoimagedpi_page1.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page1/expected_layoutexporter_exporttoimagedpi_page1.png new file mode 100644 index 00000000000..4ee4721dee6 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page1/expected_layoutexporter_exporttoimagedpi_page1.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page2/expected_layoutexporter_exporttoimagedpi_page2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page2/expected_layoutexporter_exporttoimagedpi_page2.png new file mode 100644 index 00000000000..37ead9bc4ab Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagedpi_page2/expected_layoutexporter_exporttoimagedpi_page2.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagesize_page2/expected_layoutexporter_exporttoimagesize_page2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagesize_page2/expected_layoutexporter_exporttoimagesize_page2.png new file mode 100644 index 00000000000..37641a1ffd9 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttoimagesize_page2/expected_layoutexporter_exporttoimagesize_page2.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1.png new file mode 100644 index 00000000000..4ee4721dee6 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1_mask.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1_mask.png new file mode 100644 index 00000000000..2134c41e4da Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page1/expected_layoutexporter_exporttopdfdpi_page1_mask.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2.png new file mode 100644 index 00000000000..37ead9bc4ab Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2_mask.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2_mask.png new file mode 100644 index 00000000000..e6f008bfd6f Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_exporttopdfdpi_page2/expected_layoutexporter_exporttopdfdpi_page2_mask.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1.png new file mode 100644 index 00000000000..16092fef13f Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1_mask.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1_mask.png new file mode 100644 index 00000000000..8164cc99550 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage1/expected_layoutexporter_iteratortoimage1_mask.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2.png new file mode 100644 index 00000000000..7407399e8f6 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2_mask.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2_mask.png new file mode 100644 index 00000000000..9380157f21e Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_iteratortoimage2/expected_layoutexporter_iteratortoimage2_mask.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderpage/expected_layoutexporter_renderpage.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderpage/expected_layoutexporter_renderpage.png new file mode 100644 index 00000000000..a5a2e21ae49 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderpage/expected_layoutexporter_renderpage.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderregion/expected_layoutexporter_renderregion.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderregion/expected_layoutexporter_renderregion.png new file mode 100644 index 00000000000..6660fb73a79 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_renderregion/expected_layoutexporter_renderregion.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimagepage/expected_layoutexporter_rendertoimagepage.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimagepage/expected_layoutexporter_rendertoimagepage.png new file mode 100644 index 00000000000..a5a2e21ae49 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimagepage/expected_layoutexporter_rendertoimagepage.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregiondpi/expected_layoutexporter_rendertoimageregiondpi.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregiondpi/expected_layoutexporter_rendertoimageregiondpi.png new file mode 100644 index 00000000000..429ffdae6cb Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregiondpi/expected_layoutexporter_rendertoimageregiondpi.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionoverridedpi/expected_layoutexporter_rendertoimageregionoverridedpi.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionoverridedpi/expected_layoutexporter_rendertoimageregionoverridedpi.png new file mode 100644 index 00000000000..08b61fa1412 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionoverridedpi/expected_layoutexporter_rendertoimageregionoverridedpi.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionsize/expected_layoutexporter_rendertoimageregionsize.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionsize/expected_layoutexporter_rendertoimageregionsize.png new file mode 100644 index 00000000000..6660fb73a79 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_rendertoimageregionsize/expected_layoutexporter_rendertoimageregionsize.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page1/expected_layoutexporter_report_page1.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page1/expected_layoutexporter_report_page1.png new file mode 100644 index 00000000000..4ee4721dee6 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page1/expected_layoutexporter_report_page1.png differ diff --git a/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page2/expected_layoutexporter_report_page2.png b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page2/expected_layoutexporter_report_page2.png new file mode 100644 index 00000000000..e6e4ff33056 Binary files /dev/null and b/tests/testdata/control_images/layout_exporter/expected_layoutexporter_report_page2/expected_layoutexporter_report_page2.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png new file mode 100644 index 00000000000..e9c6bf7539a Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/fedora/expected_layoutscalebar_numeric_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/fedora/expected_layoutscalebar_numeric_mask.png new file mode 100644 index 00000000000..b03077944e8 Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/fedora/expected_layoutscalebar_numeric_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/travis/expected_layoutscalebar_numeric_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/travis/expected_layoutscalebar_numeric_mask.png new file mode 100644 index 00000000000..190d2c10713 Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numeric/travis/expected_layoutscalebar_numeric_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png new file mode 100644 index 00000000000..e9c6bf7539a Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png new file mode 100644 index 00000000000..0ef8f063335 Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png new file mode 100644 index 00000000000..e9c6bf7539a Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations.png index 36557bc6bd6..4d222b80999 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations.png and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations_mask.png index 4d33e773014..fa5e0f6ff49 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations_mask.png and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Annotations/WMS_GetMap_Annotations_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC.png new file mode 100644 index 00000000000..12da8bb0ee5 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC_mask.png new file mode 100644 index 00000000000..9fa1281fe13 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Filter_OGC/WMS_GetMap_Filter_OGC_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit_mask.png new file mode 100644 index 00000000000..ae61f538575 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_16bit/WMS_GetMap_Mode_16bit_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png index 4231097d842..88b9ab67b64 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit_mask.png new file mode 100644 index 00000000000..8e7c87580b3 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_1bit/WMS_GetMap_Mode_1bit_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_8bit/WMS_GetMap_Mode_8bit_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_8bit/WMS_GetMap_Mode_8bit_mask.png new file mode 100644 index 00000000000..017aa9bea19 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_Mode_8bit/WMS_GetMap_Mode_8bit_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom.png index dfb8b557f7b..35796cc66e6 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom.png and b/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom_mask.png index 73a26c7f53d..fd34d911f2c 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom_mask.png and b/tests/testdata/control_images/qgis_server/WMS_GetMap_StyleCustom/WMS_GetMap_StyleCustom_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Basic/WMS_GetPrint_Basic.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Basic/WMS_GetPrint_Basic.png index 4f340b07a95..360a8d94306 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Basic/WMS_GetPrint_Basic.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Basic/WMS_GetPrint_Basic.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Grid/WMS_GetPrint_Grid.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Grid/WMS_GetPrint_Grid.png index 051940c2c01..fa5209fddf3 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Grid/WMS_GetPrint_Grid.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Grid/WMS_GetPrint_Grid.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight.png index 35858670263..2a1cdb772c4 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight_mask.png index a44f6c14308..b54f8ad1000 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight_mask.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Highlight/WMS_GetPrint_Highlight_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelRemoved/WMS_GetPrint_LabelRemoved.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelRemoved/WMS_GetPrint_LabelRemoved.png index c08284f6683..fefc88da0fa 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelRemoved/WMS_GetPrint_LabelRemoved.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelRemoved/WMS_GetPrint_LabelRemoved.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelUpdated/WMS_GetPrint_LabelUpdated.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelUpdated/WMS_GetPrint_LabelUpdated.png index a63f00c1d59..4c43058a8eb 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelUpdated/WMS_GetPrint_LabelUpdated.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_LabelUpdated/WMS_GetPrint_LabelUpdated.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Legend/WMS_GetPrint_Legend.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Legend/WMS_GetPrint_Legend.png index 022ac05a628..8ffc8cf3c0a 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Legend/WMS_GetPrint_Legend.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Legend/WMS_GetPrint_Legend.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity.png index 1a71e0d3758..c9b692c2dd6 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity_mask.png index b904e0738b8..051f2bb7d92 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity_mask.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Opacity/WMS_GetPrint_Opacity_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Rotation/WMS_GetPrint_Rotation.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Rotation/WMS_GetPrint_Rotation.png index e50979a7749..a9c3bdd82ec 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Rotation/WMS_GetPrint_Rotation.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Rotation/WMS_GetPrint_Rotation.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_SRS/WMS_GetPrint_SRS.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_SRS/WMS_GetPrint_SRS.png index 935e00f09ad..49a3a111772 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_SRS/WMS_GetPrint_SRS.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_SRS/WMS_GetPrint_SRS.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Scale/WMS_GetPrint_Scale.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Scale/WMS_GetPrint_Scale.png index 0877d37b57b..325f5073417 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Scale/WMS_GetPrint_Scale.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Scale/WMS_GetPrint_Scale.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Selection/WMS_GetPrint_Selection.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Selection/WMS_GetPrint_Selection.png index 4f340b07a95..360a8d94306 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_Selection/WMS_GetPrint_Selection.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_Selection/WMS_GetPrint_Selection.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom.png index 6c5c8b83cc5..c49f2109872 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom_mask.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom_mask.png index b5b69e7bafe..ee9972c7b48 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom_mask.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleCustom/WMS_GetPrint_StyleCustom_mask.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleDefault/WMS_GetPrint_StyleDefault.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleDefault/WMS_GetPrint_StyleDefault.png index fd51e9d7feb..e06eac793e7 100644 Binary files a/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleDefault/WMS_GetPrint_StyleDefault.png and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_StyleDefault/WMS_GetPrint_StyleDefault.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_TwoMaps/WMS_GetPrint_TwoMaps.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_TwoMaps/WMS_GetPrint_TwoMaps.png new file mode 100644 index 00000000000..b92c2a89b50 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_TwoMaps/WMS_GetPrint_TwoMaps.png differ diff --git a/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC.png b/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC.png new file mode 100644 index 00000000000..849c64ca976 Binary files /dev/null and b/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC.png differ diff --git a/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC_mask.png b/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC_mask.png new file mode 100644 index 00000000000..9cb756fd327 Binary files /dev/null and b/tests/testdata/control_images/qgis_server_accesscontrol/Restricted_WMS_GetMap_projectsubstring_OGC/Restricted_WMS_GetMap_projectsubstring_OGC_mask.png differ diff --git a/tests/testdata/layouts/2x_template.qpt b/tests/testdata/layouts/2x_template.qpt new file mode 100644 index 00000000000..66470a1e790 --- /dev/null +++ b/tests/testdata/layouts/2x_template.qpt @@ -0,0 +1,833 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_arrow.qpt b/tests/testdata/layouts/2x_template_arrow.qpt new file mode 100644 index 00000000000..44e24e9534a --- /dev/null +++ b/tests/testdata/layouts/2x_template_arrow.qpt @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_atlas.qpt b/tests/testdata/layouts/2x_template_atlas.qpt new file mode 100644 index 00000000000..e52f65a1107 --- /dev/null +++ b/tests/testdata/layouts/2x_template_atlas.qpt @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_attributetable.qpt b/tests/testdata/layouts/2x_template_attributetable.qpt new file mode 100644 index 00000000000..b084ccae299 --- /dev/null +++ b/tests/testdata/layouts/2x_template_attributetable.qpt @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_html.qpt b/tests/testdata/layouts/2x_template_html.qpt new file mode 100644 index 00000000000..bc410412b32 --- /dev/null +++ b/tests/testdata/layouts/2x_template_html.qpt @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_label.qpt b/tests/testdata/layouts/2x_template_label.qpt new file mode 100644 index 00000000000..3410b945f44 --- /dev/null +++ b/tests/testdata/layouts/2x_template_label.qpt @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_legend.qpt b/tests/testdata/layouts/2x_template_legend.qpt new file mode 100644 index 00000000000..bf56a759e37 --- /dev/null +++ b/tests/testdata/layouts/2x_template_legend.qpt @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_map.qpt b/tests/testdata/layouts/2x_template_map.qpt new file mode 100644 index 00000000000..d22b0babb62 --- /dev/null +++ b/tests/testdata/layouts/2x_template_map.qpt @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_map_overview.qpt b/tests/testdata/layouts/2x_template_map_overview.qpt new file mode 100644 index 00000000000..9ced543b25f --- /dev/null +++ b/tests/testdata/layouts/2x_template_map_overview.qpt @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_pictures.qpt b/tests/testdata/layouts/2x_template_pictures.qpt new file mode 100644 index 00000000000..30195f89790 --- /dev/null +++ b/tests/testdata/layouts/2x_template_pictures.qpt @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_polygon.qpt b/tests/testdata/layouts/2x_template_polygon.qpt new file mode 100644 index 00000000000..98603f45e9c --- /dev/null +++ b/tests/testdata/layouts/2x_template_polygon.qpt @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_polyline.qpt b/tests/testdata/layouts/2x_template_polyline.qpt new file mode 100644 index 00000000000..626ef657bd0 --- /dev/null +++ b/tests/testdata/layouts/2x_template_polyline.qpt @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_portrait.qpt b/tests/testdata/layouts/2x_template_portrait.qpt new file mode 100644 index 00000000000..f2f5dead0fd --- /dev/null +++ b/tests/testdata/layouts/2x_template_portrait.qpt @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_scalebar.qpt b/tests/testdata/layouts/2x_template_scalebar.qpt new file mode 100644 index 00000000000..8920aede2bc --- /dev/null +++ b/tests/testdata/layouts/2x_template_scalebar.qpt @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/2x_template_shape.qpt b/tests/testdata/layouts/2x_template_shape.qpt new file mode 100644 index 00000000000..91fc227d8d5 --- /dev/null +++ b/tests/testdata/layouts/2x_template_shape.qpt @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/layouts/sample_project.qgd b/tests/testdata/layouts/sample_project.qgd new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testdata/layouts/sample_project.qgs b/tests/testdata/layouts/sample_project.qgs new file mode 100644 index 00000000000..8c37aa23e44 --- /dev/null +++ b/tests/testdata/layouts/sample_project.qgs @@ -0,0 +1,1332 @@ + + + + + + + + + + + + + + + + + + + + degrees + + -119.80118356386469713 + 23.95241214538275187 + -82.91169073314071625 + 47.28163022407258609 + + 0 + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + + + polys20171212162309844 + points20171212162310546 + lines20171212162314640 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lines20171212162314640 + ../lines.shp + + + + lines + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + + + + + + + + + + points20171212162310546 + ../points.shp + + + + points + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + + + + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + + + + + + + + + + + + + + + -118.92286230599032137 + 24.50786971868489061 + -83.79001199101509201 + 46.72617265077044379 + + polys20171212162309844 + ../polys.shp + + + + polys + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + + + + + + + + + + + . + + 0 + . + + 0 + generatedlayout + + + + + + + + + + COALESCE( "Name", '<NULL>' ) + + + + + + + + + +proj=longlat +datum=WGS84 +no_defs + EPSG:4326 + 3452 + + + false + + + 0 + 255 + 255 + 255 + 255 + 255 + 255 + + + 2 + current_layer + off + 0 + + + 2 + true + + + false + + + + diff --git a/tests/testdata/polys_overlapping_with_id.dbf b/tests/testdata/polys_overlapping_with_id.dbf index 5aa32a87fc6..775d4ad0421 100644 Binary files a/tests/testdata/polys_overlapping_with_id.dbf and b/tests/testdata/polys_overlapping_with_id.dbf differ diff --git a/tests/testdata/projects/epsg25833.qgs b/tests/testdata/projects/epsg25833.qgs new file mode 100644 index 00000000000..7ef50a6c168 --- /dev/null +++ b/tests/testdata/projects/epsg25833.qgs @@ -0,0 +1,43 @@ + + + + + + + + + + + meters + + 423939.41302644077222794 + 5645532.31057187728583813 + 426709.05210548255126923 + 5647280.68408176116645336 + + 0 + 1 + + + +proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 2106 + 25833 + EPSG:25833 + ETRS89 / UTM zone 33N + utm + GRS80 + false + + + 0 + + + + + + + + + + + diff --git a/tests/testdata/provider/bug_17795.gpkg b/tests/testdata/provider/bug_17795.gpkg new file mode 100644 index 00000000000..7e5fca50508 Binary files /dev/null and b/tests/testdata/provider/bug_17795.gpkg differ diff --git a/tests/testdata/provider/testdata_pg.sql b/tests/testdata/provider/testdata_pg.sql index e253ec5b561..ab3037f4203 100644 --- a/tests/testdata/provider/testdata_pg.sql +++ b/tests/testdata/provider/testdata_pg.sql @@ -238,6 +238,17 @@ CREATE TABLE qgis_test.mls3d( INSERT INTO qgis_test.mls3d values (1, 'srid=4326;MultiLineString((0 0 0, 1 1 1),(2 2 2, 3 3 3))'::geometry); +-- Test of 4D geometries (with Z and M values) + +CREATE TABLE qgis_test.pt4d( + id int, + geom Geometry(PointZM,4326) +); + +INSERT INTO qgis_test.pt4d values (1, 'srid=4326;PointZM(1 2 3 4)'::geometry); + + + ----------------------------------------- -- Test tables with INHERITS -- @@ -499,3 +510,14 @@ CREATE UNIQUE INDEX constraints_uniq ON qgis_test.constraints USING btree (name COLLATE pg_catalog."default"); -- unique index + +CREATE TABLE qgis_test.check_constraints ( + id integer PRIMARY KEY, + a integer, + b integer, CHECK (a > b) +); +INSERT INTO qgis_test.check_constraints VALUES ( + 1, -- id + 4, -- a + 3 -- b +) diff --git a/tests/testdata/qgis_server/getprojectsettings.txt b/tests/testdata/qgis_server/getprojectsettings.txt index d5cb558c1f1..8aead96267b 100644 --- a/tests/testdata/qgis_server/getprojectsettings.txt +++ b/tests/testdata/qgis_server/getprojectsettings.txt @@ -112,8 +112,8 @@ Content-Type: text/xml; charset=utf-8 - - + + diff --git a/tests/testdata/qgis_server_accesscontrol/project.qgs b/tests/testdata/qgis_server_accesscontrol/project.qgs index 204d78f810f..90863eb46bd 100644 --- a/tests/testdata/qgis_server_accesscontrol/project.qgs +++ b/tests/testdata/qgis_server_accesscontrol/project.qgs @@ -1,63 +1,87 @@ - + QGIS Server Hello World - + + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + - + - + - + - + - + - + - + - + - + + + points20150803121107046 + hello20131022151106574 + Hello_copy20150804164427541 + Hello_SubsetString_copy20160222085231770 + Hello_Project_SubsetString_copy20160223113949592 + dem20150730091219559 + country20131022151106556 + Country_copy20161127151800736 + country20170328164317226 + - + - - - - - - - - + + + + + + + + - + meters - -30425236.72921397164463997 - -10846058.0813884325325489 - 29667752.81264735385775566 - 14979028.33329574391245842 + 11863620.20301065221428871 + -4918063.80917443800717592 + 19375243.89574331790208817 + -792006.19749691989272833 0 - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -68,83 +92,58 @@ 0 - - - - - - - - - - - - - - - - hello20131022151106574 - country20131022151106556 - dem20150730091219559 - points20150803121107046 - Hello_copy20150804164427541 - Hello_SubsetString_copy20160222085231770 - Hello_Project_SubsetString_copy20160223113949592 - Country_copy20161127151800736 - country20170328164317226 - - - + - + - + - + - + - + - + - + - + + - + -19619892.68012013286352158 -10327100.34232237376272678 @@ -159,7 +158,7 @@ Country_Labels - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -169,27 +168,50 @@ false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + - + - + - + - + - - + + @@ -202,7 +224,7 @@ - + @@ -222,180 +244,180 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -403,12 +425,12 @@ 0 0 - - - + + + - - + + @@ -430,22 +452,22 @@ - + . - - + + - + - + - . @@ -472,7 +494,7 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - + @@ -485,16 +507,18 @@ def my_form_open(dialog, layer, feature): 2 + - + + - - - + + + - + @@ -503,18 +527,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -523,9 +547,9 @@ def my_form_open(dialog, layer, feature): @@ -534,229 +558,46 @@ def my_form_open(dialog, layer, feature): - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -776,33 +617,32 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - + + - - + + - - + + - + - + - ../../../../../.. @@ -829,7 +669,7 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - + @@ -840,7 +680,7 @@ def my_form_open(dialog, layer, feature): name - + -14746250.07513097859919071 -112075.42807669920148328 @@ -855,7 +695,7 @@ def my_form_open(dialog, layer, feature): Hello_Filter_SubsetString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -865,23 +705,48 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - - + + + - + @@ -890,18 +755,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -910,9 +775,9 @@ def my_form_open(dialog, layer, feature): @@ -921,203 +786,25 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1144,23 +831,22 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - - + + + - - - + + + - - - + + + @@ -1168,14 +854,14 @@ def my_form_open(dialog, layer, feature): - + - + - ../../../../../.. @@ -1202,12 +888,12 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - - + + + - - + + @@ -1220,7 +906,7 @@ def my_form_open(dialog, layer, feature): "pkuid" - + -14746250.07513097859919071 -112075.42807669920148328 @@ -1235,7 +921,7 @@ def my_form_open(dialog, layer, feature): Hello_Project_SubsetString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -1245,23 +931,48 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - - + + + - + @@ -1270,18 +981,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -1290,9 +1001,9 @@ def my_form_open(dialog, layer, feature): @@ -1301,203 +1012,25 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1524,23 +1057,22 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - - + + + - - - + + + - - - + + + @@ -1548,9 +1080,9 @@ def my_form_open(dialog, layer, feature): - + - + ../../../../../.. @@ -1577,12 +1109,12 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - - + + + - - + + @@ -1595,7 +1127,7 @@ def my_form_open(dialog, layer, feature): "pkuid" - + -14746250.07513097859919071 -112075.42807669920148328 @@ -1610,7 +1142,7 @@ def my_form_open(dialog, layer, feature): Hello_SubsetString - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -1620,23 +1152,48 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - - + + + - + @@ -1645,18 +1202,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -1665,9 +1222,9 @@ def my_form_open(dialog, layer, feature): @@ -1676,203 +1233,25 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1899,23 +1278,22 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - - + + + - - - + + + - - - + + + @@ -1923,9 +1301,9 @@ def my_form_open(dialog, layer, feature): - + - + ../../../../../.. @@ -1952,12 +1330,12 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - - + + + - - + + @@ -1970,7 +1348,7 @@ def my_form_open(dialog, layer, feature): "pkuid" - + -19619892.68012013286352158 -10327100.34232237376272678 @@ -1985,7 +1363,7 @@ def my_form_open(dialog, layer, feature): Country - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -1995,27 +1373,51 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + - + - + - + - - + + @@ -2050,147 +1452,147 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 0 @@ -2200,29 +1602,29 @@ def my_form_open(dialog, layer, feature): - - + + - + 0 tablayout @@ -2235,19 +1637,19 @@ def my_form_open(dialog, layer, feature): - + - + - + - + - - + + @@ -2268,7 +1670,7 @@ def my_form_open(dialog, layer, feature): - + @@ -2303,147 +1705,147 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 0 @@ -2453,29 +1855,29 @@ def my_form_open(dialog, layer, feature): - - + + - + 0 tablayout @@ -2488,15 +1890,16 @@ def my_form_open(dialog, layer, feature): - + + - - - + + + - + @@ -2505,18 +1908,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -2525,9 +1928,9 @@ def my_form_open(dialog, layer, feature): @@ -2536,198 +1939,42 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -2747,29 +1994,28 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - + + - - + + - - + + - + - + ../../../../../.. @@ -2780,7 +2026,7 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - + @@ -2791,7 +2037,7 @@ def my_form_open(dialog, layer, feature): "name" - + -19619892.68012013286352158 -10327100.34232237376272678 @@ -2806,7 +2052,7 @@ def my_form_open(dialog, layer, feature): Country_Diagrams - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -2816,23 +2062,48 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - - + + + - + @@ -2841,9 +2112,9 @@ def my_form_open(dialog, layer, feature): @@ -2852,196 +2123,27 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - - - - - + 1 + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -3061,33 +2163,32 @@ def my_form_open(dialog, layer, feature): - - - + + - - + + - - + + - + - + - @@ -3122,7 +2223,7 @@ def my_form_open(dialog, layer, feature): name - + -29.99999999999666755 29.99999999999666755 @@ -3147,18 +2248,42 @@ def my_form_open(dialog, layer, feature): true + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + - + gdal - + - - + + - + None @@ -3174,13 +2299,13 @@ def my_form_open(dialog, layer, feature): StretchToMinimumMaximum - - + + 0 - + -14746250.07513097859919071 -112075.42807669920148328 @@ -3195,7 +2320,7 @@ def my_form_open(dialog, layer, feature): Hello - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -3205,23 +2330,48 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - - + + + - + @@ -3230,18 +2380,18 @@ def my_form_open(dialog, layer, feature): - - + + - + @@ -3250,9 +2400,9 @@ def my_form_open(dialog, layer, feature): @@ -3261,198 +2411,22 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -3479,23 +2453,22 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - - + + + - - - + + + - - - + + + @@ -3503,9 +2476,9 @@ def my_form_open(dialog, layer, feature): - + - + ../../../../../.. @@ -3516,12 +2489,12 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - - + + + - - + + @@ -3534,7 +2507,7 @@ def my_form_open(dialog, layer, feature): COALESCE( "pkuid", '<NULL>' ) - + 1000 2000 @@ -3549,7 +2522,7 @@ def my_form_open(dialog, layer, feature): db_point - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs 3857 3857 EPSG:3857 @@ -3559,41 +2532,66 @@ def my_form_open(dialog, layer, feature): false + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + spatialite - - + + - + + - - + + - + - + - + @@ -3602,198 +2600,22 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 0 0 - 0 - - - - + 1 + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -3820,23 +2642,22 @@ def my_form_open(dialog, layer, feature): - ../../../../../.. - - - + + + - - - + + + - - - + + + @@ -3844,9 +2665,9 @@ def my_form_open(dialog, layer, feature): - + - + ../../../../../.. @@ -3866,111 +2687,48 @@ def my_form_open(dialog, layer, feature): + + + + + + + + + + + + + + country20131022151106556 + hello20131022151106574 + + + enabled + enabled + + advanced + + 40.000000 + 40.000000 + + + 1 + 1 + + to vertex + + 40 + 1 + + to_vertex + to_vertex + + - false - - -20609693.37008669599890709 - -11055006.82298868149518967 - 20961935.60850896313786507 - 19143772.79360072687268257 - - - - false - - - 2000 - 1 - 0 - - - days - 1 - 0 - 1 - 0 - - - meters - m2 - - false - - WGS84 - - - - - true - - false - - - - points20150803121107046 - - - points20150803121107046 - - - points20150803121107046 - - - conditions unknown - Simple test app. - QGIS Server test - 90 - false - - - - - - true - D - 2 - - - - - - - EPSG:3857 - EPSG:4326 - - - dem20150730091219559 - - - 1 - 1 - 0 - 1 - 1 - - Stéphane Brunner - - - None - - - - - true - - 255 - 255 - 255 - 246 - 108 - 255 - 128 - - - 5000 - QGIS + 90 Hello_SubsetString_copy20160222085231770 Hello_copy20150804164427541 @@ -3978,57 +2736,147 @@ def my_form_open(dialog, layer, feature): hello20131022151106574 points20150803121107046 - - 1 - - enabled - enabled - - to vertex - advanced - - 1 - 1 - - - to_vertex - to_vertex - - - 40.000000 - 40.000000 - - - 40 - - country20131022151106556 - hello20131022151106574 - - - 5 + + false + + + + + 2 + true + D + + true + + + + + false + QGIS 5000 + + + + + EPSG:3857 + EPSG:4326 + + + WGS84 + + + meters + m2 + + QGIS Server test + + true + 0 + true + false + 16 + 30 + 50 + false + false + + + false + + 1 + 1 + 1 + 1 + 0 + + + false + true + + + + + + + + + dem20150730091219559 + + None + 5000 + + false + + + Stéphane Brunner + conditions unknown + + + points20150803121107046 + + + points20150803121107046 + + + points20150803121107046 + + + + EPSG:3857 + 1 + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + + + -20609693.37008669599890709 + -11055006.82298868149518967 + 20961935.60850896313786507 + 19143772.79360072687268257 + + Simple test app. + + 5 + + 1 + 2000 + 1 + 0 + 0 + + 0 + days + + 1 + + + 128 + 255 + 255 + 246 + 108 + 255 + 255 + - 255 - + 1 + + 255 true - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 1 - 3857 - EPSG:3857 - + - - - - + + + + + + @@ -4042,165 +2890,175 @@ def my_form_open(dialog, layer, feature): - - - - - - + + + + - - - - - - points20150803121107046 - hello20131022151106574 - Hello_copy20150804164427541 - Hello_SubsetString_copy20160222085231770 - Hello_Project_SubsetString_copy20160223113949592 - dem20150730091219559 - country20131022151106556 - Country_copy20161127151800736 - country20170328164317226 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + points20150803121107046 + hello20131022151106574 + Hello_copy20150804164427541 + Hello_SubsetString_copy20160222085231770 + Hello_Project_SubsetString_copy20160223113949592 + dem20150730091219559 + country20131022151106556 + Country_copy20161127151800736 + country20170328164317226 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4214,191 +3072,280 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points20150803121107046 + hello20131022151106574 + Hello_copy20150804164427541 + Hello_SubsetString_copy20160222085231770 + Hello_Project_SubsetString_copy20160223113949592 + dem20150730091219559 + country20131022151106556 + Country_copy20161127151800736 + country20170328164317226 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - points20150803121107046 - hello20131022151106574 - Hello_copy20150804164427541 - Hello_SubsetString_copy20160222085231770 - Hello_Project_SubsetString_copy20160223113949592 - dem20150730091219559 - country20131022151106556 - Country_copy20161127151800736 - country20170328164317226 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testdata/qgis_server_accesscontrol/project_groups.qgs b/tests/testdata/qgis_server_accesscontrol/project_groups.qgs new file mode 100644 index 00000000000..fe35e18923b --- /dev/null +++ b/tests/testdata/qgis_server_accesscontrol/project_groups.qgs @@ -0,0 +1,4396 @@ + + + QGIS Server Hello World + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + meters + + -30425236.72921397164463997 + -10846058.0813884325325489 + 29667752.81264735385775566 + 14979028.33329574391245842 + + 0 + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -19619892.68012013286352158 + -10327100.34232237376272678 + 19972134.91854240000247955 + 18415866.31293442100286484 + + Country_copy20161127151800736 + dbname='./helloworld.db' table="country" (geom) sql= + + + + Country_Labels + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + + + + + + + + + + + + + + . + + 0 + + + 0 + generatedlayout + + + + + + + + + + name + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + name + + + + + -14746250.07513097859919071 + -112075.42807669920148328 + 11342200.07719692215323448 + 10914413.7141284141689539 + + Hello_Project_SubsetString_copy20160223113949592 + dbname='./helloworld.db' table="hello" (geom) sql= + + + + Hello_Filter_SubsetString + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + "pkuid" + + + + + -14746250.07513097859919071 + -112075.42807669920148328 + 11342200.07719692215323448 + 10914413.7141284141689539 + + Hello_SubsetString_copy20160222085231770 + dbname='./helloworld.db' table="hello" (geom) sql="pkuid" in ( 7, 8 ) + + + + Hello_Project_SubsetString + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + "pkuid" + + + + + -14746250.07513097859919071 + -112075.42807669920148328 + 11342200.07719692215323448 + 10914413.7141284141689539 + + Hello_copy20150804164427541 + dbname='./helloworld.db' table="hello" (geom) sql= + + + + Hello_SubsetString + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + "pkuid" + + + + + -19619892.68012013286352158 + -10327100.34232237376272678 + 19972134.91854240000247955 + 18415866.31293442100286484 + + country20131022151106556 + dbname='./helloworld.db' table="country" (geom) sql= + + + + Country + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + name + + + + + + + + + + + 0 + tablayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + name + + + + + + + + + + + 0 + tablayout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + "name" + + + + + -19619892.68012013286352158 + -10327100.34232237376272678 + 19972134.91854240000247955 + 18415866.31293442100286484 + + country20170328164317226 + dbname='./helloworld.db' table="country" (geom) sql= + + + + Country_Diagrams + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + generatedlayout + + + + + + + name + + + + + -29.99999999999666755 + 29.99999999999666755 + 0.00000000000333245 + 59.99999999999666755 + + dem20150730091219559 + ./dem.tif + + + + dem + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + gdal + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + 6 + 1912 + StretchToMinimumMaximum + + + + + + + 0 + + + + -14746250.07513097859919071 + -112075.42807669920148328 + 11342200.07719692215323448 + 10914413.7141284141689539 + + hello20131022151106574 + dbname='./helloworld.db' table="hello" (geom) sql= + + + + Hello + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + + + + + + + + + + + COALESCE( "pkuid", '<NULL>' ) + + + + + 1000 + 2000 + 1000 + 2000 + + points20150803121107046 + dbname='./helloworld.db' table="points" (geom) sql= + + + + db_point + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 3857 + 3857 + EPSG:3857 + WGS 84 / Pseudo Mercator + merc + WGS84 + false + + + spatialite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../.. + + 0 + + + 0 + generatedlayout + + + + + + + "name" + + + + + + false + + -20609693.37008669599890709 + -11055006.82298868149518967 + 20961935.60850896313786507 + 19143772.79360072687268257 + + + + false + + + 2000 + 1 + 0 + + + days + 1 + 0 + 1 + 0 + + + meters + m2 + + false + + WGS84 + + + + + true + + false + + + + points20150803121107046 + + + points20150803121107046 + + + points20150803121107046 + + + conditions unknown + Simple test app. + QGIS Server test + 90 + false + + + + + + true + D + 2 + + + + + + + EPSG:3857 + EPSG:4326 + + + dem20150730091219559 + + + 1 + 1 + 0 + 1 + 1 + + Stéphane Brunner + + + + None + + + + + true + + 255 + 255 + 255 + 246 + 108 + 255 + 128 + + + 5000 + QGIS + + Hello_SubsetString_copy20160222085231770 + Hello_copy20150804164427541 + country20131022151106556 + hello20131022151106574 + points20150803121107046 + + + 1 + + enabled + enabled + + to vertex + advanced + + 1 + 1 + + + to_vertex + to_vertex + + + 40.000000 + 40.000000 + + + 40 + + country20131022151106556 + hello20131022151106574 + + + 5 + 5000 + + + 255 + + + true + + + + +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs + 1 + 3857 + EPSG:3857 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points20150803121107046 + hello20131022151106574 + Hello_copy20150804164427541 + Hello_SubsetString_copy20160222085231770 + Hello_Project_SubsetString_copy20160223113949592 + dem20150730091219559 + country20131022151106556 + Country_copy20161127151800736 + country20170328164317226 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + points20150803121107046 + hello20131022151106574 + Hello_copy20150804164427541 + Hello_SubsetString_copy20160222085231770 + Hello_Project_SubsetString_copy20160223113949592 + dem20150730091219559 + country20131022151106556 + Country_copy20161127151800736 + country20170328164317226 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +