File: test_dangling_references.py

package info (click to toggle)
pyosmium 4.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,584 kB
  • sloc: python: 4,400; cpp: 2,504; makefile: 20
file content (211 lines) | stat: -rw-r--r-- 5,644 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# SPDX-License-Identifier: BSD-2-Clause
#
# This file is part of pyosmium. (https://osmcode.org/pyosmium/)
#
# Copyright (C) 2025 Sarah Hoffmann <lonvia@denofr.de> and others.
# For a full list of authors see the git log.
from pathlib import Path

import pytest
import osmium

pytestmark = pytest.mark.iterations(1)

TEST_DIR = (Path(__file__) / '..').resolve()


class DanglingReferenceBase:
    """ Base class for tests that try to keep a reference to the object
        that was handed into the callback. We expect that the handler
        bails out with a runtime error in such a case.
    """
    node = None
    way = None
    relation = None
    area = None
    refkeeper = []

    def keep(self, obj, func):
        self.refkeeper.append((obj, func))

    def test_keep_reference(self):
        h = osmium.make_simple_handler(node=self.node, way=self.way,
                                       relation=self.relation, area=self.area)
        h.apply_file(TEST_DIR / 'example-test.osc')
        assert self.refkeeper

        for obj, func in self.refkeeper:
            with pytest.raises(RuntimeError, match="removed OSM object"):
                func(obj)
            # str() and repr() must not throw errors
            str(obj)
            repr(obj)

    def test_keep_reference_generator(self):
        for obj in osmium.FileProcessor(TEST_DIR / 'example-test.osc').with_areas():
            if obj.type_str() == 'n' and self.node is not None:
                self.node(obj)
            elif obj.type_str() == 'w' and self.way is not None:
                self.way(obj)
            elif obj.type_str() == 'r' and self.relation is not None:
                self.relation(obj)
            elif obj.type_str() == 'a' and self.area is not None:
                self.area(obj)

        assert self.refkeeper

        for obj, func in self.refkeeper:
            with pytest.raises(RuntimeError, match="removed OSM object"):
                func(obj)
            # str() and repr() must not throw errors
            str(obj)
            repr(obj)


class TestKeepNodeRef(DanglingReferenceBase):

    def node(self, n):
        self.keep(n, lambda n: n.id)


class TestKeepWayRef(DanglingReferenceBase):

    def way(self, w):
        self.keep(w, lambda n: n.id)


class TestKeepRelationRef(DanglingReferenceBase):

    def relation(self, r):
        self.keep(r, lambda n: n.id)


class TestKeepAreaRef(DanglingReferenceBase):

    def area(self, a):
        self.keep(a, lambda n: n.id)


class TestKeepNodeTagsRef(DanglingReferenceBase):

    def node(self, n):
        self.keep(n.tags, lambda t: 'foo' in t)


class TestKeepWayTagsRef(DanglingReferenceBase):

    def way(self, w):
        self.keep(w.tags, lambda t: 'foo' in t)


class TestKeepRelationTagsRef(DanglingReferenceBase):

    def relation(self, r):
        self.keep(r.tags, lambda t: 'foo' in t)


class TestKeepAreaTagsRef(DanglingReferenceBase):

    def area(self, a):
        self.keep(a.tags, lambda t: 'foo' in t)


class TestKeepTagListIterator(DanglingReferenceBase):

    def node(self, n):
        self.keep(n.tags.__iter__(), lambda t: next(t))


class TestKeepOuterRingIterator(DanglingReferenceBase):

    def area(self, r):
        self.keep(r.outer_rings(), lambda t: next(t))


class TestKeepOuterRing(DanglingReferenceBase):

    def area(self, r):
        for ring in r.outer_rings():
            self.keep(ring, lambda t: len(t))


class TestKeepInnerRingIterator(DanglingReferenceBase):

    def area(self, r):
        for ring in r.outer_rings():
            self.keep(r.inner_rings(ring), lambda t: next(t))


class TestKeepInnerRing(DanglingReferenceBase):

    def area(self, r):
        for outer in r.outer_rings():
            for inner in r.inner_rings(outer):
                self.keep(inner, lambda t: len(t))


class TestKeepRelationMemberIterator(DanglingReferenceBase):

    def relation(self, r):
        self.keep(r.members, lambda t: next(t))


class NotADanglingReferenceBase:
    """ Base class for tests that ensure that the callback does not
        bail out because of dangling references when POD types are
        kept.
    """

    node = None
    way = None
    relation = None
    area = None
    refkeeper = []

    def keep(self, obj, func):
        self.refkeeper.append((obj, func))

    def test_keep_reference(self):
        h = osmium.make_simple_handler(node=self.node, way=self.way,
                                       relation=self.relation, area=self.area)
        h.apply_file(TEST_DIR / 'example-test.pbf')
        assert self.refkeeper

        for obj, func in self.refkeeper:
            func(obj)

    def test_keep_reference_generator(self):
        for obj in osmium.FileProcessor(TEST_DIR / 'example-test.pbf').with_areas():
            if obj.is_node() and self.node is not None:
                self.node(obj)
            elif obj.is_way() and self.way is not None:
                self.way(obj)
            elif obj.is_relation() and self.relation is not None:
                self.relation(obj)
            elif obj.is_area() and self.area is not None:
                self.area(obj)

        assert self.refkeeper

        for obj, func in self.refkeeper:
            func(obj)


class TestKeepLocation(NotADanglingReferenceBase):

    def node(self, n):
        self.keep(n.location, lambda loc: loc.x)


class TestKeepNode(NotADanglingReferenceBase):

    def node(self, n):
        for t in n.tags:
            self.keep(t, lambda t: t.k)


class TestKeepMember(NotADanglingReferenceBase):

    def relation(self, r):
        for m in r.members:
            self.keep(m, lambda m: m.ref)