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
23 from bt2
import port
as bt2_port
26 class UserMessageIteratorTestCase(unittest
.TestCase
):
28 def _create_graph(src_comp_cls
, flt_comp_cls
=None):
29 class MySink(bt2
._UserSinkComponent
):
30 def __init__(self
, params
, obj
):
31 self
._add
_input
_port
('in')
33 def _user_consume(self
):
36 def _user_graph_is_configured(self
):
37 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
38 self
._input
_ports
['in']
42 src_comp
= graph
.add_component(src_comp_cls
, 'src')
44 if flt_comp_cls
is not None:
45 flt_comp
= graph
.add_component(flt_comp_cls
, 'flt')
47 sink_comp
= graph
.add_component(MySink
, 'sink')
49 if flt_comp_cls
is not None:
50 assert flt_comp
is not None
52 src_comp
.output_ports
['out'], flt_comp
.input_ports
['in']
54 out_port
= flt_comp
.output_ports
['out']
56 out_port
= src_comp
.output_ports
['out']
58 graph
.connect_ports(out_port
, sink_comp
.input_ports
['in'])
62 the_output_port_from_source
= None
63 the_output_port_from_iter
= None
65 class MyIter(bt2
._UserMessageIterator
):
66 def __init__(self
, self_port_output
):
68 nonlocal the_output_port_from_iter
70 the_output_port_from_iter
= self_port_output
72 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
73 def __init__(self
, params
, obj
):
74 nonlocal the_output_port_from_source
75 the_output_port_from_source
= self
._add
_output
_port
('out', 'user data')
78 graph
= self
._create
_graph
(MySource
)
80 self
.assertTrue(initialized
)
82 the_output_port_from_source
.addr
, the_output_port_from_iter
.addr
84 self
.assertEqual(the_output_port_from_iter
.user_data
, 'user data')
86 def test_create_from_message_iterator(self
):
87 class MySourceIter(bt2
._UserMessageIterator
):
88 def __init__(self
, self_port_output
):
89 nonlocal src_iter_initialized
90 src_iter_initialized
= True
92 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
93 def __init__(self
, params
, obj
):
94 self
._add
_output
_port
('out')
96 class MyFilterIter(bt2
._UserMessageIterator
):
97 def __init__(self
, self_port_output
):
98 nonlocal flt_iter_initialized
99 flt_iter_initialized
= True
100 self
._up
_iter
= self
._create
_input
_port
_message
_iterator
(
101 self
._component
._input
_ports
['in']
105 return next(self
._up
_iter
)
107 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
108 def __init__(self
, params
, obj
):
109 self
._add
_input
_port
('in')
110 self
._add
_output
_port
('out')
112 src_iter_initialized
= False
113 flt_iter_initialized
= False
114 graph
= self
._create
_graph
(MySource
, MyFilter
)
116 self
.assertTrue(src_iter_initialized
)
117 self
.assertTrue(flt_iter_initialized
)
119 def test_create_user_error(self
):
120 # This tests both error handling by
121 # _UserSinkComponent._create_input_port_message_iterator
122 # and _UserMessageIterator._create_input_port_message_iterator, as they
123 # are both used in the graph.
124 class MySourceIter(bt2
._UserMessageIterator
):
125 def __init__(self
, self_port_output
):
126 raise ValueError('Very bad error')
128 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
129 def __init__(self
, params
, obj
):
130 self
._add
_output
_port
('out')
132 class MyFilterIter(bt2
._UserMessageIterator
):
133 def __init__(self
, self_port_output
):
134 # This is expected to raise because of the error in
135 # MySourceIter.__init__.
136 self
._create
_input
_port
_message
_iterator
(
137 self
._component
._input
_ports
['in']
140 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
141 def __init__(self
, params
, obj
):
142 self
._add
_input
_port
('in')
143 self
._add
_output
_port
('out')
145 graph
= self
._create
_graph
(MySource
, MyFilter
)
147 with self
.assertRaises(bt2
._Error
) as ctx
:
153 self
.assertIsInstance(cause
, bt2
._MessageIteratorErrorCause
)
154 self
.assertEqual(cause
.component_name
, 'src')
155 self
.assertEqual(cause
.component_output_port_name
, 'out')
156 self
.assertIn('ValueError: Very bad error', cause
.message
)
158 def test_finalize(self
):
159 class MyIter(bt2
._UserMessageIterator
):
160 def _user_finalize(self
):
164 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
165 def __init__(self
, params
, obj
):
166 self
._add
_output
_port
('out')
169 graph
= self
._create
_graph
(MySource
)
172 self
.assertTrue(finalized
)
174 def test_component(self
):
175 class MyIter(bt2
._UserMessageIterator
):
176 def __init__(self
, self_port_output
):
178 salut
= self
._component
._salut
180 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
181 def __init__(self
, params
, obj
):
182 self
._add
_output
_port
('out')
186 graph
= self
._create
_graph
(MySource
)
188 self
.assertEqual(salut
, 23)
191 class MyIter(bt2
._UserMessageIterator
):
192 def __init__(self_iter
, self_port_output
):
195 port
= self_iter
._port
196 self
.assertIs(type(self_port_output
), bt2_port
._UserComponentOutputPort
)
197 self
.assertIs(type(port
), bt2_port
._UserComponentOutputPort
)
198 self
.assertEqual(self_port_output
.addr
, port
.addr
)
200 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
201 def __init__(self
, params
, obj
):
202 self
._add
_output
_port
('out')
205 graph
= self
._create
_graph
(MySource
)
207 self
.assertTrue(called
)
210 class MyIter(bt2
._UserMessageIterator
):
211 def __init__(self
, self_port_output
):
215 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
216 def __init__(self
, params
, obj
):
217 self
._add
_output
_port
('out')
220 graph
= self
._create
_graph
(MySource
)
222 self
.assertIsNotNone(addr
)
223 self
.assertNotEqual(addr
, 0)
225 # Test that messages returned by _UserMessageIterator.__next__ remain valid
226 # and can be re-used.
227 def test_reuse_message(self
):
228 class MyIter(bt2
._UserMessageIterator
):
229 def __init__(self
, port
):
230 tc
, sc
, ec
= port
.user_data
232 stream
= trace
.create_stream(sc
)
233 packet
= stream
.create_packet()
235 # This message will be returned twice by __next__.
236 event_message
= self
._create
_event
_message
(ec
, packet
)
239 self
._create
_stream
_beginning
_message
(stream
),
240 self
._create
_packet
_beginning
_message
(packet
),
246 return self
._msgs
.pop(0)
248 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
249 def __init__(self
, params
, obj
):
250 tc
= self
._create
_trace
_class
()
251 sc
= tc
.create_stream_class(supports_packets
=True)
252 ec
= sc
.create_event_class()
253 self
._add
_output
_port
('out', (tc
, sc
, ec
))
256 src
= graph
.add_component(MySource
, 'src')
257 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
259 # Skip beginning messages.
261 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
263 self
.assertIs(type(msg
), bt2
._PacketBeginningMessageConst
)
268 self
.assertIs(type(msg_ev1
), bt2
._EventMessageConst
)
269 self
.assertIs(type(msg_ev2
), bt2
._EventMessageConst
)
270 self
.assertEqual(msg_ev1
.addr
, msg_ev2
.addr
)
273 def _setup_seek_beginning_test(sink_cls
):
274 # Use a source, a filter and an output port iterator. This allows us
275 # to test calling `seek_beginning` on both a _OutputPortMessageIterator
276 # and a _UserComponentInputPortMessageIterator, on top of checking that
277 # _UserMessageIterator._seek_beginning is properly called.
279 class MySourceIter(bt2
._UserMessageIterator
):
280 def __init__(self
, port
):
281 tc
, sc
, ec
= port
.user_data
283 stream
= trace
.create_stream(sc
)
284 packet
= stream
.create_packet()
287 self
._create
_stream
_beginning
_message
(stream
),
288 self
._create
_packet
_beginning
_message
(packet
),
289 self
._create
_event
_message
(ec
, packet
),
290 self
._create
_event
_message
(ec
, packet
),
291 self
._create
_packet
_end
_message
(packet
),
292 self
._create
_stream
_end
_message
(stream
),
296 def _user_seek_beginning(self
):
300 if self
._at
< len(self
._msgs
):
301 msg
= self
._msgs
[self
._at
]
307 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
308 def __init__(self
, params
, obj
):
309 tc
= self
._create
_trace
_class
()
310 sc
= tc
.create_stream_class(supports_packets
=True)
311 ec
= sc
.create_event_class()
313 self
._add
_output
_port
('out', (tc
, sc
, ec
))
315 class MyFilterIter(bt2
._UserMessageIterator
):
316 def __init__(self
, port
):
317 input_port
= port
.user_data
318 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
323 return next(self
._upstream
_iter
)
325 def _user_seek_beginning(self
):
326 self
._upstream
_iter
.seek_beginning()
329 def _user_can_seek_beginning(self
):
330 return self
._upstream
_iter
.can_seek_beginning
332 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
333 def __init__(self
, params
, obj
):
334 input_port
= self
._add
_input
_port
('in')
335 self
._add
_output
_port
('out', input_port
)
338 src
= graph
.add_component(MySource
, 'src')
339 flt
= graph
.add_component(MyFilter
, 'flt')
340 sink
= graph
.add_component(sink_cls
, 'sink')
341 graph
.connect_ports(src
.output_ports
['out'], flt
.input_ports
['in'])
342 graph
.connect_ports(flt
.output_ports
['out'], sink
.input_ports
['in'])
343 return MySourceIter
, graph
345 def test_can_seek_beginning(self
):
346 class MySink(bt2
._UserSinkComponent
):
347 def __init__(self
, params
, obj
):
348 self
._add
_input
_port
('in')
350 def _user_graph_is_configured(self
):
351 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
352 self
._input
_ports
['in']
355 def _user_consume(self
):
356 nonlocal can_seek_beginning
357 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
359 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
361 def _user_can_seek_beginning(self
):
362 nonlocal input_port_iter_can_seek_beginning
363 return input_port_iter_can_seek_beginning
365 MySourceIter
._user
_can
_seek
_beginning
= property(_user_can_seek_beginning
)
367 input_port_iter_can_seek_beginning
= True
368 can_seek_beginning
= None
370 self
.assertTrue(can_seek_beginning
)
372 input_port_iter_can_seek_beginning
= False
373 can_seek_beginning
= None
375 self
.assertFalse(can_seek_beginning
)
377 # Once can_seek_beginning returns an error, verify that it raises when
378 # _can_seek_beginning has/returns the wrong type.
380 # Remove the _can_seek_beginning method, we now rely on the presence of
381 # a _seek_beginning method to know whether the iterator can seek to
383 del MySourceIter
._user
_can
_seek
_beginning
384 can_seek_beginning
= None
386 self
.assertTrue(can_seek_beginning
)
388 del MySourceIter
._user
_seek
_beginning
389 can_seek_beginning
= None
391 self
.assertFalse(can_seek_beginning
)
393 def test_seek_beginning(self
):
394 class MySink(bt2
._UserSinkComponent
):
395 def __init__(self
, params
, obj
):
396 self
._add
_input
_port
('in')
398 def _user_graph_is_configured(self
):
399 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
400 self
._input
_ports
['in']
403 def _user_consume(self
):
404 nonlocal do_seek_beginning
407 if do_seek_beginning
:
408 self
._msg
_iter
.seek_beginning()
411 msg
= next(self
._msg
_iter
)
413 do_seek_beginning
= False
415 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
417 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
419 self
.assertIs(type(msg
), bt2
._PacketBeginningMessageConst
)
420 do_seek_beginning
= True
422 do_seek_beginning
= False
424 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
426 def test_seek_beginning_user_error(self
):
427 class MySink(bt2
._UserSinkComponent
):
428 def __init__(self
, params
, obj
):
429 self
._add
_input
_port
('in')
431 def _user_graph_is_configured(self
):
432 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
433 self
._input
_ports
['in']
436 def _user_consume(self
):
437 self
._msg
_iter
.seek_beginning()
439 MySourceIter
, graph
= self
._setup
_seek
_beginning
_test
(MySink
)
441 def _user_seek_beginning_error(self
):
442 raise ValueError('ouch')
444 MySourceIter
._user
_seek
_beginning
= _user_seek_beginning_error
446 with self
.assertRaises(bt2
._Error
):
449 # Try consuming many times from an iterator that always returns TryAgain.
450 # This verifies that we are not missing an incref of Py_None, making the
451 # refcount of Py_None reach 0.
452 def test_try_again_many_times(self
):
453 class MyIter(bt2
._UserMessageIterator
):
457 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
458 def __init__(self
, params
, obj
):
459 self
._add
_output
_port
('out')
462 src
= graph
.add_component(MySource
, 'src')
463 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
465 # Three times the initial ref count of `None` iterations should
466 # be enough to catch the bug even if there are small differences
467 # between configurations.
468 none_ref_count
= sys
.getrefcount(None) * 3
470 for i
in range(none_ref_count
):
471 with self
.assertRaises(bt2
.TryAgain
):
475 if __name__
== '__main__':