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