qgis-profile-interpreter

qgis plugin for placing 3D points along elevation profiles
git clone git://src.adamsgaard.dk/qgis-profile-interpreter # fast
git clone https://src.adamsgaard.dk/qgis-profile-interpreter.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit 2e04edc4bd5cd1a7db9fad42c0aadb47eeef89c3
parent 59fb2af11208c12eb4b0d3a93427fa564ec1eab5
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Fri,  5 Jun 2026 21:39:52 +0300

feat: check addFeatures result; warn and return on failure (A3+B6+T4)

Diffstat:
Mprofile_interpreter/profile_interpreter.py | 9++++++++-
Mtest/fakeqgis.py | 7++++++-
Mtest/test_profile_interpreter.py | 37++++++++++++++++++++++++++++++++++++-
3 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/profile_interpreter/profile_interpreter.py b/profile_interpreter/profile_interpreter.py @@ -215,7 +215,14 @@ class ProfileInterpreterPlugin: for k, v in attrs.items(): feature[k] = v - layer.dataProvider().addFeatures([feature]) + ok, _ = layer.dataProvider().addFeatures([feature]) + if not ok: + self.iface.messageBar().pushMessage( + MENU, + 'Failed to add feature to layer.', + level=Qgis.Warning, + ) + return layer.updateExtents() layer.triggerRepaint() diff --git a/test/fakeqgis.py b/test/fakeqgis.py @@ -217,6 +217,8 @@ class QgsVectorDataProvider: class _FakeDataProvider: + _fail_next = False # set True in a test to simulate addFeatures failure + def __init__(self, caps, features_store, fields_ref): self._caps = caps self._features = features_store @@ -226,8 +228,11 @@ class _FakeDataProvider: return self._caps def addFeatures(self, features): + if _FakeDataProvider._fail_next: + _FakeDataProvider._fail_next = False + return (False, []) self._features.extend(features) - return True + return (True, list(features)) def addAttributes(self, attrs): self._fields_ref.extend(attrs) diff --git a/test/test_profile_interpreter.py b/test/test_profile_interpreter.py @@ -76,10 +76,19 @@ class _FakeBar: pass +class _TrackingBar: + def __init__(self): + self.messages = [] + + def pushMessage(self, tag, msg, level=None, **kwargs): + self.messages.append((tag, msg, level)) + + class _FakeIface: def __init__(self, active_layer=None, canvases=None): self._active_layer = active_layer self._window = _FakeMainWindow(canvases or []) + self._bar = _FakeBar() def activeLayer(self): return self._active_layer @@ -88,7 +97,7 @@ class _FakeIface: return self._window def messageBar(self): - return _FakeBar() + return self._bar def addPluginToMenu(self, *args): pass @@ -399,5 +408,31 @@ class TestFindProfileCanvas(unittest.TestCase): self.assertIsNone(plugin._find_profile_canvas()) +# ── addFeatures failure ─────────────────────────────────────────────────── + +class TestOnPickAddFeaturesFailure(unittest.TestCase): + def setUp(self): + fq.QgsProject._instance = None + fq.QgsGeometry._next_interpolate_empty = False + fq.QgsCoordinateTransform.reset() + fq._FakeDataProvider._fail_next = False + + def test_failure_shows_warning_and_adds_no_features(self): + plugin, iface = _make_plugin(active_layer=None) + bar = _TrackingBar() + iface._bar = bar + + fq._FakeDataProvider._fail_next = True + canvas = plugin._canvas + plugin._on_pick(_FakeEvent()) + + # feature was not committed + self.assertEqual(len(plugin._layer._features), 0) + # a warning was pushed + self.assertTrue(any(level == fq.Qgis.Warning for _, _, level in bar.messages)) + # canvas was not refreshed + self.assertEqual(canvas.refresh_count, 0) + + if __name__ == '__main__': unittest.main()