2 # Copyright (C) 2019 EfficiOS Inc.
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
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.
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.
22 from utils
import TestOutputPortMessageIterator
25 class UserMessageIteratorTestCase(unittest
.TestCase
):
27 def _create_graph(src_comp_cls
, flt_comp_cls
=None):
28 class MySink(bt2
._UserSinkComponent
):
29 def __init__(self
, params
, obj
):
30 self
._add
_input
_port
('in')
32 def _user_consume(self
):
35 def _user_graph_is_configured(self
):
36 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
37 self
._input
_ports
['in']
41 src_comp
= graph
.add_component(src_comp_cls
, 'src')
43 if flt_comp_cls
is not None:
44 flt_comp
= graph
.add_component(flt_comp_cls
, 'flt')
46 sink_comp
= graph
.add_component(MySink
, 'sink')
48 if flt_comp_cls
is not None:
49 assert flt_comp
is not None
51 src_comp
.output_ports
['out'], flt_comp
.input_ports
['in']
53 out_port
= flt_comp
.output_ports
['out']
55 out_port
= src_comp
.output_ports
['out']
57 graph
.connect_ports(out_port
, sink_comp
.input_ports
['in'])
61 the_output_port_from_source
= None
62 the_output_port_from_iter
= None
64 class MyIter(bt2
._UserMessageIterator
):
65 def __init__(self
, self_port_output
):
67 nonlocal the_output_port_from_iter
69 the_output_port_from_iter
= self_port_output
71 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
72 def __init__(self
, params
, obj
):
73 nonlocal the_output_port_from_source
74 the_output_port_from_source
= self
._add
_output
_port
('out', 'user data')
77 graph
= self
._create
_graph
(MySource
)
79 self
.assertTrue(initialized
)
81 the_output_port_from_source
.addr
, the_output_port_from_iter
.addr
83 self
.assertEqual(the_output_port_from_iter
.user_data
, 'user data')
85 def test_create_from_message_iterator(self
):
86 class MySourceIter(bt2
._UserMessageIterator
):
87 def __init__(self
, self_port_output
):
88 nonlocal src_iter_initialized
89 src_iter_initialized
= True
91 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
92 def __init__(self
, params
, obj
):
93 self
._add
_output
_port
('out')
95 class MyFilterIter(bt2
._UserMessageIterator
):
96 def __init__(self
, self_port_output
):
97 nonlocal flt_iter_initialized
98 flt_iter_initialized
= True
99 self
._up
_iter
= self
._create
_input
_port
_message
_iterator
(
100 self
._component
._input
_ports
['in']
104 return next(self
._up
_iter
)
106 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
107 def __init__(self
, params
, obj
):
108 self
._add
_input
_port
('in')
109 self
._add
_output
_port
('out')
111 src_iter_initialized
= False
112 flt_iter_initialized
= False
113 graph
= self
._create
_graph
(MySource
, MyFilter
)
115 self
.assertTrue(src_iter_initialized
)
116 self
.assertTrue(flt_iter_initialized
)
118 def test_create_user_error(self
):
119 # This tests both error handling by
120 # _UserSinkComponent._create_input_port_message_iterator
121 # and _UserMessageIterator._create_input_port_message_iterator, as they
122 # are both used in the graph.
123 class MySourceIter(bt2
._UserMessageIterator
):
124 def __init__(self
, self_port_output
):
125 raise ValueError('Very bad error')
127 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
128 def __init__(self
, params
, obj
):
129 self
._add
_output
_port
('out')
131 class MyFilterIter(bt2
._UserMessageIterator
):
132 def __init__(self
, self_port_output
):
133 # This is expected to raise because of the error in
134 # MySourceIter.__init__.
135 self
._create
_input
_port
_message
_iterator
(
136 self
._component
._input
_ports
['in']
139 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
140 def __init__(self
, params
, obj
):
141 self
._add
_input
_port
('in')
142 self
._add
_output
_port
('out')
144 graph
= self
._create
_graph
(MySource
, MyFilter
)
146 with self
.assertRaises(bt2
._Error
) as ctx
:
152 self
.assertIsInstance(cause
, bt2
._MessageIteratorErrorCause
)
153 self
.assertEqual(cause
.component_name
, 'src')
154 self
.assertEqual(cause
.component_output_port_name
, 'out')
155 self
.assertIn('ValueError: Very bad error', cause
.message
)
157 def test_finalize(self
):
158 class MyIter(bt2
._UserMessageIterator
):
159 def _user_finalize(self
):
163 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
164 def __init__(self
, params
, obj
):
165 self
._add
_output
_port
('out')
168 graph
= self
._create
_graph
(MySource
)
171 self
.assertTrue(finalized
)
173 def test_component(self
):
174 class MyIter(bt2
._UserMessageIterator
):
175 def __init__(self
, self_port_output
):
177 salut
= self
._component
._salut
179 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
180 def __init__(self
, params
, obj
):
181 self
._add
_output
_port
('out')
185 graph
= self
._create
_graph
(MySource
)
187 self
.assertEqual(salut
, 23)
190 class MyIter(bt2
._UserMessageIterator
):
191 def __init__(self
, self_port_output
):
195 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
196 def __init__(self
, params
, obj
):
197 self
._add
_output
_port
('out')
200 graph
= self
._create
_graph
(MySource
)
202 self
.assertIsNotNone(addr
)
203 self
.assertNotEqual(addr
, 0)
205 # Test that messages returned by _UserMessageIterator.__next__ remain valid
206 # and can be re-used.
207 def test_reuse_message(self
):
208 class MyIter(bt2
._UserMessageIterator
):
209 def __init__(self
, port
):
210 tc
, sc
, ec
= port
.user_data
212 stream
= trace
.create_stream(sc
)
213 packet
= stream
.create_packet()
215 # This message will be returned twice by __next__.
216 event_message
= self
._create
_event
_message
(ec
, packet
)
219 self
._create
_stream
_beginning
_message
(stream
),
220 self
._create
_packet
_beginning
_message
(packet
),
226 return self
._msgs
.pop(0)
228 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
229 def __init__(self
, params
, obj
):
230 tc
= self
._create
_trace
_class
()
231 sc
= tc
.create_stream_class(supports_packets
=True)
232 ec
= sc
.create_event_class()
233 self
._add
_output
_port
('out', (tc
, sc
, ec
))
236 src
= graph
.add_component(MySource
, 'src')
237 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
239 # Skip beginning messages.
241 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
243 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
248 self
.assertIsInstance(msg_ev1
, bt2
._EventMessage
)
249 self
.assertIsInstance(msg_ev2
, bt2
._EventMessage
)
250 self
.assertEqual(msg_ev1
.addr
, msg_ev2
.addr
)
253 def _setup_seek_beginning_test(sink_cls
):
254 # Use a source, a filter and an output port iterator. This allows us
255 # to test calling `seek_beginning` on both a _OutputPortMessageIterator
256 # and a _UserComponentInputPortMessageIterator, on top of checking that
257 # _UserMessageIterator._seek_beginning is properly called.
259 class MySourceIter(bt2
._UserMessageIterator
):
260 def __init__(self
, port
):
261 tc
, sc
, ec
= port
.user_data
263 stream
= trace
.create_stream(sc
)
264 packet
= stream
.create_packet()
267 self
._create
_stream
_beginning
_message
(stream
),
268 self
._create
_packet
_beginning
_message
(packet
),
269 self
._create
_event
_message
(ec
, packet
),
270 self
._create
_event
_message
(ec
, packet
),
271 self
._create
_packet
_end
_message
(packet
),
272 self
._create
_stream
_end
_message
(stream
),
276 def _user_seek_beginning(self
):
280 if self
._at
< len(self
._msgs
):
281 msg
= self
._msgs
[self
._at
]
287 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
288 def __init__(self
, params
, obj
):
289 tc
= self
._create
_trace
_class
()
290 sc
= tc
.create_stream_class(supports_packets
=True)
291 ec
= sc
.create_event_class()
293 self
._add
_output
_port
('out', (tc
, sc
, ec
))
295 class MyFilterIter(bt2
._UserMessageIterator
):
296 def __init__(self
, port
):
297 input_port
= port
.user_data
298 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
303 return next(self
._upstream
_iter
)
305 def _user_seek_beginning(self
):
306 self
._upstream
_iter
.seek_beginning()
309 def _user_can_seek_beginning(self
):
310 return self
._upstream
_iter
.can_seek_beginning
312 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
313 def __init__(self
, params
, obj
):
314 input_port
= self
._add
_input
_port
('in')
315 self
._add
_output
_port
('out', input_port
)
318 src
= graph
.add_component(MySource
, 'src')
319 flt
= graph
.add_component(MyFilter
, 'flt')
320 sink
= graph
.add_component(sink_cls
, 'sink')
321 graph
.connect_ports(src
.output_ports
['out'], flt
.input_ports
['in'])
322 graph
.connect_ports(flt
.output_ports
['out'], sink
.input_ports
['in'])
323 return MySourceIter
, graph
325 def test_can_seek_beginning(self
):
326 class MySink(bt2
._UserSinkComponent
):
327 def __init__(self
, params
, obj
):
328 self
._add
_input
_port
('in')
330 def _user_graph_is_configured(self
):
331 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
332 self
._input
_ports
['in']
335 def _user_consume(self
):
336 nonlocal can_seek_beginning
337 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
339 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
341 def _user_can_seek_beginning(self
):
342 nonlocal input_port_iter_can_seek_beginning
343 return input_port_iter_can_seek_beginning
345 MySourceIter
._user
_can
_seek
_beginning
= property(_user_can_seek_beginning
)
347 input_port_iter_can_seek_beginning
= True
348 can_seek_beginning
= None
350 self
.assertTrue(can_seek_beginning
)
352 input_port_iter_can_seek_beginning
= False
353 can_seek_beginning
= None
355 self
.assertFalse(can_seek_beginning
)
357 # Once can_seek_beginning returns an error, verify that it raises when
358 # _can_seek_beginning has/returns the wrong type.
360 # Remove the _can_seek_beginning method, we now rely on the presence of
361 # a _seek_beginning method to know whether the iterator can seek to
363 del MySourceIter
._user
_can
_seek
_beginning
364 can_seek_beginning
= None
366 self
.assertTrue(can_seek_beginning
)
368 del MySourceIter
._user
_seek
_beginning
369 can_seek_beginning
= None
371 self
.assertFalse(can_seek_beginning
)
373 def test_seek_beginning(self
):
374 class MySink(bt2
._UserSinkComponent
):
375 def __init__(self
, params
, obj
):
376 self
._add
_input
_port
('in')
378 def _user_graph_is_configured(self
):
379 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
380 self
._input
_ports
['in']
383 def _user_consume(self
):
384 nonlocal do_seek_beginning
387 if do_seek_beginning
:
388 self
._msg
_iter
.seek_beginning()
391 msg
= next(self
._msg
_iter
)
393 do_seek_beginning
= False
395 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
397 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
399 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
400 do_seek_beginning
= True
402 do_seek_beginning
= False
404 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
406 def test_seek_beginning_user_error(self
):
407 class MySink(bt2
._UserSinkComponent
):
408 def __init__(self
, params
, obj
):
409 self
._add
_input
_port
('in')
411 def _user_graph_is_configured(self
):
412 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
413 self
._input
_ports
['in']
416 def _user_consume(self
):
417 self
._msg
_iter
.seek_beginning()
419 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
421 def _user_seek_beginning_error(self
):
422 raise ValueError('ouch')
424 MySourceIter
._user
_seek
_beginning
= _user_seek_beginning_error
426 with self
.assertRaises(bt2
._Error
):
429 # Try consuming many times from an iterator that always returns TryAgain.
430 # This verifies that we are not missing an incref of Py_None, making the
431 # refcount of Py_None reach 0.
432 def test_try_again_many_times(self
):
433 class MyIter(bt2
._UserMessageIterator
):
437 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
438 def __init__(self
, params
, obj
):
439 self
._add
_output
_port
('out')
442 src
= graph
.add_component(MySource
, 'src')
443 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
445 # Three times the initial ref count of `None` iterations should
446 # be enough to catch the bug even if there are small differences
447 # between configurations.
448 none_ref_count
= sys
.getrefcount(None) * 3
450 for i
in range(none_ref_count
):
451 with self
.assertRaises(bt2
.TryAgain
):
455 if __name__
== '__main__':