fix: test_message_iterator.py hangs on Python 3.12
[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
f6a5e476 19import unittest
f6a5e476 20import bt2
e9e22c06 21import sys
fac7b25a 22from utils import TestOutputPortMessageIterator
3f9a359f 23from bt2 import port as bt2_port
9415de1c 24from bt2 import message_iterator as bt2_message_iterator
f6a5e476
PP
25
26
6ef4e30a
SM
27class SimpleSink(bt2._UserSinkComponent):
28 # Straightforward sink that creates one input port (`in`) and consumes from
29 # it.
f6a5e476 30
e3250e61 31 def __init__(self, config, params, obj):
6ef4e30a 32 self._add_input_port('in')
f6a5e476 33
6ef4e30a
SM
34 def _user_consume(self):
35 next(self._msg_iter)
f6a5e476 36
6ef4e30a 37 def _user_graph_is_configured(self):
fbd8a4e0 38 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
692f1a01 39
692f1a01 40
6ef4e30a
SM
41def _create_graph(src_comp_cls, sink_comp_cls, flt_comp_cls=None):
42 graph = bt2.Graph()
692f1a01 43
6ef4e30a
SM
44 src_comp = graph.add_component(src_comp_cls, 'src')
45 sink_comp = graph.add_component(sink_comp_cls, 'sink')
692f1a01 46
6ef4e30a
SM
47 if flt_comp_cls is not None:
48 flt_comp = graph.add_component(flt_comp_cls, 'flt')
49 graph.connect_ports(src_comp.output_ports['out'], flt_comp.input_ports['in'])
50 graph.connect_ports(flt_comp.output_ports['out'], sink_comp.input_ports['in'])
51 else:
52 graph.connect_ports(src_comp.output_ports['out'], sink_comp.input_ports['in'])
f6a5e476 53
6ef4e30a
SM
54 return graph
55
56
57class UserMessageIteratorTestCase(unittest.TestCase):
f6a5e476 58 def test_init(self):
a4dcfa96
SM
59 the_output_port_from_source = None
60 the_output_port_from_iter = None
61
fa4c33e3 62 class MyIter(bt2._UserMessageIterator):
9415de1c 63 def __init__(self, config, self_port_output):
f6a5e476 64 nonlocal initialized
a4dcfa96 65 nonlocal the_output_port_from_iter
f6a5e476 66 initialized = True
a4dcfa96 67 the_output_port_from_iter = self_port_output
f6a5e476 68
61d96b89 69 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 70 def __init__(self, config, params, obj):
a4dcfa96 71 nonlocal the_output_port_from_source
03ec9ebd 72 the_output_port_from_source = self._add_output_port('out', 'user data')
f6a5e476
PP
73
74 initialized = False
6ef4e30a 75 graph = _create_graph(MySource, SimpleSink)
a4dcfa96 76 graph.run()
f6a5e476 77 self.assertTrue(initialized)
61d96b89
FD
78 self.assertEqual(
79 the_output_port_from_source.addr, the_output_port_from_iter.addr
80 )
03ec9ebd 81 self.assertEqual(the_output_port_from_iter.user_data, 'user data')
f6a5e476 82
692f1a01
PP
83 def test_create_from_message_iterator(self):
84 class MySourceIter(bt2._UserMessageIterator):
9415de1c 85 def __init__(self, config, self_port_output):
692f1a01
PP
86 nonlocal src_iter_initialized
87 src_iter_initialized = True
88
89 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
e3250e61 90 def __init__(self, config, params, obj):
692f1a01
PP
91 self._add_output_port('out')
92
93 class MyFilterIter(bt2._UserMessageIterator):
9415de1c 94 def __init__(self, config, self_port_output):
692f1a01
PP
95 nonlocal flt_iter_initialized
96 flt_iter_initialized = True
fbd8a4e0 97 self._up_iter = self._create_message_iterator(
692f1a01
PP
98 self._component._input_ports['in']
99 )
100
101 def __next__(self):
102 return next(self._up_iter)
103
104 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
e3250e61 105 def __init__(self, config, params, obj):
692f1a01
PP
106 self._add_input_port('in')
107 self._add_output_port('out')
108
109 src_iter_initialized = False
110 flt_iter_initialized = False
6ef4e30a 111 graph = _create_graph(MySource, SimpleSink, MyFilter)
692f1a01
PP
112 graph.run()
113 self.assertTrue(src_iter_initialized)
114 self.assertTrue(flt_iter_initialized)
115
ab8b2b1b
SM
116 def test_create_user_error(self):
117 # This tests both error handling by
fbd8a4e0
PP
118 # _UserSinkComponent._create_message_iterator
119 # and _UserMessageIterator._create_message_iterator, as they
ab8b2b1b
SM
120 # are both used in the graph.
121 class MySourceIter(bt2._UserMessageIterator):
9415de1c 122 def __init__(self, config, self_port_output):
ab8b2b1b
SM
123 raise ValueError('Very bad error')
124
125 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
e3250e61 126 def __init__(self, config, params, obj):
ab8b2b1b
SM
127 self._add_output_port('out')
128
129 class MyFilterIter(bt2._UserMessageIterator):
9415de1c 130 def __init__(self, config, self_port_output):
ab8b2b1b
SM
131 # This is expected to raise because of the error in
132 # MySourceIter.__init__.
fbd8a4e0 133 self._create_message_iterator(self._component._input_ports['in'])
ab8b2b1b
SM
134
135 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
e3250e61 136 def __init__(self, config, params, obj):
ab8b2b1b
SM
137 self._add_input_port('in')
138 self._add_output_port('out')
139
6ef4e30a 140 graph = _create_graph(MySource, SimpleSink, MyFilter)
ab8b2b1b
SM
141
142 with self.assertRaises(bt2._Error) as ctx:
143 graph.run()
144
145 exc = ctx.exception
146 cause = exc[0]
147
148 self.assertIsInstance(cause, bt2._MessageIteratorErrorCause)
149 self.assertEqual(cause.component_name, 'src')
150 self.assertEqual(cause.component_output_port_name, 'out')
151 self.assertIn('ValueError: Very bad error', cause.message)
152
f6a5e476 153 def test_finalize(self):
fa4c33e3 154 class MyIter(bt2._UserMessageIterator):
819d0ae7 155 def _user_finalize(self):
f6a5e476
PP
156 nonlocal finalized
157 finalized = True
158
61d96b89 159 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 160 def __init__(self, config, params, obj):
f6a5e476
PP
161 self._add_output_port('out')
162
163 finalized = False
6ef4e30a 164 graph = _create_graph(MySource, SimpleSink)
a4dcfa96 165 graph.run()
f6a5e476
PP
166 del graph
167 self.assertTrue(finalized)
168
9415de1c
SM
169 def test_config_parameter(self):
170 class MyIter(bt2._UserMessageIterator):
171 def __init__(self, config, port):
172 nonlocal config_type
173 config_type = type(config)
174
175 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
176 def __init__(self, config, params, obj):
177 self._add_output_port('out')
178
179 config_type = None
180 graph = _create_graph(MySource, SimpleSink)
181 graph.run()
182 self.assertIs(config_type, bt2_message_iterator._MessageIteratorConfiguration)
183
184 def _test_config_can_seek_forward(self, set_can_seek_forward):
185 class MyIter(bt2._UserMessageIterator):
186 def __init__(self, config, port):
187 if set_can_seek_forward:
188 config.can_seek_forward = True
189
190 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
191 def __init__(self, config, params, obj):
192 self._add_output_port('out')
193
194 class MySink(bt2._UserSinkComponent):
195 def __init__(self, config, params, obj):
196 self._add_input_port('in')
197
198 def _user_graph_is_configured(self):
fbd8a4e0 199 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
9415de1c
SM
200
201 def _user_consume(self):
202 nonlocal can_seek_forward
203 can_seek_forward = self._msg_iter.can_seek_forward
204
205 can_seek_forward = None
206 graph = _create_graph(MySource, MySink)
207 graph.run_once()
208 self.assertIs(can_seek_forward, set_can_seek_forward)
209
210 def test_config_can_seek_forward_default(self):
211 self._test_config_can_seek_forward(False)
212
213 def test_config_can_seek_forward(self):
214 self._test_config_can_seek_forward(True)
215
216 def test_config_can_seek_forward_wrong_type(self):
217 class MyIter(bt2._UserMessageIterator):
218 def __init__(self, config, port):
219 config.can_seek_forward = 1
220
221 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
222 def __init__(self, config, params, obj):
223 self._add_output_port('out')
224
225 graph = _create_graph(MySource, SimpleSink)
226 with self.assertRaises(bt2._Error) as ctx:
227 graph.run()
228
229 root_cause = ctx.exception[0]
230 self.assertIn("TypeError: 'int' is not a 'bool' object", root_cause.message)
231
f6a5e476 232 def test_component(self):
fa4c33e3 233 class MyIter(bt2._UserMessageIterator):
9415de1c 234 def __init__(self, config, self_port_output):
f6a5e476
PP
235 nonlocal salut
236 salut = self._component._salut
237
61d96b89 238 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 239 def __init__(self, config, params, obj):
f6a5e476
PP
240 self._add_output_port('out')
241 self._salut = 23
242
243 salut = None
6ef4e30a 244 graph = _create_graph(MySource, SimpleSink)
a4dcfa96 245 graph.run()
f6a5e476
PP
246 self.assertEqual(salut, 23)
247
3f9a359f
SM
248 def test_port(self):
249 class MyIter(bt2._UserMessageIterator):
9415de1c 250 def __init__(self_iter, config, self_port_output):
3f9a359f
SM
251 nonlocal called
252 called = True
253 port = self_iter._port
254 self.assertIs(type(self_port_output), bt2_port._UserComponentOutputPort)
255 self.assertIs(type(port), bt2_port._UserComponentOutputPort)
256 self.assertEqual(self_port_output.addr, port.addr)
257
258 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 259 def __init__(self, config, params, obj):
3f9a359f
SM
260 self._add_output_port('out')
261
262 called = False
6ef4e30a 263 graph = _create_graph(MySource, SimpleSink)
3f9a359f
SM
264 graph.run()
265 self.assertTrue(called)
266
f6a5e476 267 def test_addr(self):
fa4c33e3 268 class MyIter(bt2._UserMessageIterator):
9415de1c 269 def __init__(self, config, self_port_output):
f6a5e476
PP
270 nonlocal addr
271 addr = self.addr
272
61d96b89 273 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 274 def __init__(self, config, params, obj):
f6a5e476
PP
275 self._add_output_port('out')
276
277 addr = None
6ef4e30a 278 graph = _create_graph(MySource, SimpleSink)
a4dcfa96 279 graph.run()
f6a5e476
PP
280 self.assertIsNotNone(addr)
281 self.assertNotEqual(addr, 0)
282
4e853135
SM
283 # Test that messages returned by _UserMessageIterator.__next__ remain valid
284 # and can be re-used.
285 def test_reuse_message(self):
286 class MyIter(bt2._UserMessageIterator):
9415de1c 287 def __init__(self, config, port):
4e853135
SM
288 tc, sc, ec = port.user_data
289 trace = tc()
290 stream = trace.create_stream(sc)
291 packet = stream.create_packet()
292
293 # This message will be returned twice by __next__.
294 event_message = self._create_event_message(ec, packet)
295
296 self._msgs = [
297 self._create_stream_beginning_message(stream),
4e853135
SM
298 self._create_packet_beginning_message(packet),
299 event_message,
300 event_message,
301 ]
302
303 def __next__(self):
304 return self._msgs.pop(0)
305
306 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 307 def __init__(self, config, params, obj):
4e853135 308 tc = self._create_trace_class()
37a93d41 309 sc = tc.create_stream_class(supports_packets=True)
4e853135
SM
310 ec = sc.create_event_class()
311 self._add_output_port('out', (tc, sc, ec))
312
313 graph = bt2.Graph()
314 src = graph.add_component(MySource, 'src')
fac7b25a 315 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
4e853135
SM
316
317 # Skip beginning messages.
b7cbc799 318 msg = next(it)
9cbe0c59 319 self.assertIs(type(msg), bt2._StreamBeginningMessageConst)
b7cbc799 320 msg = next(it)
9cbe0c59 321 self.assertIs(type(msg), bt2._PacketBeginningMessageConst)
4e853135
SM
322
323 msg_ev1 = next(it)
324 msg_ev2 = next(it)
325
9cbe0c59
FD
326 self.assertIs(type(msg_ev1), bt2._EventMessageConst)
327 self.assertIs(type(msg_ev2), bt2._EventMessageConst)
4e853135
SM
328 self.assertEqual(msg_ev1.addr, msg_ev2.addr)
329
6ef4e30a
SM
330 # Try consuming many times from an iterator that always returns TryAgain.
331 # This verifies that we are not missing an incref of Py_None, making the
332 # refcount of Py_None reach 0.
333 def test_try_again_many_times(self):
da110483
MJ
334 # Starting with Python 3.12, `None` is immortal: its reference
335 # count operations are no-op. Skip this test in that case.
336 before = sys.getrefcount(None)
337 dummy = None # noqa: F841
338
339 if before == sys.getrefcount(None):
340 raise unittest.SkipTest("`None` is immortal")
341
6ef4e30a 342 class MyIter(bt2._UserMessageIterator):
39ddfa44 343 def __next__(self):
6ef4e30a 344 raise bt2.TryAgain
39ddfa44 345
6ef4e30a 346 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
e3250e61 347 def __init__(self, config, params, obj):
6ef4e30a 348 self._add_output_port('out')
39ddfa44
SM
349
350 class MyFilterIter(bt2._UserMessageIterator):
351 def __init__(self, port):
352 input_port = port.user_data
fbd8a4e0 353 self._upstream_iter = self._create_message_iterator(input_port)
39ddfa44
SM
354
355 def __next__(self):
356 return next(self._upstream_iter)
357
819d0ae7 358 def _user_seek_beginning(self):
39ddfa44
SM
359 self._upstream_iter.seek_beginning()
360
819d0ae7 361 def _user_can_seek_beginning(self):
5c836b95 362 return self._upstream_iter.can_seek_beginning()
39ddfa44
SM
363
364 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
e3250e61 365 def __init__(self, config, params, obj):
39ddfa44
SM
366 input_port = self._add_input_port('in')
367 self._add_output_port('out', input_port)
368
39ddfa44
SM
369 graph = bt2.Graph()
370 src = graph.add_component(MySource, 'src')
6ef4e30a
SM
371 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
372
373 # Three times the initial ref count of `None` iterations should
374 # be enough to catch the bug even if there are small differences
375 # between configurations.
376 none_ref_count = sys.getrefcount(None) * 3
377
378 for i in range(none_ref_count):
379 with self.assertRaises(bt2.TryAgain):
380 next(it)
381
ec60bdaa
SM
382 def test_error_in_iterator_with_cycle_after_having_created_upstream_iterator(self):
383 # Test a failure that triggered an abort in libbabeltrace2, in this situation:
384 #
385 # - The filter iterator creates an upstream iterator.
386 # - The filter iterator creates a reference cycle, including itself.
387 # - An exception is raised, causing the filter iterator's
388 # initialization method to fail.
389 class MySourceIter(bt2._UserMessageIterator):
390 pass
391
392 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
393 def __init__(self, config, params, obj):
394 self._add_output_port('out')
395
396 class MyFilterIter(bt2._UserMessageIterator):
397 def __init__(self, config, port):
398 # First, create an upstream iterator.
fbd8a4e0 399 self._upstream_iter = self._create_message_iterator(
ec60bdaa
SM
400 self._component._input_ports['in']
401 )
402
403 # Then, voluntarily make a reference cycle that will keep this
404 # Python object alive, which will keep the upstream iterator
405 # Babeltrace object alive.
406 self._self = self
407
408 # Finally, raise an exception to make __init__ fail.
409 raise ValueError('woops')
410
411 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
412 def __init__(self, config, params, obj):
413 self._in = self._add_input_port('in')
414 self._out = self._add_output_port('out')
415
416 class MySink(bt2._UserSinkComponent):
417 def __init__(self, config, params, obj):
418 self._input_port = self._add_input_port('in')
419
420 def _user_graph_is_configured(self):
fbd8a4e0 421 self._upstream_iter = self._create_message_iterator(self._input_port)
ec60bdaa
SM
422
423 def _user_consume(self):
424 # We should not reach this.
425 assert False
426
427 g = bt2.Graph()
428 src = g.add_component(MySource, 'src')
429 flt = g.add_component(MyFilter, 'flt')
430 snk = g.add_component(MySink, 'snk')
431 g.connect_ports(src.output_ports['out'], flt.input_ports['in'])
432 g.connect_ports(flt.output_ports['out'], snk.input_ports['in'])
433
434 with self.assertRaisesRegex(bt2._Error, 'ValueError: woops'):
435 g.run()
436
6ef4e30a 437
24a68958
SM
438def _setup_seek_test(
439 sink_cls,
440 user_seek_beginning=None,
441 user_can_seek_beginning=None,
442 user_seek_ns_from_origin=None,
443 user_can_seek_ns_from_origin=None,
c49bf79b 444 can_seek_forward=False,
24a68958 445):
6ef4e30a 446 class MySourceIter(bt2._UserMessageIterator):
9415de1c 447 def __init__(self, config, port):
6ef4e30a
SM
448 tc, sc, ec = port.user_data
449 trace = tc()
450 stream = trace.create_stream(sc)
451 packet = stream.create_packet()
452
453 self._msgs = [
454 self._create_stream_beginning_message(stream),
455 self._create_packet_beginning_message(packet),
456 self._create_event_message(ec, packet),
457 self._create_event_message(ec, packet),
458 self._create_packet_end_message(packet),
459 self._create_stream_end_message(stream),
460 ]
461 self._at = 0
c49bf79b 462 config.can_seek_forward = can_seek_forward
6ef4e30a
SM
463
464 def __next__(self):
465 if self._at < len(self._msgs):
466 msg = self._msgs[self._at]
467 self._at += 1
468 return msg
469 else:
470 raise StopIteration
471
472 if user_seek_beginning is not None:
473 MySourceIter._user_seek_beginning = user_seek_beginning
474
475 if user_can_seek_beginning is not None:
5c836b95 476 MySourceIter._user_can_seek_beginning = user_can_seek_beginning
6ef4e30a 477
24a68958
SM
478 if user_seek_ns_from_origin is not None:
479 MySourceIter._user_seek_ns_from_origin = user_seek_ns_from_origin
480
481 if user_can_seek_ns_from_origin is not None:
482 MySourceIter._user_can_seek_ns_from_origin = user_can_seek_ns_from_origin
483
6ef4e30a 484 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
e3250e61 485 def __init__(self, config, params, obj):
6ef4e30a
SM
486 tc = self._create_trace_class()
487 sc = tc.create_stream_class(supports_packets=True)
488 ec = sc.create_event_class()
489
490 self._add_output_port('out', (tc, sc, ec))
491
492 class MyFilterIter(bt2._UserMessageIterator):
9415de1c 493 def __init__(self, config, port):
fbd8a4e0 494 self._upstream_iter = self._create_message_iterator(
6ef4e30a
SM
495 self._component._input_ports['in']
496 )
c49bf79b 497 config.can_seek_forward = self._upstream_iter.can_seek_forward
6ef4e30a
SM
498
499 def __next__(self):
500 return next(self._upstream_iter)
501
6ef4e30a 502 def _user_can_seek_beginning(self):
5c836b95 503 return self._upstream_iter.can_seek_beginning()
6ef4e30a
SM
504
505 def _user_seek_beginning(self):
506 self._upstream_iter.seek_beginning()
507
24a68958
SM
508 def _user_can_seek_ns_from_origin(self, ns_from_origin):
509 return self._upstream_iter.can_seek_ns_from_origin(ns_from_origin)
510
511 def _user_seek_ns_from_origin(self, ns_from_origin):
512 self._upstream_iter.seek_ns_from_origin(ns_from_origin)
513
6ef4e30a 514 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
e3250e61 515 def __init__(self, config, params, obj):
6ef4e30a
SM
516 self._add_input_port('in')
517 self._add_output_port('out')
518
519 return _create_graph(MySource, sink_cls, flt_comp_cls=MyFilter)
39ddfa44 520
6ef4e30a
SM
521
522class UserMessageIteratorSeekBeginningTestCase(unittest.TestCase):
7f8e969d
SM
523 def test_can_seek_beginning_without_seek_beginning(self):
524 with self.assertRaisesRegex(
525 bt2._IncompleteUserClass,
526 "cannot create component class 'MySource': message iterator class implements _user_can_seek_beginning but not _user_seek_beginning",
527 ):
528 _setup_seek_test(SimpleSink, user_can_seek_beginning=lambda: None)
529
39ddfa44 530 def test_can_seek_beginning(self):
fac7b25a 531 class MySink(bt2._UserSinkComponent):
e3250e61 532 def __init__(self, config, params, obj):
fac7b25a
PP
533 self._add_input_port('in')
534
535 def _user_graph_is_configured(self):
fbd8a4e0 536 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
fac7b25a
PP
537
538 def _user_consume(self):
539 nonlocal can_seek_beginning
5c836b95 540 can_seek_beginning = self._msg_iter.can_seek_beginning()
fac7b25a 541
819d0ae7 542 def _user_can_seek_beginning(self):
fac7b25a
PP
543 nonlocal input_port_iter_can_seek_beginning
544 return input_port_iter_can_seek_beginning
39ddfa44 545
6ef4e30a 546 graph = _setup_seek_test(
7f8e969d
SM
547 MySink,
548 user_can_seek_beginning=_user_can_seek_beginning,
549 user_seek_beginning=lambda: None,
6ef4e30a 550 )
39ddfa44 551
fac7b25a
PP
552 input_port_iter_can_seek_beginning = True
553 can_seek_beginning = None
554 graph.run_once()
a564cdf4 555 self.assertIs(can_seek_beginning, True)
39ddfa44 556
fac7b25a
PP
557 input_port_iter_can_seek_beginning = False
558 can_seek_beginning = None
559 graph.run_once()
a564cdf4 560 self.assertIs(can_seek_beginning, False)
39ddfa44 561
6ef4e30a
SM
562 def test_no_can_seek_beginning_with_seek_beginning(self):
563 # Test an iterator without a _user_can_seek_beginning method, but with
564 # a _user_seek_beginning method.
565 class MySink(bt2._UserSinkComponent):
e3250e61 566 def __init__(self, config, params, obj):
6ef4e30a
SM
567 self._add_input_port('in')
568
569 def _user_graph_is_configured(self):
fbd8a4e0 570 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
6ef4e30a
SM
571
572 def _user_consume(self):
573 nonlocal can_seek_beginning
5c836b95 574 can_seek_beginning = self._msg_iter.can_seek_beginning()
6ef4e30a
SM
575
576 def _user_seek_beginning(self):
577 pass
39ddfa44 578
6ef4e30a 579 graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning)
fac7b25a
PP
580 can_seek_beginning = None
581 graph.run_once()
a564cdf4 582 self.assertIs(can_seek_beginning, True)
39ddfa44 583
6ef4e30a
SM
584 def test_no_can_seek_beginning(self):
585 # Test an iterator without a _user_can_seek_beginning method, without
586 # a _user_seek_beginning method.
587 class MySink(bt2._UserSinkComponent):
e3250e61 588 def __init__(self, config, params, obj):
6ef4e30a
SM
589 self._add_input_port('in')
590
591 def _user_graph_is_configured(self):
fbd8a4e0 592 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
6ef4e30a
SM
593
594 def _user_consume(self):
595 nonlocal can_seek_beginning
5c836b95 596 can_seek_beginning = self._msg_iter.can_seek_beginning()
6ef4e30a
SM
597
598 graph = _setup_seek_test(MySink)
fac7b25a
PP
599 can_seek_beginning = None
600 graph.run_once()
a564cdf4 601 self.assertIs(can_seek_beginning, False)
39ddfa44 602
9e8e8b43
SM
603 def test_can_seek_beginning_user_error(self):
604 class MySink(bt2._UserSinkComponent):
e3250e61 605 def __init__(self, config, params, obj):
9e8e8b43
SM
606 self._add_input_port('in')
607
608 def _user_graph_is_configured(self):
fbd8a4e0 609 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
9e8e8b43
SM
610
611 def _user_consume(self):
612 # This is expected to raise.
5c836b95 613 self._msg_iter.can_seek_beginning()
9e8e8b43
SM
614
615 def _user_can_seek_beginning(self):
616 raise ValueError('moustiquaire')
617
618 graph = _setup_seek_test(
7f8e969d
SM
619 MySink,
620 user_can_seek_beginning=_user_can_seek_beginning,
621 user_seek_beginning=lambda: None,
9e8e8b43
SM
622 )
623
624 with self.assertRaises(bt2._Error) as ctx:
625 graph.run_once()
626
627 cause = ctx.exception[0]
628 self.assertIn('ValueError: moustiquaire', cause.message)
629
630 def test_can_seek_beginning_wrong_return_value(self):
631 class MySink(bt2._UserSinkComponent):
e3250e61 632 def __init__(self, config, params, obj):
9e8e8b43
SM
633 self._add_input_port('in')
634
635 def _user_graph_is_configured(self):
fbd8a4e0 636 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
9e8e8b43
SM
637
638 def _user_consume(self):
639 # This is expected to raise.
5c836b95 640 self._msg_iter.can_seek_beginning()
9e8e8b43
SM
641
642 def _user_can_seek_beginning(self):
643 return 'Amqui'
644
645 graph = _setup_seek_test(
7f8e969d
SM
646 MySink,
647 user_can_seek_beginning=_user_can_seek_beginning,
648 user_seek_beginning=lambda: None,
9e8e8b43
SM
649 )
650
651 with self.assertRaises(bt2._Error) as ctx:
652 graph.run_once()
653
654 cause = ctx.exception[0]
655 self.assertIn("TypeError: 'str' is not a 'bool' object", cause.message)
656
39ddfa44 657 def test_seek_beginning(self):
fac7b25a 658 class MySink(bt2._UserSinkComponent):
e3250e61 659 def __init__(self, config, params, obj):
fac7b25a 660 self._add_input_port('in')
39ddfa44 661
fac7b25a 662 def _user_graph_is_configured(self):
fbd8a4e0 663 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
fac7b25a
PP
664
665 def _user_consume(self):
666 nonlocal do_seek_beginning
667 nonlocal msg
668
669 if do_seek_beginning:
670 self._msg_iter.seek_beginning()
671 return
672
673 msg = next(self._msg_iter)
674
6ef4e30a
SM
675 def _user_seek_beginning(self):
676 self._at = 0
677
fac7b25a 678 msg = None
6ef4e30a
SM
679 graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning)
680
681 # Consume message.
682 do_seek_beginning = False
fac7b25a 683 graph.run_once()
9cbe0c59 684 self.assertIs(type(msg), bt2._StreamBeginningMessageConst)
6ef4e30a
SM
685
686 # Consume message.
fac7b25a 687 graph.run_once()
9cbe0c59 688 self.assertIs(type(msg), bt2._PacketBeginningMessageConst)
6ef4e30a
SM
689
690 # Seek beginning.
fac7b25a
PP
691 do_seek_beginning = True
692 graph.run_once()
6ef4e30a
SM
693
694 # Consume message.
fac7b25a
PP
695 do_seek_beginning = False
696 graph.run_once()
9cbe0c59 697 self.assertIs(type(msg), bt2._StreamBeginningMessageConst)
39ddfa44 698
fac7b25a
PP
699 def test_seek_beginning_user_error(self):
700 class MySink(bt2._UserSinkComponent):
e3250e61 701 def __init__(self, config, params, obj):
fac7b25a 702 self._add_input_port('in')
39ddfa44 703
fac7b25a 704 def _user_graph_is_configured(self):
fbd8a4e0 705 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
39ddfa44 706
fac7b25a
PP
707 def _user_consume(self):
708 self._msg_iter.seek_beginning()
39ddfa44 709
6ef4e30a 710 def _user_seek_beginning(self):
61d96b89 711 raise ValueError('ouch')
39ddfa44 712
6ef4e30a 713 graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning)
39ddfa44 714
614743a5 715 with self.assertRaises(bt2._Error):
fac7b25a 716 graph.run_once()
39ddfa44
SM
717
718
24a68958 719class UserMessageIteratorSeekNsFromOriginTestCase(unittest.TestCase):
7f8e969d
SM
720 def test_can_seek_ns_from_origin_without_seek_ns_from_origin(self):
721 # Test the case where:
722 #
723 # - can_seek_ns_from_origin: Returns True (don't really care, as long
724 # as it's provided)
725 # - seek_ns_from_origin provided: No
726 # - can the iterator seek beginning: Don't care
727 # - can the iterator seek forward: Don't care
728 for can_seek_ns_from_origin in (False, True):
729 for iter_can_seek_beginning in (False, True):
730 for iter_can_seek_forward in (False, True):
731 with self.assertRaisesRegex(
732 bt2._IncompleteUserClass,
733 "cannot create component class 'MySource': message iterator class implements _user_can_seek_ns_from_origin but not _user_seek_ns_from_origin",
734 ):
735 self._can_seek_ns_from_origin_test(
736 None,
737 user_can_seek_ns_from_origin_ret_val=True,
738 user_seek_ns_from_origin_provided=False,
739 iter_can_seek_beginning=iter_can_seek_beginning,
740 iter_can_seek_forward=iter_can_seek_forward,
741 )
742
c49bf79b
SM
743 def test_can_seek_ns_from_origin_returns_true(self):
744 # Test the case where:
745 #
746 # - can_seek_ns_from_origin: returns True
7f8e969d 747 # - seek_ns_from_origin provided: Yes
c49bf79b
SM
748 # - can the iterator seek beginning: Don't care
749 # - can the iterator seek forward: Don't care
750 #
751 # We expect iter.can_seek_ns_from_origin to return True.
7f8e969d
SM
752 for iter_can_seek_beginning in (False, True):
753 for iter_can_seek_forward in (False, True):
754 self._can_seek_ns_from_origin_test(
755 expected_outcome=True,
756 user_can_seek_ns_from_origin_ret_val=True,
757 user_seek_ns_from_origin_provided=True,
758 iter_can_seek_beginning=iter_can_seek_beginning,
759 iter_can_seek_forward=iter_can_seek_forward,
760 )
c49bf79b
SM
761
762 def test_can_seek_ns_from_origin_returns_false_can_seek_beginning_forward_seekable(
c1491996 763 self,
c49bf79b
SM
764 ):
765 # Test the case where:
766 #
767 # - can_seek_ns_from_origin: returns False
7f8e969d 768 # - seek_ns_from_origin provided: Yes
c49bf79b
SM
769 # - can the iterator seek beginning: Yes
770 # - can the iterator seek forward: Yes
771 #
772 # We expect iter.can_seek_ns_from_origin to return True.
7f8e969d
SM
773 self._can_seek_ns_from_origin_test(
774 expected_outcome=True,
775 user_can_seek_ns_from_origin_ret_val=False,
776 user_seek_ns_from_origin_provided=True,
777 iter_can_seek_beginning=True,
778 iter_can_seek_forward=True,
779 )
24a68958 780
c49bf79b 781 def test_can_seek_ns_from_origin_returns_false_can_seek_beginning_not_forward_seekable(
c1491996 782 self,
c49bf79b
SM
783 ):
784 # Test the case where:
785 #
786 # - can_seek_ns_from_origin: returns False
7f8e969d 787 # - seek_ns_from_origin provided: Yes
c49bf79b
SM
788 # - can the iterator seek beginning: Yes
789 # - can the iterator seek forward: No
790 #
791 # We expect iter.can_seek_ns_from_origin to return False.
7f8e969d
SM
792 self._can_seek_ns_from_origin_test(
793 expected_outcome=False,
794 user_can_seek_ns_from_origin_ret_val=False,
795 user_seek_ns_from_origin_provided=True,
796 iter_can_seek_beginning=True,
797 iter_can_seek_forward=False,
798 )
24a68958 799
c49bf79b 800 def test_can_seek_ns_from_origin_returns_false_cant_seek_beginning_forward_seekable(
c1491996 801 self,
c49bf79b
SM
802 ):
803 # Test the case where:
804 #
805 # - can_seek_ns_from_origin: returns False
7f8e969d 806 # - seek_ns_from_origin provided: Yes
c49bf79b
SM
807 # - can the iterator seek beginning: No
808 # - can the iterator seek forward: Yes
809 #
810 # We expect iter.can_seek_ns_from_origin to return False.
c49bf79b
SM
811 self._can_seek_ns_from_origin_test(
812 expected_outcome=False,
813 user_can_seek_ns_from_origin_ret_val=False,
7f8e969d 814 user_seek_ns_from_origin_provided=True,
c49bf79b
SM
815 iter_can_seek_beginning=False,
816 iter_can_seek_forward=True,
817 )
818
819 def test_can_seek_ns_from_origin_returns_false_cant_seek_beginning_not_forward_seekable(
c1491996 820 self,
c49bf79b
SM
821 ):
822 # Test the case where:
823 #
824 # - can_seek_ns_from_origin: returns False
7f8e969d 825 # - seek_ns_from_origin provided: Yes
c49bf79b
SM
826 # - can the iterator seek beginning: No
827 # - can the iterator seek forward: No
828 #
829 # We expect iter.can_seek_ns_from_origin to return False.
7f8e969d
SM
830 self._can_seek_ns_from_origin_test(
831 expected_outcome=False,
832 user_can_seek_ns_from_origin_ret_val=False,
833 user_seek_ns_from_origin_provided=True,
834 iter_can_seek_beginning=False,
835 iter_can_seek_forward=False,
836 )
c49bf79b
SM
837
838 def test_no_can_seek_ns_from_origin_seek_ns_from_origin(self):
839 # Test the case where:
840 #
841 # - can_seek_ns_from_origin: Not provided
842 # - seek_ns_from_origin provided: Yes
843 # - can the iterator seek beginning: Don't care
844 # - can the iterator seek forward: Don't care
845 #
846 # We expect iter.can_seek_ns_from_origin to return True.
847 for iter_can_seek_beginning in (False, True):
848 for iter_can_seek_forward in (False, True):
849 self._can_seek_ns_from_origin_test(
850 expected_outcome=True,
851 user_can_seek_ns_from_origin_ret_val=None,
852 user_seek_ns_from_origin_provided=True,
853 iter_can_seek_beginning=iter_can_seek_beginning,
854 iter_can_seek_forward=iter_can_seek_forward,
24a68958
SM
855 )
856
c49bf79b 857 def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_can_seek_beginning_forward_seekable(
c1491996 858 self,
c49bf79b
SM
859 ):
860 # Test the case where:
861 #
862 # - can_seek_ns_from_origin: Not provided
863 # - seek_ns_from_origin provided: Not provided
864 # - can the iterator seek beginning: Yes
865 # - can the iterator seek forward: Yes
866 #
867 # We expect iter.can_seek_ns_from_origin to return True.
868 self._can_seek_ns_from_origin_test(
869 expected_outcome=True,
870 user_can_seek_ns_from_origin_ret_val=None,
871 user_seek_ns_from_origin_provided=False,
872 iter_can_seek_beginning=True,
873 iter_can_seek_forward=True,
874 )
24a68958 875
c49bf79b 876 def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_can_seek_beginning_not_forward_seekable(
c1491996 877 self,
c49bf79b
SM
878 ):
879 # Test the case where:
880 #
881 # - can_seek_ns_from_origin: Not provided
882 # - seek_ns_from_origin provided: Not provided
883 # - can the iterator seek beginning: Yes
884 # - can the iterator seek forward: No
885 #
886 # We expect iter.can_seek_ns_from_origin to return False.
887 self._can_seek_ns_from_origin_test(
888 expected_outcome=False,
889 user_can_seek_ns_from_origin_ret_val=None,
890 user_seek_ns_from_origin_provided=False,
891 iter_can_seek_beginning=True,
892 iter_can_seek_forward=False,
24a68958
SM
893 )
894
c49bf79b 895 def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_cant_seek_beginning_forward_seekable(
c1491996 896 self,
c49bf79b
SM
897 ):
898 # Test the case where:
899 #
900 # - can_seek_ns_from_origin: Not provided
901 # - seek_ns_from_origin provided: Not provided
902 # - can the iterator seek beginning: No
903 # - can the iterator seek forward: Yes
904 #
905 # We expect iter.can_seek_ns_from_origin to return False.
906 self._can_seek_ns_from_origin_test(
907 expected_outcome=False,
908 user_can_seek_ns_from_origin_ret_val=None,
909 user_seek_ns_from_origin_provided=False,
910 iter_can_seek_beginning=False,
911 iter_can_seek_forward=True,
912 )
24a68958 913
c49bf79b 914 def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_cant_seek_beginning_not_forward_seekable(
c1491996 915 self,
c49bf79b
SM
916 ):
917 # Test the case where:
918 #
919 # - can_seek_ns_from_origin: Not provided
920 # - seek_ns_from_origin provided: Not provided
921 # - can the iterator seek beginning: No
922 # - can the iterator seek forward: No
923 #
924 # We expect iter.can_seek_ns_from_origin to return False.
925 self._can_seek_ns_from_origin_test(
926 expected_outcome=False,
927 user_can_seek_ns_from_origin_ret_val=None,
928 user_seek_ns_from_origin_provided=False,
929 iter_can_seek_beginning=False,
930 iter_can_seek_forward=False,
931 )
24a68958 932
c49bf79b
SM
933 def _can_seek_ns_from_origin_test(
934 self,
935 expected_outcome,
936 user_can_seek_ns_from_origin_ret_val,
937 user_seek_ns_from_origin_provided,
938 iter_can_seek_beginning,
939 iter_can_seek_forward,
940 ):
24a68958 941 class MySink(bt2._UserSinkComponent):
e3250e61 942 def __init__(self, config, params, obj):
24a68958
SM
943 self._add_input_port('in')
944
945 def _user_graph_is_configured(self):
fbd8a4e0 946 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
24a68958
SM
947
948 def _user_consume(self):
949 nonlocal can_seek_ns_from_origin
24a68958 950 can_seek_ns_from_origin = self._msg_iter.can_seek_ns_from_origin(
c49bf79b 951 passed_ns_from_origin
24a68958
SM
952 )
953
c49bf79b 954 if user_can_seek_ns_from_origin_ret_val is not None:
24a68958 955
c49bf79b
SM
956 def user_can_seek_ns_from_origin(self, ns_from_origin):
957 nonlocal received_ns_from_origin
958 received_ns_from_origin = ns_from_origin
959 return user_can_seek_ns_from_origin_ret_val
24a68958 960
c49bf79b
SM
961 else:
962 user_can_seek_ns_from_origin = None
24a68958 963
c49bf79b 964 if user_seek_ns_from_origin_provided:
24a68958 965
c49bf79b
SM
966 def user_seek_ns_from_origin(self, ns_from_origin):
967 pass
24a68958 968
c49bf79b
SM
969 else:
970 user_seek_ns_from_origin = None
24a68958 971
c49bf79b 972 if iter_can_seek_beginning:
24a68958 973
c49bf79b
SM
974 def user_seek_beginning(self):
975 pass
24a68958 976
c49bf79b
SM
977 else:
978 user_seek_beginning = None
24a68958 979
c49bf79b
SM
980 graph = _setup_seek_test(
981 MySink,
982 user_can_seek_ns_from_origin=user_can_seek_ns_from_origin,
983 user_seek_ns_from_origin=user_seek_ns_from_origin,
984 user_seek_beginning=user_seek_beginning,
985 can_seek_forward=iter_can_seek_forward,
986 )
24a68958 987
c49bf79b
SM
988 passed_ns_from_origin = 77
989 received_ns_from_origin = None
24a68958 990 can_seek_ns_from_origin = None
24a68958 991 graph.run_once()
c49bf79b
SM
992 self.assertIs(can_seek_ns_from_origin, expected_outcome)
993
994 if user_can_seek_ns_from_origin_ret_val is not None:
995 self.assertEqual(received_ns_from_origin, passed_ns_from_origin)
24a68958
SM
996
997 def test_can_seek_ns_from_origin_user_error(self):
998 class MySink(bt2._UserSinkComponent):
e3250e61 999 def __init__(self, config, params, obj):
24a68958
SM
1000 self._add_input_port('in')
1001
1002 def _user_graph_is_configured(self):
fbd8a4e0 1003 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
24a68958
SM
1004
1005 def _user_consume(self):
1006 # This is expected to raise.
1007 self._msg_iter.can_seek_ns_from_origin(2)
1008
1009 def _user_can_seek_ns_from_origin(self, ns_from_origin):
1010 raise ValueError('Joutel')
1011
1012 graph = _setup_seek_test(
7f8e969d
SM
1013 MySink,
1014 user_can_seek_ns_from_origin=_user_can_seek_ns_from_origin,
1015 user_seek_ns_from_origin=lambda: None,
24a68958
SM
1016 )
1017
1018 with self.assertRaises(bt2._Error) as ctx:
1019 graph.run_once()
1020
1021 cause = ctx.exception[0]
1022 self.assertIn('ValueError: Joutel', cause.message)
1023
1024 def test_can_seek_ns_from_origin_wrong_return_value(self):
1025 class MySink(bt2._UserSinkComponent):
e3250e61 1026 def __init__(self, config, params, obj):
24a68958
SM
1027 self._add_input_port('in')
1028
1029 def _user_graph_is_configured(self):
fbd8a4e0 1030 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
24a68958
SM
1031
1032 def _user_consume(self):
1033 # This is expected to raise.
1034 self._msg_iter.can_seek_ns_from_origin(2)
1035
1036 def _user_can_seek_ns_from_origin(self, ns_from_origin):
1037 return 'Nitchequon'
1038
1039 graph = _setup_seek_test(
7f8e969d
SM
1040 MySink,
1041 user_can_seek_ns_from_origin=_user_can_seek_ns_from_origin,
1042 user_seek_ns_from_origin=lambda: None,
24a68958
SM
1043 )
1044
1045 with self.assertRaises(bt2._Error) as ctx:
1046 graph.run_once()
1047
1048 cause = ctx.exception[0]
1049 self.assertIn("TypeError: 'str' is not a 'bool' object", cause.message)
1050
1051 def test_seek_ns_from_origin(self):
1052 class MySink(bt2._UserSinkComponent):
e3250e61 1053 def __init__(self, config, params, obj):
24a68958
SM
1054 self._add_input_port('in')
1055
1056 def _user_graph_is_configured(self):
fbd8a4e0 1057 self._msg_iter = self._create_message_iterator(self._input_ports['in'])
24a68958
SM
1058
1059 def _user_consume(self):
1060 self._msg_iter.seek_ns_from_origin(17)
1061
1062 def _user_seek_ns_from_origin(self, ns_from_origin):
1063 nonlocal actual_ns_from_origin
1064 actual_ns_from_origin = ns_from_origin
1065
24a68958
SM
1066 graph = _setup_seek_test(
1067 MySink, user_seek_ns_from_origin=_user_seek_ns_from_origin
1068 )
1069
1070 actual_ns_from_origin = None
1071 graph.run_once()
1072 self.assertEqual(actual_ns_from_origin, 17)
1073
1074
39ddfa44
SM
1075if __name__ == '__main__':
1076 unittest.main()
This page took 0.100464 seconds and 4 git commands to generate.