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 | 104 | # Test that creating a message iterator from a sink component on a |
e7401568 | 105 | # non-connected input port raises. |
415d43a1 SM |
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 | ||
e7401568 SM |
127 | # Test that creating a message iterator from a message iterator on a |
128 | # non-connected input port raises. | |
415d43a1 SM |
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): | |
a3788aca SM |
383 | # Starting with Python 3.12, `None` is immortal: its reference |
384 | # count operations are no-op. Skip this test in that case. | |
385 | before = sys.getrefcount(None) | |
386 | dummy = None # noqa: F841 | |
387 | ||
388 | if before == sys.getrefcount(None): | |
389 | raise unittest.SkipTest("`None` is immortal") | |
390 | ||
0a6d7302 | 391 | class MyIter(bt2._UserMessageIterator): |
f00b8d40 | 392 | def __next__(self): |
0a6d7302 | 393 | raise bt2.TryAgain |
f00b8d40 | 394 | |
0a6d7302 | 395 | class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter): |
59225a3e | 396 | def __init__(self, config, params, obj): |
f5567ea8 | 397 | self._add_output_port("out") |
f00b8d40 | 398 | |
f00b8d40 | 399 | graph = bt2.Graph() |
f5567ea8 FD |
400 | src = graph.add_component(MySource, "src") |
401 | it = TestOutputPortMessageIterator(graph, src.output_ports["out"]) | |
0a6d7302 SM |
402 | |
403 | # Three times the initial ref count of `None` iterations should | |
404 | # be enough to catch the bug even if there are small differences | |
405 | # between configurations. | |
406 | none_ref_count = sys.getrefcount(None) * 3 | |
407 | ||
408 | for i in range(none_ref_count): | |
409 | with self.assertRaises(bt2.TryAgain): | |
410 | next(it) | |
411 | ||
fca28f75 SM |
412 | def test_error_in_iterator_with_cycle_after_having_created_upstream_iterator(self): |
413 | # Test a failure that triggered an abort in libbabeltrace2, in this situation: | |
414 | # | |
415 | # - The filter iterator creates an upstream iterator. | |
416 | # - The filter iterator creates a reference cycle, including itself. | |
417 | # - An exception is raised, causing the filter iterator's | |
418 | # initialization method to fail. | |
419 | class MySourceIter(bt2._UserMessageIterator): | |
420 | pass | |
421 | ||
422 | class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter): | |
423 | def __init__(self, config, params, obj): | |
f5567ea8 | 424 | self._add_output_port("out") |
fca28f75 SM |
425 | |
426 | class MyFilterIter(bt2._UserMessageIterator): | |
427 | def __init__(self, config, port): | |
428 | # First, create an upstream iterator. | |
9a2c8b8e | 429 | self._upstream_iter = self._create_message_iterator( |
f5567ea8 | 430 | self._component._input_ports["in"] |
fca28f75 SM |
431 | ) |
432 | ||
433 | # Then, voluntarily make a reference cycle that will keep this | |
434 | # Python object alive, which will keep the upstream iterator | |
435 | # Babeltrace object alive. | |
436 | self._self = self | |
437 | ||
438 | # Finally, raise an exception to make __init__ fail. | |
f5567ea8 | 439 | raise ValueError("woops") |
fca28f75 SM |
440 | |
441 | class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter): | |
442 | def __init__(self, config, params, obj): | |
f5567ea8 FD |
443 | self._in = self._add_input_port("in") |
444 | self._out = self._add_output_port("out") | |
fca28f75 SM |
445 | |
446 | class MySink(bt2._UserSinkComponent): | |
447 | def __init__(self, config, params, obj): | |
f5567ea8 | 448 | self._input_port = self._add_input_port("in") |
fca28f75 SM |
449 | |
450 | def _user_graph_is_configured(self): | |
9a2c8b8e | 451 | self._upstream_iter = self._create_message_iterator(self._input_port) |
fca28f75 SM |
452 | |
453 | def _user_consume(self): | |
454 | # We should not reach this. | |
455 | assert False | |
456 | ||
457 | g = bt2.Graph() | |
f5567ea8 FD |
458 | src = g.add_component(MySource, "src") |
459 | flt = g.add_component(MyFilter, "flt") | |
460 | snk = g.add_component(MySink, "snk") | |
461 | g.connect_ports(src.output_ports["out"], flt.input_ports["in"]) | |
462 | g.connect_ports(flt.output_ports["out"], snk.input_ports["in"]) | |
fca28f75 | 463 | |
f5567ea8 | 464 | with self.assertRaisesRegex(bt2._Error, "ValueError: woops"): |
fca28f75 SM |
465 | g.run() |
466 | ||
0a6d7302 | 467 | |
c182d7dd SM |
468 | def _setup_seek_test( |
469 | sink_cls, | |
470 | user_seek_beginning=None, | |
471 | user_can_seek_beginning=None, | |
472 | user_seek_ns_from_origin=None, | |
473 | user_can_seek_ns_from_origin=None, | |
c0e46a7c | 474 | can_seek_forward=False, |
c182d7dd | 475 | ): |
0a6d7302 | 476 | class MySourceIter(bt2._UserMessageIterator): |
8d8b141d | 477 | def __init__(self, config, port): |
0a6d7302 SM |
478 | tc, sc, ec = port.user_data |
479 | trace = tc() | |
480 | stream = trace.create_stream(sc) | |
481 | packet = stream.create_packet() | |
482 | ||
483 | self._msgs = [ | |
484 | self._create_stream_beginning_message(stream), | |
485 | self._create_packet_beginning_message(packet), | |
486 | self._create_event_message(ec, packet), | |
487 | self._create_event_message(ec, packet), | |
488 | self._create_packet_end_message(packet), | |
489 | self._create_stream_end_message(stream), | |
490 | ] | |
491 | self._at = 0 | |
c0e46a7c | 492 | config.can_seek_forward = can_seek_forward |
0a6d7302 SM |
493 | |
494 | def __next__(self): | |
495 | if self._at < len(self._msgs): | |
496 | msg = self._msgs[self._at] | |
497 | self._at += 1 | |
498 | return msg | |
499 | else: | |
500 | raise StopIteration | |
501 | ||
502 | if user_seek_beginning is not None: | |
503 | MySourceIter._user_seek_beginning = user_seek_beginning | |
504 | ||
505 | if user_can_seek_beginning is not None: | |
14cfc8ce | 506 | MySourceIter._user_can_seek_beginning = user_can_seek_beginning |
0a6d7302 | 507 | |
c182d7dd SM |
508 | if user_seek_ns_from_origin is not None: |
509 | MySourceIter._user_seek_ns_from_origin = user_seek_ns_from_origin | |
510 | ||
511 | if user_can_seek_ns_from_origin is not None: | |
512 | MySourceIter._user_can_seek_ns_from_origin = user_can_seek_ns_from_origin | |
513 | ||
0a6d7302 | 514 | class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter): |
59225a3e | 515 | def __init__(self, config, params, obj): |
0a6d7302 SM |
516 | tc = self._create_trace_class() |
517 | sc = tc.create_stream_class(supports_packets=True) | |
518 | ec = sc.create_event_class() | |
519 | ||
f5567ea8 | 520 | self._add_output_port("out", (tc, sc, ec)) |
0a6d7302 SM |
521 | |
522 | class MyFilterIter(bt2._UserMessageIterator): | |
8d8b141d | 523 | def __init__(self, config, port): |
9a2c8b8e | 524 | self._upstream_iter = self._create_message_iterator( |
f5567ea8 | 525 | self._component._input_ports["in"] |
0a6d7302 | 526 | ) |
c0e46a7c | 527 | config.can_seek_forward = self._upstream_iter.can_seek_forward |
0a6d7302 SM |
528 | |
529 | def __next__(self): | |
530 | return next(self._upstream_iter) | |
531 | ||
0a6d7302 | 532 | def _user_can_seek_beginning(self): |
14cfc8ce | 533 | return self._upstream_iter.can_seek_beginning() |
0a6d7302 SM |
534 | |
535 | def _user_seek_beginning(self): | |
536 | self._upstream_iter.seek_beginning() | |
537 | ||
c182d7dd SM |
538 | def _user_can_seek_ns_from_origin(self, ns_from_origin): |
539 | return self._upstream_iter.can_seek_ns_from_origin(ns_from_origin) | |
540 | ||
541 | def _user_seek_ns_from_origin(self, ns_from_origin): | |
542 | self._upstream_iter.seek_ns_from_origin(ns_from_origin) | |
543 | ||
0a6d7302 | 544 | class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter): |
59225a3e | 545 | def __init__(self, config, params, obj): |
f5567ea8 FD |
546 | self._add_input_port("in") |
547 | self._add_output_port("out") | |
0a6d7302 SM |
548 | |
549 | return _create_graph(MySource, sink_cls, flt_comp_cls=MyFilter) | |
f00b8d40 | 550 | |
0a6d7302 SM |
551 | |
552 | class UserMessageIteratorSeekBeginningTestCase(unittest.TestCase): | |
2e1b5615 SM |
553 | def test_can_seek_beginning_without_seek_beginning(self): |
554 | with self.assertRaisesRegex( | |
555 | bt2._IncompleteUserClass, | |
556 | "cannot create component class 'MySource': message iterator class implements _user_can_seek_beginning but not _user_seek_beginning", | |
557 | ): | |
558 | _setup_seek_test(SimpleSink, user_can_seek_beginning=lambda: None) | |
559 | ||
f00b8d40 | 560 | def test_can_seek_beginning(self): |
6c373cc9 | 561 | class MySink(bt2._UserSinkComponent): |
59225a3e | 562 | def __init__(self, config, params, obj): |
f5567ea8 | 563 | self._add_input_port("in") |
6c373cc9 PP |
564 | |
565 | def _user_graph_is_configured(self): | |
f5567ea8 | 566 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
6c373cc9 PP |
567 | |
568 | def _user_consume(self): | |
569 | nonlocal can_seek_beginning | |
14cfc8ce | 570 | can_seek_beginning = self._msg_iter.can_seek_beginning() |
6c373cc9 | 571 | |
6a91742b | 572 | def _user_can_seek_beginning(self): |
6c373cc9 PP |
573 | nonlocal input_port_iter_can_seek_beginning |
574 | return input_port_iter_can_seek_beginning | |
f00b8d40 | 575 | |
0a6d7302 | 576 | graph = _setup_seek_test( |
2e1b5615 SM |
577 | MySink, |
578 | user_can_seek_beginning=_user_can_seek_beginning, | |
579 | user_seek_beginning=lambda: None, | |
0a6d7302 | 580 | ) |
f00b8d40 | 581 | |
6c373cc9 PP |
582 | input_port_iter_can_seek_beginning = True |
583 | can_seek_beginning = None | |
584 | graph.run_once() | |
7f0c21bb | 585 | self.assertIs(can_seek_beginning, True) |
f00b8d40 | 586 | |
6c373cc9 PP |
587 | input_port_iter_can_seek_beginning = False |
588 | can_seek_beginning = None | |
589 | graph.run_once() | |
7f0c21bb | 590 | self.assertIs(can_seek_beginning, False) |
f00b8d40 | 591 | |
0a6d7302 SM |
592 | def test_no_can_seek_beginning_with_seek_beginning(self): |
593 | # Test an iterator without a _user_can_seek_beginning method, but with | |
594 | # a _user_seek_beginning method. | |
595 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 596 | def __init__(self, config, params, obj): |
f5567ea8 | 597 | self._add_input_port("in") |
0a6d7302 SM |
598 | |
599 | def _user_graph_is_configured(self): | |
f5567ea8 | 600 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
0a6d7302 SM |
601 | |
602 | def _user_consume(self): | |
603 | nonlocal can_seek_beginning | |
14cfc8ce | 604 | can_seek_beginning = self._msg_iter.can_seek_beginning() |
0a6d7302 SM |
605 | |
606 | def _user_seek_beginning(self): | |
607 | pass | |
f00b8d40 | 608 | |
0a6d7302 | 609 | graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning) |
6c373cc9 PP |
610 | can_seek_beginning = None |
611 | graph.run_once() | |
7f0c21bb | 612 | self.assertIs(can_seek_beginning, True) |
f00b8d40 | 613 | |
0a6d7302 SM |
614 | def test_no_can_seek_beginning(self): |
615 | # Test an iterator without a _user_can_seek_beginning method, without | |
616 | # a _user_seek_beginning method. | |
617 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 618 | def __init__(self, config, params, obj): |
f5567ea8 | 619 | self._add_input_port("in") |
0a6d7302 SM |
620 | |
621 | def _user_graph_is_configured(self): | |
f5567ea8 | 622 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
0a6d7302 SM |
623 | |
624 | def _user_consume(self): | |
625 | nonlocal can_seek_beginning | |
14cfc8ce | 626 | can_seek_beginning = self._msg_iter.can_seek_beginning() |
0a6d7302 SM |
627 | |
628 | graph = _setup_seek_test(MySink) | |
6c373cc9 PP |
629 | can_seek_beginning = None |
630 | graph.run_once() | |
7f0c21bb | 631 | self.assertIs(can_seek_beginning, False) |
f00b8d40 | 632 | |
f2fb1b32 SM |
633 | def test_can_seek_beginning_user_error(self): |
634 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 635 | def __init__(self, config, params, obj): |
f5567ea8 | 636 | self._add_input_port("in") |
f2fb1b32 SM |
637 | |
638 | def _user_graph_is_configured(self): | |
f5567ea8 | 639 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
f2fb1b32 SM |
640 | |
641 | def _user_consume(self): | |
642 | # This is expected to raise. | |
14cfc8ce | 643 | self._msg_iter.can_seek_beginning() |
f2fb1b32 SM |
644 | |
645 | def _user_can_seek_beginning(self): | |
f5567ea8 | 646 | raise ValueError("moustiquaire") |
f2fb1b32 SM |
647 | |
648 | graph = _setup_seek_test( | |
2e1b5615 SM |
649 | MySink, |
650 | user_can_seek_beginning=_user_can_seek_beginning, | |
651 | user_seek_beginning=lambda: None, | |
f2fb1b32 SM |
652 | ) |
653 | ||
654 | with self.assertRaises(bt2._Error) as ctx: | |
655 | graph.run_once() | |
656 | ||
657 | cause = ctx.exception[0] | |
f5567ea8 | 658 | self.assertIn("ValueError: moustiquaire", cause.message) |
f2fb1b32 SM |
659 | |
660 | def test_can_seek_beginning_wrong_return_value(self): | |
661 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 662 | def __init__(self, config, params, obj): |
f5567ea8 | 663 | self._add_input_port("in") |
f2fb1b32 SM |
664 | |
665 | def _user_graph_is_configured(self): | |
f5567ea8 | 666 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
f2fb1b32 SM |
667 | |
668 | def _user_consume(self): | |
669 | # This is expected to raise. | |
14cfc8ce | 670 | self._msg_iter.can_seek_beginning() |
f2fb1b32 SM |
671 | |
672 | def _user_can_seek_beginning(self): | |
f5567ea8 | 673 | return "Amqui" |
f2fb1b32 SM |
674 | |
675 | graph = _setup_seek_test( | |
2e1b5615 SM |
676 | MySink, |
677 | user_can_seek_beginning=_user_can_seek_beginning, | |
678 | user_seek_beginning=lambda: None, | |
f2fb1b32 SM |
679 | ) |
680 | ||
681 | with self.assertRaises(bt2._Error) as ctx: | |
682 | graph.run_once() | |
683 | ||
684 | cause = ctx.exception[0] | |
685 | self.assertIn("TypeError: 'str' is not a 'bool' object", cause.message) | |
686 | ||
f00b8d40 | 687 | def test_seek_beginning(self): |
6c373cc9 | 688 | class MySink(bt2._UserSinkComponent): |
59225a3e | 689 | def __init__(self, config, params, obj): |
f5567ea8 | 690 | self._add_input_port("in") |
f00b8d40 | 691 | |
6c373cc9 | 692 | def _user_graph_is_configured(self): |
f5567ea8 | 693 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
6c373cc9 PP |
694 | |
695 | def _user_consume(self): | |
696 | nonlocal do_seek_beginning | |
697 | nonlocal msg | |
698 | ||
699 | if do_seek_beginning: | |
700 | self._msg_iter.seek_beginning() | |
701 | return | |
702 | ||
703 | msg = next(self._msg_iter) | |
704 | ||
0a6d7302 SM |
705 | def _user_seek_beginning(self): |
706 | self._at = 0 | |
707 | ||
6c373cc9 | 708 | msg = None |
0a6d7302 SM |
709 | graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning) |
710 | ||
711 | # Consume message. | |
712 | do_seek_beginning = False | |
6c373cc9 | 713 | graph.run_once() |
f0a42b33 | 714 | self.assertIs(type(msg), bt2._StreamBeginningMessageConst) |
0a6d7302 SM |
715 | |
716 | # Consume message. | |
6c373cc9 | 717 | graph.run_once() |
f0a42b33 | 718 | self.assertIs(type(msg), bt2._PacketBeginningMessageConst) |
0a6d7302 SM |
719 | |
720 | # Seek beginning. | |
6c373cc9 PP |
721 | do_seek_beginning = True |
722 | graph.run_once() | |
0a6d7302 SM |
723 | |
724 | # Consume message. | |
6c373cc9 PP |
725 | do_seek_beginning = False |
726 | graph.run_once() | |
f0a42b33 | 727 | self.assertIs(type(msg), bt2._StreamBeginningMessageConst) |
f00b8d40 | 728 | |
6c373cc9 PP |
729 | def test_seek_beginning_user_error(self): |
730 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 731 | def __init__(self, config, params, obj): |
f5567ea8 | 732 | self._add_input_port("in") |
f00b8d40 | 733 | |
6c373cc9 | 734 | def _user_graph_is_configured(self): |
f5567ea8 | 735 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
f00b8d40 | 736 | |
6c373cc9 PP |
737 | def _user_consume(self): |
738 | self._msg_iter.seek_beginning() | |
f00b8d40 | 739 | |
0a6d7302 | 740 | def _user_seek_beginning(self): |
f5567ea8 | 741 | raise ValueError("ouch") |
f00b8d40 | 742 | |
0a6d7302 | 743 | graph = _setup_seek_test(MySink, user_seek_beginning=_user_seek_beginning) |
f00b8d40 | 744 | |
694c792b | 745 | with self.assertRaises(bt2._Error): |
6c373cc9 | 746 | graph.run_once() |
f00b8d40 SM |
747 | |
748 | ||
c182d7dd | 749 | class UserMessageIteratorSeekNsFromOriginTestCase(unittest.TestCase): |
2e1b5615 SM |
750 | def test_can_seek_ns_from_origin_without_seek_ns_from_origin(self): |
751 | # Test the case where: | |
752 | # | |
753 | # - can_seek_ns_from_origin: Returns True (don't really care, as long | |
754 | # as it's provided) | |
755 | # - seek_ns_from_origin provided: No | |
756 | # - can the iterator seek beginning: Don't care | |
757 | # - can the iterator seek forward: Don't care | |
758 | for can_seek_ns_from_origin in (False, True): | |
759 | for iter_can_seek_beginning in (False, True): | |
760 | for iter_can_seek_forward in (False, True): | |
761 | with self.assertRaisesRegex( | |
762 | bt2._IncompleteUserClass, | |
763 | "cannot create component class 'MySource': message iterator class implements _user_can_seek_ns_from_origin but not _user_seek_ns_from_origin", | |
764 | ): | |
765 | self._can_seek_ns_from_origin_test( | |
766 | None, | |
767 | user_can_seek_ns_from_origin_ret_val=True, | |
768 | user_seek_ns_from_origin_provided=False, | |
769 | iter_can_seek_beginning=iter_can_seek_beginning, | |
770 | iter_can_seek_forward=iter_can_seek_forward, | |
771 | ) | |
772 | ||
c0e46a7c SM |
773 | def test_can_seek_ns_from_origin_returns_true(self): |
774 | # Test the case where: | |
775 | # | |
776 | # - can_seek_ns_from_origin: returns True | |
2e1b5615 | 777 | # - seek_ns_from_origin provided: Yes |
c0e46a7c SM |
778 | # - can the iterator seek beginning: Don't care |
779 | # - can the iterator seek forward: Don't care | |
780 | # | |
781 | # We expect iter.can_seek_ns_from_origin to return True. | |
2e1b5615 SM |
782 | for iter_can_seek_beginning in (False, True): |
783 | for iter_can_seek_forward in (False, True): | |
784 | self._can_seek_ns_from_origin_test( | |
785 | expected_outcome=True, | |
786 | user_can_seek_ns_from_origin_ret_val=True, | |
787 | user_seek_ns_from_origin_provided=True, | |
788 | iter_can_seek_beginning=iter_can_seek_beginning, | |
789 | iter_can_seek_forward=iter_can_seek_forward, | |
790 | ) | |
c0e46a7c SM |
791 | |
792 | def test_can_seek_ns_from_origin_returns_false_can_seek_beginning_forward_seekable( | |
75882e97 | 793 | self, |
c0e46a7c SM |
794 | ): |
795 | # Test the case where: | |
796 | # | |
797 | # - can_seek_ns_from_origin: returns False | |
2e1b5615 | 798 | # - seek_ns_from_origin provided: Yes |
c0e46a7c SM |
799 | # - can the iterator seek beginning: Yes |
800 | # - can the iterator seek forward: Yes | |
801 | # | |
802 | # We expect iter.can_seek_ns_from_origin to return True. | |
2e1b5615 SM |
803 | self._can_seek_ns_from_origin_test( |
804 | expected_outcome=True, | |
805 | user_can_seek_ns_from_origin_ret_val=False, | |
806 | user_seek_ns_from_origin_provided=True, | |
807 | iter_can_seek_beginning=True, | |
808 | iter_can_seek_forward=True, | |
809 | ) | |
c182d7dd | 810 | |
c0e46a7c | 811 | def test_can_seek_ns_from_origin_returns_false_can_seek_beginning_not_forward_seekable( |
75882e97 | 812 | self, |
c0e46a7c SM |
813 | ): |
814 | # Test the case where: | |
815 | # | |
816 | # - can_seek_ns_from_origin: returns False | |
2e1b5615 | 817 | # - seek_ns_from_origin provided: Yes |
c0e46a7c SM |
818 | # - can the iterator seek beginning: Yes |
819 | # - can the iterator seek forward: No | |
820 | # | |
821 | # We expect iter.can_seek_ns_from_origin to return False. | |
2e1b5615 SM |
822 | self._can_seek_ns_from_origin_test( |
823 | expected_outcome=False, | |
824 | user_can_seek_ns_from_origin_ret_val=False, | |
825 | user_seek_ns_from_origin_provided=True, | |
826 | iter_can_seek_beginning=True, | |
827 | iter_can_seek_forward=False, | |
828 | ) | |
c182d7dd | 829 | |
c0e46a7c | 830 | def test_can_seek_ns_from_origin_returns_false_cant_seek_beginning_forward_seekable( |
75882e97 | 831 | self, |
c0e46a7c SM |
832 | ): |
833 | # Test the case where: | |
834 | # | |
835 | # - can_seek_ns_from_origin: returns False | |
2e1b5615 | 836 | # - seek_ns_from_origin provided: Yes |
c0e46a7c SM |
837 | # - can the iterator seek beginning: No |
838 | # - can the iterator seek forward: Yes | |
839 | # | |
840 | # We expect iter.can_seek_ns_from_origin to return False. | |
c0e46a7c SM |
841 | self._can_seek_ns_from_origin_test( |
842 | expected_outcome=False, | |
843 | user_can_seek_ns_from_origin_ret_val=False, | |
2e1b5615 | 844 | user_seek_ns_from_origin_provided=True, |
c0e46a7c SM |
845 | iter_can_seek_beginning=False, |
846 | iter_can_seek_forward=True, | |
847 | ) | |
848 | ||
849 | def test_can_seek_ns_from_origin_returns_false_cant_seek_beginning_not_forward_seekable( | |
75882e97 | 850 | self, |
c0e46a7c SM |
851 | ): |
852 | # Test the case where: | |
853 | # | |
854 | # - can_seek_ns_from_origin: returns False | |
2e1b5615 | 855 | # - seek_ns_from_origin provided: Yes |
c0e46a7c SM |
856 | # - can the iterator seek beginning: No |
857 | # - can the iterator seek forward: No | |
858 | # | |
859 | # We expect iter.can_seek_ns_from_origin to return False. | |
2e1b5615 SM |
860 | self._can_seek_ns_from_origin_test( |
861 | expected_outcome=False, | |
862 | user_can_seek_ns_from_origin_ret_val=False, | |
863 | user_seek_ns_from_origin_provided=True, | |
864 | iter_can_seek_beginning=False, | |
865 | iter_can_seek_forward=False, | |
866 | ) | |
c0e46a7c SM |
867 | |
868 | def test_no_can_seek_ns_from_origin_seek_ns_from_origin(self): | |
869 | # Test the case where: | |
870 | # | |
871 | # - can_seek_ns_from_origin: Not provided | |
872 | # - seek_ns_from_origin provided: Yes | |
873 | # - can the iterator seek beginning: Don't care | |
874 | # - can the iterator seek forward: Don't care | |
875 | # | |
876 | # We expect iter.can_seek_ns_from_origin to return True. | |
877 | for iter_can_seek_beginning in (False, True): | |
878 | for iter_can_seek_forward in (False, True): | |
879 | self._can_seek_ns_from_origin_test( | |
880 | expected_outcome=True, | |
881 | user_can_seek_ns_from_origin_ret_val=None, | |
882 | user_seek_ns_from_origin_provided=True, | |
883 | iter_can_seek_beginning=iter_can_seek_beginning, | |
884 | iter_can_seek_forward=iter_can_seek_forward, | |
c182d7dd SM |
885 | ) |
886 | ||
c0e46a7c | 887 | def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_can_seek_beginning_forward_seekable( |
75882e97 | 888 | self, |
c0e46a7c SM |
889 | ): |
890 | # Test the case where: | |
891 | # | |
892 | # - can_seek_ns_from_origin: Not provided | |
893 | # - seek_ns_from_origin provided: Not provided | |
894 | # - can the iterator seek beginning: Yes | |
895 | # - can the iterator seek forward: Yes | |
896 | # | |
897 | # We expect iter.can_seek_ns_from_origin to return True. | |
898 | self._can_seek_ns_from_origin_test( | |
899 | expected_outcome=True, | |
900 | user_can_seek_ns_from_origin_ret_val=None, | |
901 | user_seek_ns_from_origin_provided=False, | |
902 | iter_can_seek_beginning=True, | |
903 | iter_can_seek_forward=True, | |
904 | ) | |
c182d7dd | 905 | |
c0e46a7c | 906 | def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_can_seek_beginning_not_forward_seekable( |
75882e97 | 907 | self, |
c0e46a7c SM |
908 | ): |
909 | # Test the case where: | |
910 | # | |
911 | # - can_seek_ns_from_origin: Not provided | |
912 | # - seek_ns_from_origin provided: Not provided | |
913 | # - can the iterator seek beginning: Yes | |
914 | # - can the iterator seek forward: No | |
915 | # | |
916 | # We expect iter.can_seek_ns_from_origin to return False. | |
917 | self._can_seek_ns_from_origin_test( | |
918 | expected_outcome=False, | |
919 | user_can_seek_ns_from_origin_ret_val=None, | |
920 | user_seek_ns_from_origin_provided=False, | |
921 | iter_can_seek_beginning=True, | |
922 | iter_can_seek_forward=False, | |
c182d7dd SM |
923 | ) |
924 | ||
c0e46a7c | 925 | def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_cant_seek_beginning_forward_seekable( |
75882e97 | 926 | self, |
c0e46a7c SM |
927 | ): |
928 | # Test the case where: | |
929 | # | |
930 | # - can_seek_ns_from_origin: Not provided | |
931 | # - seek_ns_from_origin provided: Not provided | |
932 | # - can the iterator seek beginning: No | |
933 | # - can the iterator seek forward: Yes | |
934 | # | |
935 | # We expect iter.can_seek_ns_from_origin to return False. | |
936 | self._can_seek_ns_from_origin_test( | |
937 | expected_outcome=False, | |
938 | user_can_seek_ns_from_origin_ret_val=None, | |
939 | user_seek_ns_from_origin_provided=False, | |
940 | iter_can_seek_beginning=False, | |
941 | iter_can_seek_forward=True, | |
942 | ) | |
c182d7dd | 943 | |
c0e46a7c | 944 | def test_no_can_seek_ns_from_origin_no_seek_ns_from_origin_cant_seek_beginning_not_forward_seekable( |
75882e97 | 945 | self, |
c0e46a7c SM |
946 | ): |
947 | # Test the case where: | |
948 | # | |
949 | # - can_seek_ns_from_origin: Not provided | |
950 | # - seek_ns_from_origin provided: Not provided | |
951 | # - can the iterator seek beginning: No | |
952 | # - can the iterator seek forward: No | |
953 | # | |
954 | # We expect iter.can_seek_ns_from_origin to return False. | |
955 | self._can_seek_ns_from_origin_test( | |
956 | expected_outcome=False, | |
957 | user_can_seek_ns_from_origin_ret_val=None, | |
958 | user_seek_ns_from_origin_provided=False, | |
959 | iter_can_seek_beginning=False, | |
960 | iter_can_seek_forward=False, | |
961 | ) | |
c182d7dd | 962 | |
c0e46a7c SM |
963 | def _can_seek_ns_from_origin_test( |
964 | self, | |
965 | expected_outcome, | |
966 | user_can_seek_ns_from_origin_ret_val, | |
967 | user_seek_ns_from_origin_provided, | |
968 | iter_can_seek_beginning, | |
969 | iter_can_seek_forward, | |
970 | ): | |
c182d7dd | 971 | class MySink(bt2._UserSinkComponent): |
59225a3e | 972 | def __init__(self, config, params, obj): |
f5567ea8 | 973 | self._add_input_port("in") |
c182d7dd SM |
974 | |
975 | def _user_graph_is_configured(self): | |
f5567ea8 | 976 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
c182d7dd SM |
977 | |
978 | def _user_consume(self): | |
979 | nonlocal can_seek_ns_from_origin | |
c182d7dd | 980 | can_seek_ns_from_origin = self._msg_iter.can_seek_ns_from_origin( |
c0e46a7c | 981 | passed_ns_from_origin |
c182d7dd SM |
982 | ) |
983 | ||
c0e46a7c | 984 | if user_can_seek_ns_from_origin_ret_val is not None: |
c182d7dd | 985 | |
c0e46a7c SM |
986 | def user_can_seek_ns_from_origin(self, ns_from_origin): |
987 | nonlocal received_ns_from_origin | |
988 | received_ns_from_origin = ns_from_origin | |
989 | return user_can_seek_ns_from_origin_ret_val | |
c182d7dd | 990 | |
c0e46a7c SM |
991 | else: |
992 | user_can_seek_ns_from_origin = None | |
c182d7dd | 993 | |
c0e46a7c | 994 | if user_seek_ns_from_origin_provided: |
c182d7dd | 995 | |
c0e46a7c SM |
996 | def user_seek_ns_from_origin(self, ns_from_origin): |
997 | pass | |
c182d7dd | 998 | |
c0e46a7c SM |
999 | else: |
1000 | user_seek_ns_from_origin = None | |
c182d7dd | 1001 | |
c0e46a7c | 1002 | if iter_can_seek_beginning: |
c182d7dd | 1003 | |
c0e46a7c SM |
1004 | def user_seek_beginning(self): |
1005 | pass | |
c182d7dd | 1006 | |
c0e46a7c SM |
1007 | else: |
1008 | user_seek_beginning = None | |
c182d7dd | 1009 | |
c0e46a7c SM |
1010 | graph = _setup_seek_test( |
1011 | MySink, | |
1012 | user_can_seek_ns_from_origin=user_can_seek_ns_from_origin, | |
1013 | user_seek_ns_from_origin=user_seek_ns_from_origin, | |
1014 | user_seek_beginning=user_seek_beginning, | |
1015 | can_seek_forward=iter_can_seek_forward, | |
1016 | ) | |
c182d7dd | 1017 | |
c0e46a7c SM |
1018 | passed_ns_from_origin = 77 |
1019 | received_ns_from_origin = None | |
c182d7dd | 1020 | can_seek_ns_from_origin = None |
c182d7dd | 1021 | graph.run_once() |
c0e46a7c SM |
1022 | self.assertIs(can_seek_ns_from_origin, expected_outcome) |
1023 | ||
1024 | if user_can_seek_ns_from_origin_ret_val is not None: | |
1025 | self.assertEqual(received_ns_from_origin, passed_ns_from_origin) | |
c182d7dd SM |
1026 | |
1027 | def test_can_seek_ns_from_origin_user_error(self): | |
1028 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 1029 | def __init__(self, config, params, obj): |
f5567ea8 | 1030 | self._add_input_port("in") |
c182d7dd SM |
1031 | |
1032 | def _user_graph_is_configured(self): | |
f5567ea8 | 1033 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
c182d7dd SM |
1034 | |
1035 | def _user_consume(self): | |
1036 | # This is expected to raise. | |
1037 | self._msg_iter.can_seek_ns_from_origin(2) | |
1038 | ||
1039 | def _user_can_seek_ns_from_origin(self, ns_from_origin): | |
f5567ea8 | 1040 | raise ValueError("Joutel") |
c182d7dd SM |
1041 | |
1042 | graph = _setup_seek_test( | |
2e1b5615 SM |
1043 | MySink, |
1044 | user_can_seek_ns_from_origin=_user_can_seek_ns_from_origin, | |
1045 | user_seek_ns_from_origin=lambda: None, | |
c182d7dd SM |
1046 | ) |
1047 | ||
1048 | with self.assertRaises(bt2._Error) as ctx: | |
1049 | graph.run_once() | |
1050 | ||
1051 | cause = ctx.exception[0] | |
f5567ea8 | 1052 | self.assertIn("ValueError: Joutel", cause.message) |
c182d7dd SM |
1053 | |
1054 | def test_can_seek_ns_from_origin_wrong_return_value(self): | |
1055 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 1056 | def __init__(self, config, params, obj): |
f5567ea8 | 1057 | self._add_input_port("in") |
c182d7dd SM |
1058 | |
1059 | def _user_graph_is_configured(self): | |
f5567ea8 | 1060 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
c182d7dd SM |
1061 | |
1062 | def _user_consume(self): | |
1063 | # This is expected to raise. | |
1064 | self._msg_iter.can_seek_ns_from_origin(2) | |
1065 | ||
1066 | def _user_can_seek_ns_from_origin(self, ns_from_origin): | |
f5567ea8 | 1067 | return "Nitchequon" |
c182d7dd SM |
1068 | |
1069 | graph = _setup_seek_test( | |
2e1b5615 SM |
1070 | MySink, |
1071 | user_can_seek_ns_from_origin=_user_can_seek_ns_from_origin, | |
1072 | user_seek_ns_from_origin=lambda: None, | |
c182d7dd SM |
1073 | ) |
1074 | ||
1075 | with self.assertRaises(bt2._Error) as ctx: | |
1076 | graph.run_once() | |
1077 | ||
1078 | cause = ctx.exception[0] | |
1079 | self.assertIn("TypeError: 'str' is not a 'bool' object", cause.message) | |
1080 | ||
1081 | def test_seek_ns_from_origin(self): | |
1082 | class MySink(bt2._UserSinkComponent): | |
59225a3e | 1083 | def __init__(self, config, params, obj): |
f5567ea8 | 1084 | self._add_input_port("in") |
c182d7dd SM |
1085 | |
1086 | def _user_graph_is_configured(self): | |
f5567ea8 | 1087 | self._msg_iter = self._create_message_iterator(self._input_ports["in"]) |
c182d7dd SM |
1088 | |
1089 | def _user_consume(self): | |
1090 | self._msg_iter.seek_ns_from_origin(17) | |
1091 | ||
1092 | def _user_seek_ns_from_origin(self, ns_from_origin): | |
1093 | nonlocal actual_ns_from_origin | |
1094 | actual_ns_from_origin = ns_from_origin | |
1095 | ||
c182d7dd SM |
1096 | graph = _setup_seek_test( |
1097 | MySink, user_seek_ns_from_origin=_user_seek_ns_from_origin | |
1098 | ) | |
1099 | ||
1100 | actual_ns_from_origin = None | |
1101 | graph.run_once() | |
1102 | self.assertEqual(actual_ns_from_origin, 17) | |
1103 | ||
1104 | ||
f5567ea8 | 1105 | if __name__ == "__main__": |
f00b8d40 | 1106 | unittest.main() |