test_try_again_many_times(): use three times `None`'s ref count
[babeltrace.git] / tests / bindings / python / bt2 / test_message_iterator.py
CommitLineData
32d2d479
MJ
1#
2# Copyright (C) 2019 EfficiOS Inc.
3#
4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License
6# as published by the Free Software Foundation; only version 2
7# of the License.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17#
18
ae0bfae8 19from bt2 import value
0d0edb5e 20import collections
f6a5e476
PP
21import unittest
22import copy
23import bt2
e9e22c06 24import sys
fac7b25a 25from utils import TestOutputPortMessageIterator
f6a5e476
PP
26
27
fa4c33e3 28class UserMessageIteratorTestCase(unittest.TestCase):
f6a5e476 29 @staticmethod
692f1a01 30 def _create_graph(src_comp_cls, flt_comp_cls=None):
f6a5e476 31 class MySink(bt2._UserSinkComponent):
b20382e2 32 def __init__(self, params, obj):
f6a5e476
PP
33 self._add_input_port('in')
34
819d0ae7 35 def _user_consume(self):
fa4c33e3 36 next(self._msg_iter)
f6a5e476 37
819d0ae7 38 def _user_graph_is_configured(self):
692f1a01
PP
39 self._msg_iter = self._create_input_port_message_iterator(
40 self._input_ports['in']
41 )
f6a5e476
PP
42
43 graph = bt2.Graph()
44 src_comp = graph.add_component(src_comp_cls, 'src')
692f1a01
PP
45
46 if flt_comp_cls is not None:
47 flt_comp = graph.add_component(flt_comp_cls, 'flt')
48
f6a5e476 49 sink_comp = graph.add_component(MySink, 'sink')
692f1a01
PP
50
51 if flt_comp_cls is not None:
52 assert flt_comp is not None
53 graph.connect_ports(
54 src_comp.output_ports['out'], flt_comp.input_ports['in']
55 )
56 out_port = flt_comp.output_ports['out']
57 else:
58 out_port = src_comp.output_ports['out']
59
60 graph.connect_ports(out_port, sink_comp.input_ports['in'])
f6a5e476
PP
61 return graph
62
63 def test_init(self):
a4dcfa96
SM
64 the_output_port_from_source = None
65 the_output_port_from_iter = None
66
fa4c33e3 67 class MyIter(bt2._UserMessageIterator):
a4dcfa96 68 def __init__(self, self_port_output):
f6a5e476 69 nonlocal initialized
a4dcfa96 70 nonlocal the_output_port_from_iter
f6a5e476 71 initialized = True
a4dcfa96 72 the_output_port_from_iter = self_port_output
f6a5e476 73
61d96b89 74 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 75 def __init__(self, params, obj):
a4dcfa96 76 nonlocal the_output_port_from_source
03ec9ebd 77 the_output_port_from_source = self._add_output_port('out', 'user data')
f6a5e476
PP
78
79 initialized = False
80 graph = self._create_graph(MySource)
a4dcfa96 81 graph.run()
f6a5e476 82 self.assertTrue(initialized)
61d96b89
FD
83 self.assertEqual(
84 the_output_port_from_source.addr, the_output_port_from_iter.addr
85 )
03ec9ebd 86 self.assertEqual(the_output_port_from_iter.user_data, 'user data')
f6a5e476 87
692f1a01
PP
88 def test_create_from_message_iterator(self):
89 class MySourceIter(bt2._UserMessageIterator):
90 def __init__(self, self_port_output):
91 nonlocal src_iter_initialized
92 src_iter_initialized = True
93
94 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
b20382e2 95 def __init__(self, params, obj):
692f1a01
PP
96 self._add_output_port('out')
97
98 class MyFilterIter(bt2._UserMessageIterator):
99 def __init__(self, self_port_output):
100 nonlocal flt_iter_initialized
101 flt_iter_initialized = True
102 self._up_iter = self._create_input_port_message_iterator(
103 self._component._input_ports['in']
104 )
105
106 def __next__(self):
107 return next(self._up_iter)
108
109 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
b20382e2 110 def __init__(self, params, obj):
692f1a01
PP
111 self._add_input_port('in')
112 self._add_output_port('out')
113
114 src_iter_initialized = False
115 flt_iter_initialized = False
116 graph = self._create_graph(MySource, MyFilter)
117 graph.run()
118 self.assertTrue(src_iter_initialized)
119 self.assertTrue(flt_iter_initialized)
120
f6a5e476 121 def test_finalize(self):
fa4c33e3 122 class MyIter(bt2._UserMessageIterator):
819d0ae7 123 def _user_finalize(self):
f6a5e476
PP
124 nonlocal finalized
125 finalized = True
126
61d96b89 127 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 128 def __init__(self, params, obj):
f6a5e476
PP
129 self._add_output_port('out')
130
131 finalized = False
132 graph = self._create_graph(MySource)
a4dcfa96 133 graph.run()
f6a5e476
PP
134 del graph
135 self.assertTrue(finalized)
136
137 def test_component(self):
fa4c33e3 138 class MyIter(bt2._UserMessageIterator):
a4dcfa96 139 def __init__(self, self_port_output):
f6a5e476
PP
140 nonlocal salut
141 salut = self._component._salut
142
61d96b89 143 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 144 def __init__(self, params, obj):
f6a5e476
PP
145 self._add_output_port('out')
146 self._salut = 23
147
148 salut = None
149 graph = self._create_graph(MySource)
a4dcfa96 150 graph.run()
f6a5e476
PP
151 self.assertEqual(salut, 23)
152
153 def test_addr(self):
fa4c33e3 154 class MyIter(bt2._UserMessageIterator):
a4dcfa96 155 def __init__(self, self_port_output):
f6a5e476
PP
156 nonlocal addr
157 addr = self.addr
158
61d96b89 159 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 160 def __init__(self, params, obj):
f6a5e476
PP
161 self._add_output_port('out')
162
163 addr = None
164 graph = self._create_graph(MySource)
a4dcfa96 165 graph.run()
f6a5e476
PP
166 self.assertIsNotNone(addr)
167 self.assertNotEqual(addr, 0)
168
4e853135
SM
169 # Test that messages returned by _UserMessageIterator.__next__ remain valid
170 # and can be re-used.
171 def test_reuse_message(self):
172 class MyIter(bt2._UserMessageIterator):
173 def __init__(self, port):
174 tc, sc, ec = port.user_data
175 trace = tc()
176 stream = trace.create_stream(sc)
177 packet = stream.create_packet()
178
179 # This message will be returned twice by __next__.
180 event_message = self._create_event_message(ec, packet)
181
182 self._msgs = [
183 self._create_stream_beginning_message(stream),
4e853135
SM
184 self._create_packet_beginning_message(packet),
185 event_message,
186 event_message,
187 ]
188
189 def __next__(self):
190 return self._msgs.pop(0)
191
192 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 193 def __init__(self, params, obj):
4e853135 194 tc = self._create_trace_class()
37a93d41 195 sc = tc.create_stream_class(supports_packets=True)
4e853135
SM
196 ec = sc.create_event_class()
197 self._add_output_port('out', (tc, sc, ec))
198
199 graph = bt2.Graph()
200 src = graph.add_component(MySource, 'src')
fac7b25a 201 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
4e853135
SM
202
203 # Skip beginning messages.
b7cbc799 204 msg = next(it)
c946c9de 205 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
b7cbc799 206 msg = next(it)
c946c9de 207 self.assertIsInstance(msg, bt2._PacketBeginningMessage)
4e853135
SM
208
209 msg_ev1 = next(it)
210 msg_ev2 = next(it)
211
c946c9de
PP
212 self.assertIsInstance(msg_ev1, bt2._EventMessage)
213 self.assertIsInstance(msg_ev2, bt2._EventMessage)
4e853135
SM
214 self.assertEqual(msg_ev1.addr, msg_ev2.addr)
215
39ddfa44 216 @staticmethod
fac7b25a 217 def _setup_seek_beginning_test(sink_cls):
39ddfa44
SM
218 # Use a source, a filter and an output port iterator. This allows us
219 # to test calling `seek_beginning` on both a _OutputPortMessageIterator
220 # and a _UserComponentInputPortMessageIterator, on top of checking that
221 # _UserMessageIterator._seek_beginning is properly called.
222
223 class MySourceIter(bt2._UserMessageIterator):
224 def __init__(self, port):
225 tc, sc, ec = port.user_data
226 trace = tc()
227 stream = trace.create_stream(sc)
228 packet = stream.create_packet()
229
230 self._msgs = [
231 self._create_stream_beginning_message(stream),
39ddfa44
SM
232 self._create_packet_beginning_message(packet),
233 self._create_event_message(ec, packet),
234 self._create_event_message(ec, packet),
235 self._create_packet_end_message(packet),
39ddfa44
SM
236 self._create_stream_end_message(stream),
237 ]
238 self._at = 0
239
819d0ae7 240 def _user_seek_beginning(self):
39ddfa44
SM
241 self._at = 0
242
243 def __next__(self):
244 if self._at < len(self._msgs):
245 msg = self._msgs[self._at]
246 self._at += 1
247 return msg
248 else:
249 raise StopIteration
250
61d96b89 251 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
b20382e2 252 def __init__(self, params, obj):
39ddfa44 253 tc = self._create_trace_class()
37a93d41 254 sc = tc.create_stream_class(supports_packets=True)
39ddfa44
SM
255 ec = sc.create_event_class()
256
257 self._add_output_port('out', (tc, sc, ec))
258
259 class MyFilterIter(bt2._UserMessageIterator):
260 def __init__(self, port):
261 input_port = port.user_data
692f1a01
PP
262 self._upstream_iter = self._create_input_port_message_iterator(
263 input_port
264 )
39ddfa44
SM
265
266 def __next__(self):
267 return next(self._upstream_iter)
268
819d0ae7 269 def _user_seek_beginning(self):
39ddfa44
SM
270 self._upstream_iter.seek_beginning()
271
272 @property
819d0ae7 273 def _user_can_seek_beginning(self):
39ddfa44
SM
274 return self._upstream_iter.can_seek_beginning
275
276 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
b20382e2 277 def __init__(self, params, obj):
39ddfa44
SM
278 input_port = self._add_input_port('in')
279 self._add_output_port('out', input_port)
280
39ddfa44
SM
281 graph = bt2.Graph()
282 src = graph.add_component(MySource, 'src')
283 flt = graph.add_component(MyFilter, 'flt')
fac7b25a 284 sink = graph.add_component(sink_cls, 'sink')
39ddfa44 285 graph.connect_ports(src.output_ports['out'], flt.input_ports['in'])
fac7b25a
PP
286 graph.connect_ports(flt.output_ports['out'], sink.input_ports['in'])
287 return MySourceIter, graph
39ddfa44
SM
288
289 def test_can_seek_beginning(self):
fac7b25a
PP
290 class MySink(bt2._UserSinkComponent):
291 def __init__(self, params, obj):
292 self._add_input_port('in')
293
294 def _user_graph_is_configured(self):
295 self._msg_iter = self._create_input_port_message_iterator(
296 self._input_ports['in']
297 )
298
299 def _user_consume(self):
300 nonlocal can_seek_beginning
301 can_seek_beginning = self._msg_iter.can_seek_beginning
302
303 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
39ddfa44 304
819d0ae7 305 def _user_can_seek_beginning(self):
fac7b25a
PP
306 nonlocal input_port_iter_can_seek_beginning
307 return input_port_iter_can_seek_beginning
39ddfa44 308
819d0ae7 309 MySourceIter._user_can_seek_beginning = property(_user_can_seek_beginning)
39ddfa44 310
fac7b25a
PP
311 input_port_iter_can_seek_beginning = True
312 can_seek_beginning = None
313 graph.run_once()
314 self.assertTrue(can_seek_beginning)
39ddfa44 315
fac7b25a
PP
316 input_port_iter_can_seek_beginning = False
317 can_seek_beginning = None
318 graph.run_once()
319 self.assertFalse(can_seek_beginning)
39ddfa44
SM
320
321 # Once can_seek_beginning returns an error, verify that it raises when
322 # _can_seek_beginning has/returns the wrong type.
323
324 # Remove the _can_seek_beginning method, we now rely on the presence of
325 # a _seek_beginning method to know whether the iterator can seek to
326 # beginning or not.
819d0ae7 327 del MySourceIter._user_can_seek_beginning
fac7b25a
PP
328 can_seek_beginning = None
329 graph.run_once()
330 self.assertTrue(can_seek_beginning)
39ddfa44 331
819d0ae7 332 del MySourceIter._user_seek_beginning
fac7b25a
PP
333 can_seek_beginning = None
334 graph.run_once()
335 self.assertFalse(can_seek_beginning)
39ddfa44
SM
336
337 def test_seek_beginning(self):
fac7b25a
PP
338 class MySink(bt2._UserSinkComponent):
339 def __init__(self, params, obj):
340 self._add_input_port('in')
39ddfa44 341
fac7b25a
PP
342 def _user_graph_is_configured(self):
343 self._msg_iter = self._create_input_port_message_iterator(
344 self._input_ports['in']
345 )
346
347 def _user_consume(self):
348 nonlocal do_seek_beginning
349 nonlocal msg
350
351 if do_seek_beginning:
352 self._msg_iter.seek_beginning()
353 return
354
355 msg = next(self._msg_iter)
356
357 do_seek_beginning = False
358 msg = None
359 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
360 graph.run_once()
c946c9de 361 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
fac7b25a 362 graph.run_once()
c946c9de 363 self.assertIsInstance(msg, bt2._PacketBeginningMessage)
fac7b25a
PP
364 do_seek_beginning = True
365 graph.run_once()
366 do_seek_beginning = False
367 graph.run_once()
368 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
39ddfa44 369
fac7b25a
PP
370 def test_seek_beginning_user_error(self):
371 class MySink(bt2._UserSinkComponent):
372 def __init__(self, params, obj):
373 self._add_input_port('in')
39ddfa44 374
fac7b25a
PP
375 def _user_graph_is_configured(self):
376 self._msg_iter = self._create_input_port_message_iterator(
377 self._input_ports['in']
378 )
39ddfa44 379
fac7b25a
PP
380 def _user_consume(self):
381 self._msg_iter.seek_beginning()
39ddfa44 382
fac7b25a 383 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
39ddfa44 384
819d0ae7 385 def _user_seek_beginning_error(self):
61d96b89 386 raise ValueError('ouch')
39ddfa44 387
819d0ae7 388 MySourceIter._user_seek_beginning = _user_seek_beginning_error
39ddfa44 389
614743a5 390 with self.assertRaises(bt2._Error):
fac7b25a 391 graph.run_once()
39ddfa44 392
752f4edf
SM
393 # Try consuming many times from an iterator that always returns TryAgain.
394 # This verifies that we are not missing an incref of Py_None, making the
395 # refcount of Py_None reach 0.
396 def test_try_again_many_times(self):
397 class MyIter(bt2._UserMessageIterator):
398 def __next__(self):
399 raise bt2.TryAgain
400
401 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
b20382e2 402 def __init__(self, params, obj):
752f4edf
SM
403 self._add_output_port('out')
404
405 graph = bt2.Graph()
406 src = graph.add_component(MySource, 'src')
fac7b25a 407 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
752f4edf 408
e9e22c06
PP
409 # Three times the initial ref count of `None` iterations should
410 # be enough to catch the bug even if there are small differences
752f4edf 411 # between configurations.
e9e22c06
PP
412 none_ref_count = sys.getrefcount(None) * 3
413
414 for i in range(none_ref_count):
752f4edf
SM
415 with self.assertRaises(bt2.TryAgain):
416 next(it)
417
39ddfa44 418
39ddfa44
SM
419if __name__ == '__main__':
420 unittest.main()
This page took 0.057606 seconds and 4 git commands to generate.