From e7f11a2083ca49d473e86325050662b902bfecec Mon Sep 17 00:00:00 2001 From: joonalai <33314057+Joonalai@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:22:00 +0200 Subject: [PATCH 1/4] fix: use self.search_layer_ids again --- CHANGELOG.md | 2 + pickLayer/core/set_active_layer_tool.py | 5 ++- test/unit/test_set_active_layer_tool.py | 59 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a8662e..805e2f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Fix defining layers to search from for set active layer tool + ## [3.9.9] - 2023-11-01 - Find most logical closest feature from nested features with set active layer tool diff --git a/pickLayer/core/set_active_layer_tool.py b/pickLayer/core/set_active_layer_tool.py index f7c26c0..062eda1 100644 --- a/pickLayer/core/set_active_layer_tool.py +++ b/pickLayer/core/set_active_layer_tool.py @@ -100,9 +100,10 @@ def set_active_layer_using_closest_feature( def _get_identify_results( self, location: QgsPointXY, search_layer_ids: Optional[list[str]] = None ) -> list[QgsMapToolIdentify.IdentifyResult]: + layer_ids = search_layer_ids or self.search_layer_ids or [] layers = [] - if search_layer_ids: - for layer_id in search_layer_ids: + if layer_ids: + for layer_id in layer_ids: layer = QgsProject.instance().mapLayer(layer_id) if isinstance(layer, QgsVectorLayer): layers.append(layer) diff --git a/test/unit/test_set_active_layer_tool.py b/test/unit/test_set_active_layer_tool.py index b5efcc8..43dc696 100644 --- a/test/unit/test_set_active_layer_tool.py +++ b/test/unit/test_set_active_layer_tool.py @@ -139,6 +139,65 @@ def test_set_active_layer_using_closest_feature( assert len(identify_results) == expected_num_results +@pytest.mark.parametrize( + argnames=("search_layers", "expected_num_results", "search_layer_count"), + argvalues=[ + ([0], 2, 1), + ([0, 1], 2, 1), + ([0, 1], 4, 2), + ([0, 1, 2], 2, 1), + ([0, 1, 2], 4, 2), + ([0, 1, 2], 6, 3), + ], + ids=[ + "2-features-from-one-layer-found", + "2-features-from-two-layers-found", + "4-features-from-two-layers-found", + "2-features-from-three-layers-found", + "4-features-from-three-layers-found", + "6-features-from-three-layers-found", + ], +) +def test_set_active_layer_using_closest_feature_with_search_layer_ids( + map_tool, + test_layers, + search_layers, + expected_num_results, + search_layer_count, + mocker, +): + map_tool.search_layer_ids = [test_layers[i].id() for i in range(search_layer_count)] + + layer_ids = [] + + for index in search_layers: + layer_ids.append(test_layers[index].id()) + + m_choose_layer_from_identify_results = mocker.patch.object( + map_tool, + "_choose_layer_from_identify_results", + return_value=None, + autospec=True, + ) + + map_tool.set_active_layer_using_closest_feature(MOUSE_LOCATION, search_radius=2.5) + + identify_results = m_choose_layer_from_identify_results.call_args.args[0] + assert len(identify_results) == expected_num_results + + # check that identify results are in same order + # as requested layer ids (result may contain multiple + # features from one layer) + identify_results_layer_ids = [] + previous_id = "" + for result in identify_results: + if result.mLayer.id() != previous_id: + identify_results_layer_ids.append(result.mLayer.id()) + previous_id = result.mLayer.id() + + assert identify_results_layer_ids == layer_ids[:search_layer_count] + + @pytest.mark.parametrize( argnames=( "search_layers", From 5aeecc0e312855b5a13cb17d1d077c9bffbb5361 Mon Sep 17 00:00:00 2001 From: joonalai <33314057+Joonalai@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:22:08 +0200 Subject: [PATCH 2/4] test: use real identify in tests --- test/unit/test_set_active_layer_tool.py | 67 ++++++++++++------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/test/unit/test_set_active_layer_tool.py b/test/unit/test_set_active_layer_tool.py index 43dc696..24b2db7 100644 --- a/test/unit/test_set_active_layer_tool.py +++ b/test/unit/test_set_active_layer_tool.py @@ -30,10 +30,11 @@ QgsPointXY, QgsProject, QgsRasterLayer, + QgsRectangle, QgsVectorLayer, QgsVectorLayerUtils, ) -from qgis.gui import QgsMapTool, QgsMapToolIdentify +from qgis.gui import QgsMapTool from qgis_plugin_tools.tools.resources import plugin_test_data_path from pickLayer.core.set_active_layer_tool import SetActiveLayerTool @@ -42,11 +43,9 @@ MOUSE_LOCATION = QgsPointXY(0, 0) -def create_identify_result( +def create_identify_layers( identified_feature_geom_wtks: list[tuple[str, str, str]] -) -> list[QgsMapToolIdentify.IdentifyResult]: - results = [] - +) -> None: for wkt, crs, layer_name in identified_feature_geom_wtks: geometry = QgsGeometry.fromWkt(wkt) layer = QgsMemoryProviderUtils.createMemoryLayer( @@ -57,22 +56,15 @@ def create_identify_result( ) feature = QgsVectorLayerUtils.createFeature(layer, geometry, {}) layer.dataProvider().addFeature(feature) - - # using the actual QgsMapToolIdentify.IdentifyResult causes - # fatal exceptions, mock probably is sufficient for testing - results.append( - MagicMock(**{"mLayer": layer, "mFeature": feature}) # noqa: PIE804 - ) - - return results + QgsProject.instance().addMapLayer(layer) @pytest.fixture() -def map_tool(qgis_iface): +def map_tool(qgis_iface, qgis_new_project): return SetActiveLayerTool(qgis_iface.mapCanvas()) -@pytest.fixture(scope="session") +@pytest.fixture() def test_layers(): layers = [ QgsVectorLayer("PointZ", "point_layer", "memory"), @@ -342,7 +334,7 @@ def test_preferred_type_chosen_from_different_types( mocker: MockerFixture, qgis_iface: QgisInterface, ): - results = create_identify_result( + create_identify_layers( [ ("POINT(3 3)", "EPSG:3067", "point"), ("LINESTRING(4 4, 5 5)", "EPSG:3067", "line"), @@ -352,8 +344,6 @@ def test_preferred_type_chosen_from_different_types( QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - mocker.patch.object(map_tool, "identify", return_value=results) - m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None ) @@ -372,7 +362,7 @@ def test_closest_of_same_type_chosen( mocker: MockerFixture, qgis_iface: QgisInterface, ): - results = create_identify_result( + create_identify_layers( [ ("POINT(3 3)", "EPSG:3067", "point-mid"), ("POINT(2 2)", "EPSG:3067", "point-close"), @@ -383,8 +373,6 @@ def test_closest_of_same_type_chosen( QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - mocker.patch.object(map_tool, "identify", return_value=results) - m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None ) @@ -404,19 +392,32 @@ def test_closest_of_same_type_chosen_even_if_project_and_layer_crs_differs( qgis_iface: QgisInterface, qgis_new_project, ): - results = create_identify_result( + Settings.search_radius.set(2) + create_identify_layers( [ # points close to 250000,6700000 in 3067 in different crs's - ("POINT(22.46220271 60.35440884)", "EPSG:4326", "point-far"), - ("POINT(2415468 6694753)", "EPSG:2392", "point-mid"), - ("POINT(2501177 8479351)", "EPSG:3857", "point-close"), + ( + "POINT(22.46220271 60.35440884)", + "EPSG:4326", + "point-far", + ), + ( + "POINT(2415468 6694753)", + "EPSG:2392", + "point-mid", + ), + ( + "POINT(2501177 8479351)", + "EPSG:3857", + "point-close", + ), ] ) QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - - mocker.patch.object(map_tool, "identify", return_value=results) + map_tool.canvas().setExtent(QgsRectangle(249900, 6699000, 250100, 6701000)) + map_tool.canvas().refreshAllLayers() m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None @@ -437,7 +438,7 @@ def test_line_crossing_origin_chosen_as_closest( qgis_iface: QgisInterface, qgis_new_project, ): - results = create_identify_result( + create_identify_layers( [ ("LINESTRING(1.1 1.1, 2 2)", "EPSG:3067", "line-not-crossing"), ("LINESTRING(0 2, 2 0)", "EPSG:3067", "line-crossing"), @@ -447,8 +448,6 @@ def test_line_crossing_origin_chosen_as_closest( QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - mocker.patch.object(map_tool, "identify", return_value=results) - m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None ) @@ -468,7 +467,7 @@ def test_top_polygon_chosen_from_multiple_nested_even_if_top_not_closest( qgis_iface: QgisInterface, qgis_new_project, ): - results = create_identify_result( + create_identify_layers( [ ("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", "EPSG:3067", "polygon-0-10"), ("POLYGON((0 -5, 0 5, 5 5, 5 -5, 0 -5))", "EPSG:3067", "polygon-5-5"), @@ -478,8 +477,6 @@ def test_top_polygon_chosen_from_multiple_nested_even_if_top_not_closest( QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - mocker.patch.object(map_tool, "identify", return_value=results) - m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None ) @@ -500,7 +497,7 @@ def test_bottom_polygon_chosen_from_multiple_nested_when_bottom_closest( qgis_iface: QgisInterface, qgis_new_project, ): - results = create_identify_result( + create_identify_layers( [ ("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", "EPSG:3067", "polygon-0-10"), ("POLYGON((3 3, 3 5, 5 5, 5 3, 3 3))", "EPSG:3067", "polygon-3-5"), @@ -510,8 +507,6 @@ def test_bottom_polygon_chosen_from_multiple_nested_when_bottom_closest( QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067")) map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067")) - mocker.patch.object(map_tool, "identify", return_value=results) - m_set_active_layer = mocker.patch.object( qgis_iface, "setActiveLayer", return_value=None ) From ea256ae0a986b59b516f9ccd7f56e9085974d59d Mon Sep 17 00:00:00 2001 From: joonalai <33314057+Joonalai@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:22:12 +0200 Subject: [PATCH 3/4] chore: update pytest-qgis --- requirements-dev.in | 2 +- requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev.in b/requirements-dev.in index 6ceb430..3f68117 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -5,7 +5,7 @@ pytest==6.2.5 pytest-qt==3.3.0 pytest-mock==3.7.0 pytest-cov==2.12.1 -pytest-qgis==1.3.1 +pytest-qgis==1.3.5 #stubs pyqt5-stubs==5.15.6.0 diff --git a/requirements-dev.txt b/requirements-dev.txt index 6b58d0f..fff51aa 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -85,7 +85,7 @@ pytest-cov==2.12.1 # via -r requirements-dev.in pytest-mock==3.7.0 # via -r requirements-dev.in -pytest-qgis==1.3.1 +pytest-qgis==1.3.5 # via -r requirements-dev.in pytest-qt==3.3.0 # via -r requirements-dev.in From d244cb280ffc854050dabe98ee1e9e178b52b66d Mon Sep 17 00:00:00 2001 From: joonalai <33314057+Joonalai@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:22:18 +0200 Subject: [PATCH 4/4] chore: adjust ruff default autofix behaviour --- .pre-commit-config.yaml | 2 +- pyproject.toml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 163e090..b17bdb4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: rev: v0.0.292 hooks: - id: ruff - args: [--fix, --exit-non-zero-on-fix] + args: [--fix, --exit-non-zero-on-fix, --extend-fixable=F401] - repo: https://github.com/psf/black rev: 23.7.0 hooks: diff --git a/pyproject.toml b/pyproject.toml index 69db86f..f2dc864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,6 +4,11 @@ ignore = [ ] line-length = 88 +unfixable = [ + "F401", # Unused imports + "F841", # Unused variables +] + # List of all rules https://docs.astral.sh/ruff/rules/ select = [ "ANN", # flake8-annotations