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