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 SimpleSink(bt2
._UserSinkComponent
):
27 # Straightforward sink that creates one input port (`in`) and consumes from
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 def _create_graph(src_comp_cls
, sink_comp_cls
, flt_comp_cls
=None):
45 src_comp
= graph
.add_component(src_comp_cls
, 'src')
46 sink_comp
= graph
.add_component(sink_comp_cls
, 'sink')
48 if flt_comp_cls
is not None:
49 flt_comp
= graph
.add_component(flt_comp_cls
, 'flt')
50 graph
.connect_ports(src_comp
.output_ports
['out'], flt_comp
.input_ports
['in'])
51 graph
.connect_ports(flt_comp
.output_ports
['out'], sink_comp
.input_ports
['in'])
53 graph
.connect_ports(src_comp
.output_ports
['out'], sink_comp
.input_ports
['in'])
58 class UserMessageIteratorTestCase(unittest
.TestCase
):
60 the_output_port_from_source
= None
61 the_output_port_from_iter
= None
63 class MyIter(bt2
._UserMessageIterator
):
64 def __init__(self
, self_port_output
):
66 nonlocal the_output_port_from_iter
68 the_output_port_from_iter
= self_port_output
70 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
71 def __init__(self
, params
, obj
):
72 nonlocal the_output_port_from_source
73 the_output_port_from_source
= self
._add
_output
_port
('out', 'user data')
76 graph
= _create_graph(MySource
, SimpleSink
)
78 self
.assertTrue(initialized
)
80 the_output_port_from_source
.addr
, the_output_port_from_iter
.addr
82 self
.assertEqual(the_output_port_from_iter
.user_data
, 'user data')
84 def test_create_from_message_iterator(self
):
85 class MySourceIter(bt2
._UserMessageIterator
):
86 def __init__(self
, self_port_output
):
87 nonlocal src_iter_initialized
88 src_iter_initialized
= True
90 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
91 def __init__(self
, params
, obj
):
92 self
._add
_output
_port
('out')
94 class MyFilterIter(bt2
._UserMessageIterator
):
95 def __init__(self
, self_port_output
):
96 nonlocal flt_iter_initialized
97 flt_iter_initialized
= True
98 self
._up
_iter
= self
._create
_input
_port
_message
_iterator
(
99 self
._component
._input
_ports
['in']
103 return next(self
._up
_iter
)
105 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
106 def __init__(self
, params
, obj
):
107 self
._add
_input
_port
('in')
108 self
._add
_output
_port
('out')
110 src_iter_initialized
= False
111 flt_iter_initialized
= False
112 graph
= _create_graph(MySource
, SimpleSink
, MyFilter
)
114 self
.assertTrue(src_iter_initialized
)
115 self
.assertTrue(flt_iter_initialized
)
117 def test_create_user_error(self
):
118 # This tests both error handling by
119 # _UserSinkComponent._create_input_port_message_iterator
120 # and _UserMessageIterator._create_input_port_message_iterator, as they
121 # are both used in the graph.
122 class MySourceIter(bt2
._UserMessageIterator
):
123 def __init__(self
, self_port_output
):
124 raise ValueError('Very bad error')
126 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
127 def __init__(self
, params
, obj
):
128 self
._add
_output
_port
('out')
130 class MyFilterIter(bt2
._UserMessageIterator
):
131 def __init__(self
, self_port_output
):
132 # This is expected to raise because of the error in
133 # MySourceIter.__init__.
134 self
._create
_input
_port
_message
_iterator
(
135 self
._component
._input
_ports
['in']
138 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
139 def __init__(self
, params
, obj
):
140 self
._add
_input
_port
('in')
141 self
._add
_output
_port
('out')
143 graph
= _create_graph(MySource
, SimpleSink
, MyFilter
)
145 with self
.assertRaises(bt2
._Error
) as ctx
:
151 self
.assertIsInstance(cause
, bt2
._MessageIteratorErrorCause
)
152 self
.assertEqual(cause
.component_name
, 'src')
153 self
.assertEqual(cause
.component_output_port_name
, 'out')
154 self
.assertIn('ValueError: Very bad error', cause
.message
)
156 def test_finalize(self
):
157 class MyIter(bt2
._UserMessageIterator
):
158 def _user_finalize(self
):
162 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
163 def __init__(self
, params
, obj
):
164 self
._add
_output
_port
('out')
167 graph
= _create_graph(MySource
, SimpleSink
)
170 self
.assertTrue(finalized
)
172 def test_component(self
):
173 class MyIter(bt2
._UserMessageIterator
):
174 def __init__(self
, self_port_output
):
176 salut
= self
._component
._salut
178 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
179 def __init__(self
, params
, obj
):
180 self
._add
_output
_port
('out')
184 graph
= _create_graph(MySource
, SimpleSink
)
186 self
.assertEqual(salut
, 23)
189 class MyIter(bt2
._UserMessageIterator
):
190 def __init__(self_iter
, self_port_output
):
193 port
= self_iter
._port
194 self
.assertIs(type(self_port_output
), bt2_port
._UserComponentOutputPort
)
195 self
.assertIs(type(port
), bt2_port
._UserComponentOutputPort
)
196 self
.assertEqual(self_port_output
.addr
, port
.addr
)
198 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
199 def __init__(self
, params
, obj
):
200 self
._add
_output
_port
('out')
203 graph
= _create_graph(MySource
, SimpleSink
)
205 self
.assertTrue(called
)
208 class MyIter(bt2
._UserMessageIterator
):
209 def __init__(self
, self_port_output
):
213 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
214 def __init__(self
, params
, obj
):
215 self
._add
_output
_port
('out')
218 graph
= _create_graph(MySource
, SimpleSink
)
220 self
.assertIsNotNone(addr
)
221 self
.assertNotEqual(addr
, 0)
223 # Test that messages returned by _UserMessageIterator.__next__ remain valid
224 # and can be re-used.
225 def test_reuse_message(self
):
226 class MyIter(bt2
._UserMessageIterator
):
227 def __init__(self
, port
):
228 tc
, sc
, ec
= port
.user_data
230 stream
= trace
.create_stream(sc
)
231 packet
= stream
.create_packet()
233 # This message will be returned twice by __next__.
234 event_message
= self
._create
_event
_message
(ec
, packet
)
237 self
._create
_stream
_beginning
_message
(stream
),
238 self
._create
_packet
_beginning
_message
(packet
),
244 return self
._msgs
.pop(0)
246 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
247 def __init__(self
, params
, obj
):
248 tc
= self
._create
_trace
_class
()
249 sc
= tc
.create_stream_class(supports_packets
=True)
250 ec
= sc
.create_event_class()
251 self
._add
_output
_port
('out', (tc
, sc
, ec
))
254 src
= graph
.add_component(MySource
, 'src')
255 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
257 # Skip beginning messages.
259 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
261 self
.assertIs(type(msg
), bt2
._PacketBeginningMessageConst
)
266 self
.assertIs(type(msg_ev1
), bt2
._EventMessageConst
)
267 self
.assertIs(type(msg_ev2
), bt2
._EventMessageConst
)
268 self
.assertEqual(msg_ev1
.addr
, msg_ev2
.addr
)
270 # Try consuming many times from an iterator that always returns TryAgain.
271 # This verifies that we are not missing an incref of Py_None, making the
272 # refcount of Py_None reach 0.
273 def test_try_again_many_times(self
):
274 class MyIter(bt2
._UserMessageIterator
):
278 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MyIter
):
279 def __init__(self
, params
, obj
):
280 self
._add
_output
_port
('out')
282 class MyFilterIter(bt2
._UserMessageIterator
):
283 def __init__(self
, port
):
284 input_port
= port
.user_data
285 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
290 return next(self
._upstream
_iter
)
292 def _user_seek_beginning(self
):
293 self
._upstream
_iter
.seek_beginning()
296 def _user_can_seek_beginning(self
):
297 return self
._upstream
_iter
.can_seek_beginning
299 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
300 def __init__(self
, params
, obj
):
301 input_port
= self
._add
_input
_port
('in')
302 self
._add
_output
_port
('out', input_port
)
305 src
= graph
.add_component(MySource
, 'src')
306 it
= TestOutputPortMessageIterator(graph
, src
.output_ports
['out'])
308 # Three times the initial ref count of `None` iterations should
309 # be enough to catch the bug even if there are small differences
310 # between configurations.
311 none_ref_count
= sys
.getrefcount(None) * 3
313 for i
in range(none_ref_count
):
314 with self
.assertRaises(bt2
.TryAgain
):
318 def _setup_seek_test(sink_cls
, user_seek_beginning
=None, user_can_seek_beginning
=None):
319 class MySourceIter(bt2
._UserMessageIterator
):
320 def __init__(self
, port
):
321 tc
, sc
, ec
= port
.user_data
323 stream
= trace
.create_stream(sc
)
324 packet
= stream
.create_packet()
327 self
._create
_stream
_beginning
_message
(stream
),
328 self
._create
_packet
_beginning
_message
(packet
),
329 self
._create
_event
_message
(ec
, packet
),
330 self
._create
_event
_message
(ec
, packet
),
331 self
._create
_packet
_end
_message
(packet
),
332 self
._create
_stream
_end
_message
(stream
),
337 if self
._at
< len(self
._msgs
):
338 msg
= self
._msgs
[self
._at
]
344 if user_seek_beginning
is not None:
345 MySourceIter
._user
_seek
_beginning
= user_seek_beginning
347 if user_can_seek_beginning
is not None:
348 MySourceIter
._user
_can
_seek
_beginning
= property(user_can_seek_beginning
)
350 class MySource(bt2
._UserSourceComponent
, message_iterator_class
=MySourceIter
):
351 def __init__(self
, params
, obj
):
352 tc
= self
._create
_trace
_class
()
353 sc
= tc
.create_stream_class(supports_packets
=True)
354 ec
= sc
.create_event_class()
356 self
._add
_output
_port
('out', (tc
, sc
, ec
))
358 class MyFilterIter(bt2
._UserMessageIterator
):
359 def __init__(self
, port
):
360 self
._upstream
_iter
= self
._create
_input
_port
_message
_iterator
(
361 self
._component
._input
_ports
['in']
365 return next(self
._upstream
_iter
)
368 def _user_can_seek_beginning(self
):
369 return self
._upstream
_iter
.can_seek_beginning
371 def _user_seek_beginning(self
):
372 self
._upstream
_iter
.seek_beginning()
374 class MyFilter(bt2
._UserFilterComponent
, message_iterator_class
=MyFilterIter
):
375 def __init__(self
, params
, obj
):
376 self
._add
_input
_port
('in')
377 self
._add
_output
_port
('out')
379 return _create_graph(MySource
, sink_cls
, flt_comp_cls
=MyFilter
)
382 class UserMessageIteratorSeekBeginningTestCase(unittest
.TestCase
):
383 def test_can_seek_beginning(self
):
384 class MySink(bt2
._UserSinkComponent
):
385 def __init__(self
, params
, obj
):
386 self
._add
_input
_port
('in')
388 def _user_graph_is_configured(self
):
389 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
390 self
._input
_ports
['in']
393 def _user_consume(self
):
394 nonlocal can_seek_beginning
395 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
397 def _user_can_seek_beginning(self
):
398 nonlocal input_port_iter_can_seek_beginning
399 return input_port_iter_can_seek_beginning
401 graph
= _setup_seek_test(
402 MySink
, user_can_seek_beginning
=_user_can_seek_beginning
405 input_port_iter_can_seek_beginning
= True
406 can_seek_beginning
= None
408 self
.assertTrue(can_seek_beginning
)
410 input_port_iter_can_seek_beginning
= False
411 can_seek_beginning
= None
413 self
.assertFalse(can_seek_beginning
)
415 def test_no_can_seek_beginning_with_seek_beginning(self
):
416 # Test an iterator without a _user_can_seek_beginning method, but with
417 # a _user_seek_beginning method.
418 class MySink(bt2
._UserSinkComponent
):
419 def __init__(self
, params
, obj
):
420 self
._add
_input
_port
('in')
422 def _user_graph_is_configured(self
):
423 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
424 self
._input
_ports
['in']
427 def _user_consume(self
):
428 nonlocal can_seek_beginning
429 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
431 def _user_seek_beginning(self
):
434 graph
= _setup_seek_test(MySink
, user_seek_beginning
=_user_seek_beginning
)
435 can_seek_beginning
= None
437 self
.assertTrue(can_seek_beginning
)
439 def test_no_can_seek_beginning(self
):
440 # Test an iterator without a _user_can_seek_beginning method, without
441 # a _user_seek_beginning method.
442 class MySink(bt2
._UserSinkComponent
):
443 def __init__(self
, params
, obj
):
444 self
._add
_input
_port
('in')
446 def _user_graph_is_configured(self
):
447 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
448 self
._input
_ports
['in']
451 def _user_consume(self
):
452 nonlocal can_seek_beginning
453 can_seek_beginning
= self
._msg
_iter
.can_seek_beginning
455 graph
= _setup_seek_test(MySink
)
456 can_seek_beginning
= None
458 self
.assertFalse(can_seek_beginning
)
460 def test_can_seek_beginning_user_error(self
):
461 class MySink(bt2
._UserSinkComponent
):
462 def __init__(self
, params
, obj
):
463 self
._add
_input
_port
('in')
465 def _user_graph_is_configured(self
):
466 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
467 self
._input
_ports
['in']
470 def _user_consume(self
):
471 # This is expected to raise.
472 self
._msg
_iter
.can_seek_beginning
474 def _user_can_seek_beginning(self
):
475 raise ValueError('moustiquaire')
477 graph
= _setup_seek_test(
478 MySink
, user_can_seek_beginning
=_user_can_seek_beginning
481 with self
.assertRaises(bt2
._Error
) as ctx
:
484 cause
= ctx
.exception
[0]
485 self
.assertIn('ValueError: moustiquaire', cause
.message
)
487 def test_can_seek_beginning_wrong_return_value(self
):
488 class MySink(bt2
._UserSinkComponent
):
489 def __init__(self
, params
, obj
):
490 self
._add
_input
_port
('in')
492 def _user_graph_is_configured(self
):
493 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
494 self
._input
_ports
['in']
497 def _user_consume(self
):
498 # This is expected to raise.
499 self
._msg
_iter
.can_seek_beginning
501 def _user_can_seek_beginning(self
):
504 graph
= _setup_seek_test(
505 MySink
, user_can_seek_beginning
=_user_can_seek_beginning
508 with self
.assertRaises(bt2
._Error
) as ctx
:
511 cause
= ctx
.exception
[0]
512 self
.assertIn("TypeError: 'str' is not a 'bool' object", cause
.message
)
514 def test_seek_beginning(self
):
515 class MySink(bt2
._UserSinkComponent
):
516 def __init__(self
, params
, obj
):
517 self
._add
_input
_port
('in')
519 def _user_graph_is_configured(self
):
520 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
521 self
._input
_ports
['in']
524 def _user_consume(self
):
525 nonlocal do_seek_beginning
528 if do_seek_beginning
:
529 self
._msg
_iter
.seek_beginning()
532 msg
= next(self
._msg
_iter
)
534 def _user_seek_beginning(self
):
538 graph
= _setup_seek_test(MySink
, user_seek_beginning
=_user_seek_beginning
)
541 do_seek_beginning
= False
543 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
547 self
.assertIs(type(msg
), bt2
._PacketBeginningMessageConst
)
550 do_seek_beginning
= True
554 do_seek_beginning
= False
556 self
.assertIs(type(msg
), bt2
._StreamBeginningMessageConst
)
558 def test_seek_beginning_user_error(self
):
559 class MySink(bt2
._UserSinkComponent
):
560 def __init__(self
, params
, obj
):
561 self
._add
_input
_port
('in')
563 def _user_graph_is_configured(self
):
564 self
._msg
_iter
= self
._create
_input
_port
_message
_iterator
(
565 self
._input
_ports
['in']
568 def _user_consume(self
):
569 self
._msg
_iter
.seek_beginning()
571 def _user_seek_beginning(self
):
572 raise ValueError('ouch')
574 graph
= _setup_seek_test(MySink
, user_seek_beginning
=_user_seek_beginning
)
576 with self
.assertRaises(bt2
._Error
):
580 if __name__
== '__main__':