bt2: add auto source discovery support to TraceCollectionMessageIterator
[babeltrace.git] / tests / bindings / python / bt2 / test_message_iterator.py
CommitLineData
d2d857a8
MJ
1#
2# Copyright (C) 2019 EfficiOS Inc.
3#
4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License
6# as published by the Free Software Foundation; only version 2
7# of the License.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17#
18
c4239792 19from bt2 import value
548bb510 20import collections
811644b8
PP
21import unittest
22import copy
23import bt2
8e97c333 24import sys
6c373cc9 25from utils import TestOutputPortMessageIterator
811644b8
PP
26
27
5602ef81 28class UserMessageIteratorTestCase(unittest.TestCase):
811644b8 29 @staticmethod
ca02df0a 30 def _create_graph(src_comp_cls, flt_comp_cls=None):
811644b8 31 class MySink(bt2._UserSinkComponent):
66964f3f 32 def __init__(self, params, obj):
811644b8
PP
33 self._add_input_port('in')
34
6a91742b 35 def _user_consume(self):
5602ef81 36 next(self._msg_iter)
811644b8 37
6a91742b 38 def _user_graph_is_configured(self):
ca02df0a
PP
39 self._msg_iter = self._create_input_port_message_iterator(
40 self._input_ports['in']
41 )
811644b8
PP
42
43 graph = bt2.Graph()
44 src_comp = graph.add_component(src_comp_cls, 'src')
ca02df0a
PP
45
46 if flt_comp_cls is not None:
47 flt_comp = graph.add_component(flt_comp_cls, 'flt')
48
811644b8 49 sink_comp = graph.add_component(MySink, 'sink')
ca02df0a
PP
50
51 if flt_comp_cls is not None:
52 assert flt_comp is not None
53 graph.connect_ports(
54 src_comp.output_ports['out'], flt_comp.input_ports['in']
55 )
56 out_port = flt_comp.output_ports['out']
57 else:
58 out_port = src_comp.output_ports['out']
59
60 graph.connect_ports(out_port, sink_comp.input_ports['in'])
811644b8
PP
61 return graph
62
63 def test_init(self):
c5f330cd
SM
64 the_output_port_from_source = None
65 the_output_port_from_iter = None
66
5602ef81 67 class MyIter(bt2._UserMessageIterator):
c5f330cd 68 def __init__(self, self_port_output):
811644b8 69 nonlocal initialized
c5f330cd 70 nonlocal the_output_port_from_iter
811644b8 71 initialized = True
c5f330cd 72 the_output_port_from_iter = self_port_output
811644b8 73
cfbd7cf3 74 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 75 def __init__(self, params, obj):
c5f330cd 76 nonlocal the_output_port_from_source
2e00bc76 77 the_output_port_from_source = self._add_output_port('out', 'user data')
811644b8
PP
78
79 initialized = False
80 graph = self._create_graph(MySource)
c5f330cd 81 graph.run()
811644b8 82 self.assertTrue(initialized)
cfbd7cf3
FD
83 self.assertEqual(
84 the_output_port_from_source.addr, the_output_port_from_iter.addr
85 )
2e00bc76 86 self.assertEqual(the_output_port_from_iter.user_data, 'user data')
811644b8 87
ca02df0a
PP
88 def test_create_from_message_iterator(self):
89 class MySourceIter(bt2._UserMessageIterator):
90 def __init__(self, self_port_output):
91 nonlocal src_iter_initialized
92 src_iter_initialized = True
93
94 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
66964f3f 95 def __init__(self, params, obj):
ca02df0a
PP
96 self._add_output_port('out')
97
98 class MyFilterIter(bt2._UserMessageIterator):
99 def __init__(self, self_port_output):
100 nonlocal flt_iter_initialized
101 flt_iter_initialized = True
102 self._up_iter = self._create_input_port_message_iterator(
103 self._component._input_ports['in']
104 )
105
106 def __next__(self):
107 return next(self._up_iter)
108
109 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
66964f3f 110 def __init__(self, params, obj):
ca02df0a
PP
111 self._add_input_port('in')
112 self._add_output_port('out')
113
114 src_iter_initialized = False
115 flt_iter_initialized = False
116 graph = self._create_graph(MySource, MyFilter)
117 graph.run()
118 self.assertTrue(src_iter_initialized)
119 self.assertTrue(flt_iter_initialized)
120
811644b8 121 def test_finalize(self):
5602ef81 122 class MyIter(bt2._UserMessageIterator):
6a91742b 123 def _user_finalize(self):
811644b8
PP
124 nonlocal finalized
125 finalized = True
126
cfbd7cf3 127 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 128 def __init__(self, params, obj):
811644b8
PP
129 self._add_output_port('out')
130
131 finalized = False
132 graph = self._create_graph(MySource)
c5f330cd 133 graph.run()
811644b8
PP
134 del graph
135 self.assertTrue(finalized)
136
137 def test_component(self):
5602ef81 138 class MyIter(bt2._UserMessageIterator):
c5f330cd 139 def __init__(self, self_port_output):
811644b8
PP
140 nonlocal salut
141 salut = self._component._salut
142
cfbd7cf3 143 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 144 def __init__(self, params, obj):
811644b8
PP
145 self._add_output_port('out')
146 self._salut = 23
147
148 salut = None
149 graph = self._create_graph(MySource)
c5f330cd 150 graph.run()
811644b8
PP
151 self.assertEqual(salut, 23)
152
153 def test_addr(self):
5602ef81 154 class MyIter(bt2._UserMessageIterator):
c5f330cd 155 def __init__(self, self_port_output):
811644b8
PP
156 nonlocal addr
157 addr = self.addr
158
cfbd7cf3 159 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 160 def __init__(self, params, obj):
811644b8
PP
161 self._add_output_port('out')
162
163 addr = None
164 graph = self._create_graph(MySource)
c5f330cd 165 graph.run()
811644b8
PP
166 self.assertIsNotNone(addr)
167 self.assertNotEqual(addr, 0)
168
d79a8353
SM
169 # Test that messages returned by _UserMessageIterator.__next__ remain valid
170 # and can be re-used.
171 def test_reuse_message(self):
172 class MyIter(bt2._UserMessageIterator):
173 def __init__(self, port):
174 tc, sc, ec = port.user_data
175 trace = tc()
176 stream = trace.create_stream(sc)
177 packet = stream.create_packet()
178
179 # This message will be returned twice by __next__.
180 event_message = self._create_event_message(ec, packet)
181
182 self._msgs = [
183 self._create_stream_beginning_message(stream),
d79a8353
SM
184 self._create_packet_beginning_message(packet),
185 event_message,
186 event_message,
187 ]
188
189 def __next__(self):
190 return self._msgs.pop(0)
191
192 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 193 def __init__(self, params, obj):
d79a8353 194 tc = self._create_trace_class()
26fc5aed 195 sc = tc.create_stream_class(supports_packets=True)
d79a8353
SM
196 ec = sc.create_event_class()
197 self._add_output_port('out', (tc, sc, ec))
198
199 graph = bt2.Graph()
200 src = graph.add_component(MySource, 'src')
6c373cc9 201 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
d79a8353
SM
202
203 # Skip beginning messages.
188edac1 204 msg = next(it)
3fb99a22 205 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
188edac1 206 msg = next(it)
3fb99a22 207 self.assertIsInstance(msg, bt2._PacketBeginningMessage)
d79a8353
SM
208
209 msg_ev1 = next(it)
210 msg_ev2 = next(it)
211
3fb99a22
PP
212 self.assertIsInstance(msg_ev1, bt2._EventMessage)
213 self.assertIsInstance(msg_ev2, bt2._EventMessage)
d79a8353
SM
214 self.assertEqual(msg_ev1.addr, msg_ev2.addr)
215
f00b8d40 216 @staticmethod
6c373cc9 217 def _setup_seek_beginning_test(sink_cls):
f00b8d40
SM
218 # Use a source, a filter and an output port iterator. This allows us
219 # to test calling `seek_beginning` on both a _OutputPortMessageIterator
220 # and a _UserComponentInputPortMessageIterator, on top of checking that
221 # _UserMessageIterator._seek_beginning is properly called.
222
223 class MySourceIter(bt2._UserMessageIterator):
224 def __init__(self, port):
225 tc, sc, ec = port.user_data
226 trace = tc()
227 stream = trace.create_stream(sc)
228 packet = stream.create_packet()
229
230 self._msgs = [
231 self._create_stream_beginning_message(stream),
f00b8d40
SM
232 self._create_packet_beginning_message(packet),
233 self._create_event_message(ec, packet),
234 self._create_event_message(ec, packet),
235 self._create_packet_end_message(packet),
f00b8d40
SM
236 self._create_stream_end_message(stream),
237 ]
238 self._at = 0
239
6a91742b 240 def _user_seek_beginning(self):
f00b8d40
SM
241 self._at = 0
242
243 def __next__(self):
244 if self._at < len(self._msgs):
245 msg = self._msgs[self._at]
246 self._at += 1
247 return msg
248 else:
249 raise StopIteration
250
cfbd7cf3 251 class MySource(bt2._UserSourceComponent, message_iterator_class=MySourceIter):
66964f3f 252 def __init__(self, params, obj):
f00b8d40 253 tc = self._create_trace_class()
26fc5aed 254 sc = tc.create_stream_class(supports_packets=True)
f00b8d40
SM
255 ec = sc.create_event_class()
256
257 self._add_output_port('out', (tc, sc, ec))
258
259 class MyFilterIter(bt2._UserMessageIterator):
260 def __init__(self, port):
261 input_port = port.user_data
ca02df0a
PP
262 self._upstream_iter = self._create_input_port_message_iterator(
263 input_port
264 )
f00b8d40
SM
265
266 def __next__(self):
267 return next(self._upstream_iter)
268
6a91742b 269 def _user_seek_beginning(self):
f00b8d40
SM
270 self._upstream_iter.seek_beginning()
271
272 @property
6a91742b 273 def _user_can_seek_beginning(self):
f00b8d40
SM
274 return self._upstream_iter.can_seek_beginning
275
276 class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
66964f3f 277 def __init__(self, params, obj):
f00b8d40
SM
278 input_port = self._add_input_port('in')
279 self._add_output_port('out', input_port)
280
f00b8d40
SM
281 graph = bt2.Graph()
282 src = graph.add_component(MySource, 'src')
283 flt = graph.add_component(MyFilter, 'flt')
6c373cc9 284 sink = graph.add_component(sink_cls, 'sink')
f00b8d40 285 graph.connect_ports(src.output_ports['out'], flt.input_ports['in'])
6c373cc9
PP
286 graph.connect_ports(flt.output_ports['out'], sink.input_ports['in'])
287 return MySourceIter, graph
f00b8d40
SM
288
289 def test_can_seek_beginning(self):
6c373cc9
PP
290 class MySink(bt2._UserSinkComponent):
291 def __init__(self, params, obj):
292 self._add_input_port('in')
293
294 def _user_graph_is_configured(self):
295 self._msg_iter = self._create_input_port_message_iterator(
296 self._input_ports['in']
297 )
298
299 def _user_consume(self):
300 nonlocal can_seek_beginning
301 can_seek_beginning = self._msg_iter.can_seek_beginning
302
303 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
f00b8d40 304
6a91742b 305 def _user_can_seek_beginning(self):
6c373cc9
PP
306 nonlocal input_port_iter_can_seek_beginning
307 return input_port_iter_can_seek_beginning
f00b8d40 308
6a91742b 309 MySourceIter._user_can_seek_beginning = property(_user_can_seek_beginning)
f00b8d40 310
6c373cc9
PP
311 input_port_iter_can_seek_beginning = True
312 can_seek_beginning = None
313 graph.run_once()
314 self.assertTrue(can_seek_beginning)
f00b8d40 315
6c373cc9
PP
316 input_port_iter_can_seek_beginning = False
317 can_seek_beginning = None
318 graph.run_once()
319 self.assertFalse(can_seek_beginning)
f00b8d40
SM
320
321 # Once can_seek_beginning returns an error, verify that it raises when
322 # _can_seek_beginning has/returns the wrong type.
323
324 # Remove the _can_seek_beginning method, we now rely on the presence of
325 # a _seek_beginning method to know whether the iterator can seek to
326 # beginning or not.
6a91742b 327 del MySourceIter._user_can_seek_beginning
6c373cc9
PP
328 can_seek_beginning = None
329 graph.run_once()
330 self.assertTrue(can_seek_beginning)
f00b8d40 331
6a91742b 332 del MySourceIter._user_seek_beginning
6c373cc9
PP
333 can_seek_beginning = None
334 graph.run_once()
335 self.assertFalse(can_seek_beginning)
f00b8d40
SM
336
337 def test_seek_beginning(self):
6c373cc9
PP
338 class MySink(bt2._UserSinkComponent):
339 def __init__(self, params, obj):
340 self._add_input_port('in')
f00b8d40 341
6c373cc9
PP
342 def _user_graph_is_configured(self):
343 self._msg_iter = self._create_input_port_message_iterator(
344 self._input_ports['in']
345 )
346
347 def _user_consume(self):
348 nonlocal do_seek_beginning
349 nonlocal msg
350
351 if do_seek_beginning:
352 self._msg_iter.seek_beginning()
353 return
354
355 msg = next(self._msg_iter)
356
357 do_seek_beginning = False
358 msg = None
359 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
360 graph.run_once()
3fb99a22 361 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
6c373cc9 362 graph.run_once()
3fb99a22 363 self.assertIsInstance(msg, bt2._PacketBeginningMessage)
6c373cc9
PP
364 do_seek_beginning = True
365 graph.run_once()
366 do_seek_beginning = False
367 graph.run_once()
368 self.assertIsInstance(msg, bt2._StreamBeginningMessage)
f00b8d40 369
6c373cc9
PP
370 def test_seek_beginning_user_error(self):
371 class MySink(bt2._UserSinkComponent):
372 def __init__(self, params, obj):
373 self._add_input_port('in')
f00b8d40 374
6c373cc9
PP
375 def _user_graph_is_configured(self):
376 self._msg_iter = self._create_input_port_message_iterator(
377 self._input_ports['in']
378 )
f00b8d40 379
6c373cc9
PP
380 def _user_consume(self):
381 self._msg_iter.seek_beginning()
f00b8d40 382
6c373cc9 383 MySourceIter, graph = self._setup_seek_beginning_test(MySink)
f00b8d40 384
6a91742b 385 def _user_seek_beginning_error(self):
cfbd7cf3 386 raise ValueError('ouch')
f00b8d40 387
6a91742b 388 MySourceIter._user_seek_beginning = _user_seek_beginning_error
f00b8d40 389
694c792b 390 with self.assertRaises(bt2._Error):
6c373cc9 391 graph.run_once()
f00b8d40 392
0361868a
SM
393 # Try consuming many times from an iterator that always returns TryAgain.
394 # This verifies that we are not missing an incref of Py_None, making the
395 # refcount of Py_None reach 0.
396 def test_try_again_many_times(self):
397 class MyIter(bt2._UserMessageIterator):
398 def __next__(self):
399 raise bt2.TryAgain
400
401 class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter):
66964f3f 402 def __init__(self, params, obj):
0361868a
SM
403 self._add_output_port('out')
404
405 graph = bt2.Graph()
406 src = graph.add_component(MySource, 'src')
6c373cc9 407 it = TestOutputPortMessageIterator(graph, src.output_ports['out'])
0361868a 408
8e97c333
PP
409 # Three times the initial ref count of `None` iterations should
410 # be enough to catch the bug even if there are small differences
0361868a 411 # between configurations.
8e97c333
PP
412 none_ref_count = sys.getrefcount(None) * 3
413
414 for i in range(none_ref_count):
0361868a
SM
415 with self.assertRaises(bt2.TryAgain):
416 next(it)
417
f00b8d40 418
f00b8d40
SM
419if __name__ == '__main__':
420 unittest.main()
This page took 0.058033 seconds and 4 git commands to generate.