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.
24 from utils
import TestOutputPortMessageIterator
27 class UserMessageIteratorTestCase(unittest
.TestCase
):
29 def _create_graph(src_comp_cls
, flt_comp_cls
=None):
30 class MySink(bt2
._UserSinkComponent
):
31 def __init__(self
, params
, obj
):
32 self
._add
_input
_port
('in')
34 def _user_consume(self
):
37 def _user_graph_is_configured(self
):
38 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
39 self
._input
_ports
['in']
43 src_comp
= graph
.add_component(src_comp_cls
, 'src')
45 if flt_comp_cls
is not None:
46 flt_comp
= graph
.add_component(flt_comp_cls
, 'flt')
48 sink_comp
= graph
.add_component(MySink
, 'sink')
50 if flt_comp_cls
is not None:
51 assert flt_comp
is not None
53 src_comp
.output_ports
['out'], flt_comp
.input_ports
['in']
55 out_port
= flt_comp
.output_ports
['out']
57 out_port
= src_comp
.output_ports
['out']
59 graph
.connect_ports(out_port
, sink_comp
.input_ports
['in'])
63 the_output_port_from_source
= None
64 the_output_port_from_iter
= None
66 class MyIter(bt2
._UserMessageIterator
):
67 def __init__(self
, self_port_output
):
69 nonlocal the_output_port_from_iter
71 the_output_port_from_iter
= self_port_output
73 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
74 def __init__(self
, params
, obj
):
75 nonlocal the_output_port_from_source
76 the_output_port_from_source
= self
._add
_output
_port
('out', 'user data')
79 graph
= self
._create
_graph
(MySource
)
81 self
.assertTrue(initialized
)
83 the_output_port_from_source
.addr
, the_output_port_from_iter
.addr
85 self
.assertEqual(the_output_port_from_iter
.user_data
, 'user data')
87 def test_create_from_message_iterator(self
):
88 class MySourceIter(bt2
._UserMessageIterator
):
89 def __init__(self
, self_port_output
):
90 nonlocal src_iter_initialized
91 src_iter_initialized
= True
93 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
94 def __init__(self
, params
, obj
):
95 self
._add
_output
_port
('out')
97 class MyFilterIter(bt2
._UserMessageIterator
):
98 def __init__(self
, self_port_output
):
99 nonlocal flt_iter_initialized
100 flt_iter_initialized
= True
101 self
._up
_iter
= self
._create
_input
_port
_message
_iterator
(
102 self
._component
._input
_ports
['in']
106 return next(self
._up
_iter
)
108 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
109 def __init__(self
, params
, obj
):
110 self
._add
_input
_port
('in')
111 self
._add
_output
_port
('out')
113 src_iter_initialized
= False
114 flt_iter_initialized
= False
115 graph
= self
._create
_graph
(MySource
, MyFilter
)
117 self
.assertTrue(src_iter_initialized
)
118 self
.assertTrue(flt_iter_initialized
)
120 def test_finalize(self
):
121 class MyIter(bt2
._UserMessageIterator
):
122 def _user_finalize(self
):
126 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
127 def __init__(self
, params
, obj
):
128 self
._add
_output
_port
('out')
131 graph
= self
._create
_graph
(MySource
)
134 self
.assertTrue(finalized
)
136 def test_component(self
):
137 class MyIter(bt2
._UserMessageIterator
):
138 def __init__(self
, self_port_output
):
140 salut
= self
._component
._salut
142 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
143 def __init__(self
, params
, obj
):
144 self
._add
_output
_port
('out')
148 graph
= self
._create
_graph
(MySource
)
150 self
.assertEqual(salut
, 23)
153 class MyIter(bt2
._UserMessageIterator
):
154 def __init__(self
, self_port_output
):
158 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
159 def __init__(self
, params
, obj
):
160 self
._add
_output
_port
('out')
163 graph
= self
._create
_graph
(MySource
)
165 self
.assertIsNotNone(addr
)
166 self
.assertNotEqual(addr
, 0)
168 # Test that messages returned by _UserMessageIterator.__next__ remain valid
169 # and can be re-used.
170 def test_reuse_message(self
):
171 class MyIter(bt2
._UserMessageIterator
):
172 def __init__(self
, port
):
173 tc
, sc
, ec
= port
.user_data
175 stream
= trace
.create_stream(sc
)
176 packet
= stream
.create_packet()
178 # This message will be returned twice by __next__.
179 event_message
= self
._create
_event
_message
(ec
, packet
)
182 self
._create
_stream
_beginning
_message
(stream
),
183 self
._create
_packet
_beginning
_message
(packet
),
189 return self
._msgs
.pop(0)
191 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
192 def __init__(self
, params
, obj
):
193 tc
= self
._create
_trace
_class
()
194 sc
= tc
.create_stream_class(supports_packets
=True)
195 ec
= sc
.create_event_class()
196 self
._add
_output
_port
('out', (tc
, sc
, ec
))
199 src
= graph
.add_component(MySource
, 'src')
200 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
202 # Skip beginning messages.
204 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
206 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
211 self
.assertIsInstance(msg_ev1
, bt2
._EventMessage
)
212 self
.assertIsInstance(msg_ev2
, bt2
._EventMessage
)
213 self
.assertEqual(msg_ev1
.addr
, msg_ev2
.addr
)
216 def _setup_seek_beginning_test(sink_cls
):
217 # Use a source, a filter and an output port iterator. This allows us
218 # to test calling `seek_beginning` on both a _OutputPortMessageIterator
219 # and a _UserComponentInputPortMessageIterator, on top of checking that
220 # _UserMessageIterator._seek_beginning is properly called.
222 class MySourceIter(bt2
._UserMessageIterator
):
223 def __init__(self
, port
):
224 tc
, sc
, ec
= port
.user_data
226 stream
= trace
.create_stream(sc
)
227 packet
= stream
.create_packet()
230 self
._create
_stream
_beginning
_message
(stream
),
231 self
._create
_packet
_beginning
_message
(packet
),
232 self
._create
_event
_message
(ec
, packet
),
233 self
._create
_event
_message
(ec
, packet
),
234 self
._create
_packet
_end
_message
(packet
),
235 self
._create
_stream
_end
_message
(stream
),
239 def _user_seek_beginning(self
):
243 if self
._at
< len(self
._msgs
):
244 msg
= self
._msgs
[self
._at
]
250 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
251 def __init__(self
, params
, obj
):
252 tc
= self
._create
_trace
_class
()
253 sc
= tc
.create_stream_class(supports_packets
=True)
254 ec
= sc
.create_event_class()
256 self
._add
_output
_port
('out', (tc
, sc
, ec
))
258 class MyFilterIter(bt2
._UserMessageIterator
):
259 def __init__(self
, port
):
260 input_port
= port
.user_data
261 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
266 return next(self
._upstream
_iter
)
268 def _user_seek_beginning(self
):
269 self
._upstream
_iter
.seek_beginning()
272 def _user_can_seek_beginning(self
):
273 return self
._upstream
_iter
.can_seek_beginning
275 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
276 def __init__(self
, params
, obj
):
277 input_port
= self
._add
_input
_port
('in')
278 self
._add
_output
_port
('out', input_port
)
281 src
= graph
.add_component(MySource
, 'src')
282 flt
= graph
.add_component(MyFilter
, 'flt')
283 sink
= graph
.add_component(sink_cls
, 'sink')
284 graph
.connect_ports(src
.output_ports
['out'], flt
.input_ports
['in'])
285 graph
.connect_ports(flt
.output_ports
['out'], sink
.input_ports
['in'])
286 return MySourceIter
, graph
288 def test_can_seek_beginning(self
):
289 class MySink(bt2
._UserSinkComponent
):
290 def __init__(self
, params
, obj
):
291 self
._add
_input
_port
('in')
293 def _user_graph_is_configured(self
):
294 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
295 self
._input
_ports
['in']
298 def _user_consume(self
):
299 nonlocal can_seek_beginning
300 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
302 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
304 def _user_can_seek_beginning(self
):
305 nonlocal input_port_iter_can_seek_beginning
306 return input_port_iter_can_seek_beginning
308 MySourceIter
._user
_can
_seek
_beginning
= property(_user_can_seek_beginning
)
310 input_port_iter_can_seek_beginning
= True
311 can_seek_beginning
= None
313 self
.assertTrue(can_seek_beginning
)
315 input_port_iter_can_seek_beginning
= False
316 can_seek_beginning
= None
318 self
.assertFalse(can_seek_beginning
)
320 # Once can_seek_beginning returns an error, verify that it raises when
321 # _can_seek_beginning has/returns the wrong type.
323 # Remove the _can_seek_beginning method, we now rely on the presence of
324 # a _seek_beginning method to know whether the iterator can seek to
326 del MySourceIter
._user
_can
_seek
_beginning
327 can_seek_beginning
= None
329 self
.assertTrue(can_seek_beginning
)
331 del MySourceIter
._user
_seek
_beginning
332 can_seek_beginning
= None
334 self
.assertFalse(can_seek_beginning
)
336 def test_seek_beginning(self
):
337 class MySink(bt2
._UserSinkComponent
):
338 def __init__(self
, params
, obj
):
339 self
._add
_input
_port
('in')
341 def _user_graph_is_configured(self
):
342 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
343 self
._input
_ports
['in']
346 def _user_consume(self
):
347 nonlocal do_seek_beginning
350 if do_seek_beginning
:
351 self
._msg
_iter
.seek_beginning()
354 msg
= next(self
._msg
_iter
)
356 do_seek_beginning
= False
358 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
360 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
362 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
363 do_seek_beginning
= True
365 do_seek_beginning
= False
367 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
369 def test_seek_beginning_user_error(self
):
370 class MySink(bt2
._UserSinkComponent
):
371 def __init__(self
, params
, obj
):
372 self
._add
_input
_port
('in')
374 def _user_graph_is_configured(self
):
375 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
376 self
._input
_ports
['in']
379 def _user_consume(self
):
380 self
._msg
_iter
.seek_beginning()
382 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
384 def _user_seek_beginning_error(self
):
385 raise ValueError('ouch')
387 MySourceIter
._user
_seek
_beginning
= _user_seek_beginning_error
389 with self
.assertRaises(bt2
._Error
):
392 # Try consuming many times from an iterator that always returns TryAgain.
393 # This verifies that we are not missing an incref of Py_None, making the
394 # refcount of Py_None reach 0.
395 def test_try_again_many_times(self
):
396 class MyIter(bt2
._UserMessageIterator
):
400 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
401 def __init__(self
, params
, obj
):
402 self
._add
_output
_port
('out')
405 src
= graph
.add_component(MySource
, 'src')
406 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
408 # The initial refcount of Py_None was in the 7000, so 100000 iterations
409 # should be enough to catch the bug even if there are small differences
410 # between configurations.
411 for i
in range(100000):
412 with self
.assertRaises(bt2
.TryAgain
):
416 if __name__
== '__main__':