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.
25 from utils
import TestOutputPortMessageIterator
28 class UserMessageIteratorTestCase(unittest
.TestCase
):
30 def _create_graph(src_comp_cls
, flt_comp_cls
=None):
31 class MySink(bt2
._UserSinkComponent
):
32 def __init__(self
, params
, obj
):
33 self
._add
_input
_port
('in')
35 def _user_consume(self
):
38 def _user_graph_is_configured(self
):
39 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
40 self
._input
_ports
['in']
44 src_comp
= graph
.add_component(src_comp_cls
, 'src')
46 if flt_comp_cls
is not None:
47 flt_comp
= graph
.add_component(flt_comp_cls
, 'flt')
49 sink_comp
= graph
.add_component(MySink
, 'sink')
51 if flt_comp_cls
is not None:
52 assert flt_comp
is not None
54 src_comp
.output_ports
['out'], flt_comp
.input_ports
['in']
56 out_port
= flt_comp
.output_ports
['out']
58 out_port
= src_comp
.output_ports
['out']
60 graph
.connect_ports(out_port
, sink_comp
.input_ports
['in'])
64 the_output_port_from_source
= None
65 the_output_port_from_iter
= None
67 class MyIter(bt2
._UserMessageIterator
):
68 def __init__(self
, self_port_output
):
70 nonlocal the_output_port_from_iter
72 the_output_port_from_iter
= self_port_output
74 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
75 def __init__(self
, params
, obj
):
76 nonlocal the_output_port_from_source
77 the_output_port_from_source
= self
._add
_output
_port
('out', 'user data')
80 graph
= self
._create
_graph
(MySource
)
82 self
.assertTrue(initialized
)
84 the_output_port_from_source
.addr
, the_output_port_from_iter
.addr
86 self
.assertEqual(the_output_port_from_iter
.user_data
, 'user data')
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
94 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
95 def __init__(self
, params
, obj
):
96 self
._add
_output
_port
('out')
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']
107 return next(self
._up
_iter
)
109 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
110 def __init__(self
, params
, obj
):
111 self
._add
_input
_port
('in')
112 self
._add
_output
_port
('out')
114 src_iter_initialized
= False
115 flt_iter_initialized
= False
116 graph
= self
._create
_graph
(MySource
, MyFilter
)
118 self
.assertTrue(src_iter_initialized
)
119 self
.assertTrue(flt_iter_initialized
)
121 def test_finalize(self
):
122 class MyIter(bt2
._UserMessageIterator
):
123 def _user_finalize(self
):
127 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
128 def __init__(self
, params
, obj
):
129 self
._add
_output
_port
('out')
132 graph
= self
._create
_graph
(MySource
)
135 self
.assertTrue(finalized
)
137 def test_component(self
):
138 class MyIter(bt2
._UserMessageIterator
):
139 def __init__(self
, self_port_output
):
141 salut
= self
._component
._salut
143 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
144 def __init__(self
, params
, obj
):
145 self
._add
_output
_port
('out')
149 graph
= self
._create
_graph
(MySource
)
151 self
.assertEqual(salut
, 23)
154 class MyIter(bt2
._UserMessageIterator
):
155 def __init__(self
, self_port_output
):
159 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
160 def __init__(self
, params
, obj
):
161 self
._add
_output
_port
('out')
164 graph
= self
._create
_graph
(MySource
)
166 self
.assertIsNotNone(addr
)
167 self
.assertNotEqual(addr
, 0)
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
176 stream
= trace
.create_stream(sc
)
177 packet
= stream
.create_packet()
179 # This message will be returned twice by __next__.
180 event_message
= self
._create
_event
_message
(ec
, packet
)
183 self
._create
_stream
_beginning
_message
(stream
),
184 self
._create
_packet
_beginning
_message
(packet
),
190 return self
._msgs
.pop(0)
192 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
193 def __init__(self
, params
, obj
):
194 tc
= self
._create
_trace
_class
()
195 sc
= tc
.create_stream_class(supports_packets
=True)
196 ec
= sc
.create_event_class()
197 self
._add
_output
_port
('out', (tc
, sc
, ec
))
200 src
= graph
.add_component(MySource
, 'src')
201 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
203 # Skip beginning messages.
205 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
207 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
212 self
.assertIsInstance(msg_ev1
, bt2
._EventMessage
)
213 self
.assertIsInstance(msg_ev2
, bt2
._EventMessage
)
214 self
.assertEqual(msg_ev1
.addr
, msg_ev2
.addr
)
217 def _setup_seek_beginning_test(sink_cls
):
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.
223 class MySourceIter(bt2
._UserMessageIterator
):
224 def __init__(self
, port
):
225 tc
, sc
, ec
= port
.user_data
227 stream
= trace
.create_stream(sc
)
228 packet
= stream
.create_packet()
231 self
._create
_stream
_beginning
_message
(stream
),
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
),
236 self
._create
_stream
_end
_message
(stream
),
240 def _user_seek_beginning(self
):
244 if self
._at
< len(self
._msgs
):
245 msg
= self
._msgs
[self
._at
]
251 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
252 def __init__(self
, params
, obj
):
253 tc
= self
._create
_trace
_class
()
254 sc
= tc
.create_stream_class(supports_packets
=True)
255 ec
= sc
.create_event_class()
257 self
._add
_output
_port
('out', (tc
, sc
, ec
))
259 class MyFilterIter(bt2
._UserMessageIterator
):
260 def __init__(self
, port
):
261 input_port
= port
.user_data
262 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
267 return next(self
._upstream
_iter
)
269 def _user_seek_beginning(self
):
270 self
._upstream
_iter
.seek_beginning()
273 def _user_can_seek_beginning(self
):
274 return self
._upstream
_iter
.can_seek_beginning
276 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
277 def __init__(self
, params
, obj
):
278 input_port
= self
._add
_input
_port
('in')
279 self
._add
_output
_port
('out', input_port
)
282 src
= graph
.add_component(MySource
, 'src')
283 flt
= graph
.add_component(MyFilter
, 'flt')
284 sink
= graph
.add_component(sink_cls
, 'sink')
285 graph
.connect_ports(src
.output_ports
['out'], flt
.input_ports
['in'])
286 graph
.connect_ports(flt
.output_ports
['out'], sink
.input_ports
['in'])
287 return MySourceIter
, graph
289 def test_can_seek_beginning(self
):
290 class MySink(bt2
._UserSinkComponent
):
291 def __init__(self
, params
, obj
):
292 self
._add
_input
_port
('in')
294 def _user_graph_is_configured(self
):
295 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
296 self
._input
_ports
['in']
299 def _user_consume(self
):
300 nonlocal can_seek_beginning
301 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
303 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
305 def _user_can_seek_beginning(self
):
306 nonlocal input_port_iter_can_seek_beginning
307 return input_port_iter_can_seek_beginning
309 MySourceIter
._user
_can
_seek
_beginning
= property(_user_can_seek_beginning
)
311 input_port_iter_can_seek_beginning
= True
312 can_seek_beginning
= None
314 self
.assertTrue(can_seek_beginning
)
316 input_port_iter_can_seek_beginning
= False
317 can_seek_beginning
= None
319 self
.assertFalse(can_seek_beginning
)
321 # Once can_seek_beginning returns an error, verify that it raises when
322 # _can_seek_beginning has/returns the wrong type.
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
327 del MySourceIter
._user
_can
_seek
_beginning
328 can_seek_beginning
= None
330 self
.assertTrue(can_seek_beginning
)
332 del MySourceIter
._user
_seek
_beginning
333 can_seek_beginning
= None
335 self
.assertFalse(can_seek_beginning
)
337 def test_seek_beginning(self
):
338 class MySink(bt2
._UserSinkComponent
):
339 def __init__(self
, params
, obj
):
340 self
._add
_input
_port
('in')
342 def _user_graph_is_configured(self
):
343 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
344 self
._input
_ports
['in']
347 def _user_consume(self
):
348 nonlocal do_seek_beginning
351 if do_seek_beginning
:
352 self
._msg
_iter
.seek_beginning()
355 msg
= next(self
._msg
_iter
)
357 do_seek_beginning
= False
359 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
361 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
363 self
.assertIsInstance(msg
, bt2
._PacketBeginningMessage
)
364 do_seek_beginning
= True
366 do_seek_beginning
= False
368 self
.assertIsInstance(msg
, bt2
._StreamBeginningMessage
)
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')
375 def _user_graph_is_configured(self
):
376 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
377 self
._input
_ports
['in']
380 def _user_consume(self
):
381 self
._msg
_iter
.seek_beginning()
383 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
385 def _user_seek_beginning_error(self
):
386 raise ValueError('ouch')
388 MySourceIter
._user
_seek
_beginning
= _user_seek_beginning_error
390 with self
.assertRaises(bt2
._Error
):
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
):
401 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
402 def __init__(self
, params
, obj
):
403 self
._add
_output
_port
('out')
406 src
= graph
.add_component(MySource
, 'src')
407 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
409 # Three times the initial ref count of `None` iterations should
410 # be enough to catch the bug even if there are small differences
411 # between configurations.
412 none_ref_count
= sys
.getrefcount(None) * 3
414 for i
in range(none_ref_count
):
415 with self
.assertRaises(bt2
.TryAgain
):
419 if __name__
== '__main__':