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