Commit | Line | Data |
---|---|---|
d2d857a8 MJ |
1 | |
2 | # | |
3 | # Copyright (C) 2019 EfficiOS Inc. | |
4 | # | |
5 | # This program is free software; you can redistribute it and/or | |
6 | # modify it under the terms of the GNU General Public License | |
7 | # as published by the Free Software Foundation; only version 2 | |
8 | # of the License. | |
9 | # | |
10 | # This program is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | # GNU General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU General Public License | |
16 | # along with this program; if not, write to the Free Software | |
17 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | # | |
19 | ||
c4239792 | 20 | from bt2 import value |
548bb510 | 21 | import collections |
811644b8 PP |
22 | import unittest |
23 | import copy | |
24 | import bt2 | |
25 | ||
26 | ||
5602ef81 | 27 | class UserMessageIteratorTestCase(unittest.TestCase): |
811644b8 PP |
28 | @staticmethod |
29 | def _create_graph(src_comp_cls): | |
30 | class MySink(bt2._UserSinkComponent): | |
31 | def __init__(self, params): | |
32 | self._add_input_port('in') | |
33 | ||
34 | def _consume(self): | |
5602ef81 | 35 | next(self._msg_iter) |
811644b8 | 36 | |
c5f330cd SM |
37 | def _graph_is_configured(self): |
38 | self._msg_iter = self._input_ports['in'].create_message_iterator() | |
811644b8 PP |
39 | |
40 | graph = bt2.Graph() | |
41 | src_comp = graph.add_component(src_comp_cls, 'src') | |
42 | sink_comp = graph.add_component(MySink, 'sink') | |
43 | graph.connect_ports(src_comp.output_ports['out'], | |
44 | sink_comp.input_ports['in']) | |
45 | return graph | |
46 | ||
47 | def test_init(self): | |
c5f330cd SM |
48 | the_output_port_from_source = None |
49 | the_output_port_from_iter = None | |
50 | ||
5602ef81 | 51 | class MyIter(bt2._UserMessageIterator): |
c5f330cd | 52 | def __init__(self, self_port_output): |
811644b8 | 53 | nonlocal initialized |
c5f330cd | 54 | nonlocal the_output_port_from_iter |
811644b8 | 55 | initialized = True |
c5f330cd | 56 | the_output_port_from_iter = self_port_output |
811644b8 PP |
57 | |
58 | class MySource(bt2._UserSourceComponent, | |
5602ef81 | 59 | message_iterator_class=MyIter): |
811644b8 | 60 | def __init__(self, params): |
c5f330cd | 61 | nonlocal the_output_port_from_source |
2e00bc76 | 62 | the_output_port_from_source = self._add_output_port('out', 'user data') |
811644b8 PP |
63 | |
64 | initialized = False | |
65 | graph = self._create_graph(MySource) | |
c5f330cd | 66 | graph.run() |
811644b8 | 67 | self.assertTrue(initialized) |
c5f330cd | 68 | self.assertEqual(the_output_port_from_source.addr, the_output_port_from_iter.addr) |
2e00bc76 | 69 | self.assertEqual(the_output_port_from_iter.user_data, 'user data') |
811644b8 PP |
70 | |
71 | def test_finalize(self): | |
5602ef81 | 72 | class MyIter(bt2._UserMessageIterator): |
811644b8 PP |
73 | def _finalize(self): |
74 | nonlocal finalized | |
75 | finalized = True | |
76 | ||
77 | class MySource(bt2._UserSourceComponent, | |
5602ef81 | 78 | message_iterator_class=MyIter): |
811644b8 PP |
79 | def __init__(self, params): |
80 | self._add_output_port('out') | |
81 | ||
82 | finalized = False | |
83 | graph = self._create_graph(MySource) | |
c5f330cd | 84 | graph.run() |
811644b8 PP |
85 | del graph |
86 | self.assertTrue(finalized) | |
87 | ||
88 | def test_component(self): | |
5602ef81 | 89 | class MyIter(bt2._UserMessageIterator): |
c5f330cd | 90 | def __init__(self, self_port_output): |
811644b8 PP |
91 | nonlocal salut |
92 | salut = self._component._salut | |
93 | ||
94 | class MySource(bt2._UserSourceComponent, | |
5602ef81 | 95 | message_iterator_class=MyIter): |
811644b8 PP |
96 | def __init__(self, params): |
97 | self._add_output_port('out') | |
98 | self._salut = 23 | |
99 | ||
100 | salut = None | |
101 | graph = self._create_graph(MySource) | |
c5f330cd | 102 | graph.run() |
811644b8 PP |
103 | self.assertEqual(salut, 23) |
104 | ||
105 | def test_addr(self): | |
5602ef81 | 106 | class MyIter(bt2._UserMessageIterator): |
c5f330cd | 107 | def __init__(self, self_port_output): |
811644b8 PP |
108 | nonlocal addr |
109 | addr = self.addr | |
110 | ||
111 | class MySource(bt2._UserSourceComponent, | |
5602ef81 | 112 | message_iterator_class=MyIter): |
811644b8 PP |
113 | def __init__(self, params): |
114 | self._add_output_port('out') | |
115 | ||
116 | addr = None | |
117 | graph = self._create_graph(MySource) | |
c5f330cd | 118 | graph.run() |
811644b8 PP |
119 | self.assertIsNotNone(addr) |
120 | self.assertNotEqual(addr, 0) | |
121 | ||
d79a8353 SM |
122 | # Test that messages returned by _UserMessageIterator.__next__ remain valid |
123 | # and can be re-used. | |
124 | def test_reuse_message(self): | |
125 | class MyIter(bt2._UserMessageIterator): | |
126 | def __init__(self, port): | |
127 | tc, sc, ec = port.user_data | |
128 | trace = tc() | |
129 | stream = trace.create_stream(sc) | |
130 | packet = stream.create_packet() | |
131 | ||
132 | # This message will be returned twice by __next__. | |
133 | event_message = self._create_event_message(ec, packet) | |
134 | ||
135 | self._msgs = [ | |
136 | self._create_stream_beginning_message(stream), | |
d79a8353 SM |
137 | self._create_packet_beginning_message(packet), |
138 | event_message, | |
139 | event_message, | |
140 | ] | |
141 | ||
142 | def __next__(self): | |
143 | return self._msgs.pop(0) | |
144 | ||
145 | class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter): | |
146 | def __init__(self, params): | |
147 | tc = self._create_trace_class() | |
148 | sc = tc.create_stream_class() | |
149 | ec = sc.create_event_class() | |
150 | self._add_output_port('out', (tc, sc, ec)) | |
151 | ||
152 | graph = bt2.Graph() | |
153 | src = graph.add_component(MySource, 'src') | |
154 | it = graph.create_output_port_message_iterator(src.output_ports['out']) | |
155 | ||
156 | # Skip beginning messages. | |
188edac1 SM |
157 | msg = next(it) |
158 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
159 | msg = next(it) | |
160 | self.assertIsInstance(msg, bt2.message._PacketBeginningMessage) | |
d79a8353 SM |
161 | |
162 | msg_ev1 = next(it) | |
163 | msg_ev2 = next(it) | |
164 | ||
165 | self.assertIsInstance(msg_ev1, bt2.message._EventMessage) | |
166 | self.assertIsInstance(msg_ev2, bt2.message._EventMessage) | |
167 | self.assertEqual(msg_ev1.addr, msg_ev2.addr) | |
168 | ||
f00b8d40 SM |
169 | @staticmethod |
170 | def _setup_seek_beginning_test(): | |
171 | # Use a source, a filter and an output port iterator. This allows us | |
172 | # to test calling `seek_beginning` on both a _OutputPortMessageIterator | |
173 | # and a _UserComponentInputPortMessageIterator, on top of checking that | |
174 | # _UserMessageIterator._seek_beginning is properly called. | |
175 | ||
176 | class MySourceIter(bt2._UserMessageIterator): | |
177 | def __init__(self, port): | |
178 | tc, sc, ec = port.user_data | |
179 | trace = tc() | |
180 | stream = trace.create_stream(sc) | |
181 | packet = stream.create_packet() | |
182 | ||
183 | self._msgs = [ | |
184 | self._create_stream_beginning_message(stream), | |
f00b8d40 SM |
185 | self._create_packet_beginning_message(packet), |
186 | self._create_event_message(ec, packet), | |
187 | self._create_event_message(ec, packet), | |
188 | self._create_packet_end_message(packet), | |
f00b8d40 SM |
189 | self._create_stream_end_message(stream), |
190 | ] | |
191 | self._at = 0 | |
192 | ||
193 | def _seek_beginning(self): | |
194 | self._at = 0 | |
195 | ||
196 | def __next__(self): | |
197 | if self._at < len(self._msgs): | |
198 | msg = self._msgs[self._at] | |
199 | self._at += 1 | |
200 | return msg | |
201 | else: | |
202 | raise StopIteration | |
203 | ||
204 | class MySource(bt2._UserSourceComponent, | |
205 | message_iterator_class=MySourceIter): | |
206 | def __init__(self, params): | |
207 | tc = self._create_trace_class() | |
208 | sc = tc.create_stream_class() | |
209 | ec = sc.create_event_class() | |
210 | ||
211 | self._add_output_port('out', (tc, sc, ec)) | |
212 | ||
213 | class MyFilterIter(bt2._UserMessageIterator): | |
214 | def __init__(self, port): | |
215 | input_port = port.user_data | |
216 | self._upstream_iter = input_port.create_message_iterator() | |
217 | ||
218 | def __next__(self): | |
219 | return next(self._upstream_iter) | |
220 | ||
221 | def _seek_beginning(self): | |
222 | self._upstream_iter.seek_beginning() | |
223 | ||
224 | @property | |
225 | def _can_seek_beginning(self): | |
226 | return self._upstream_iter.can_seek_beginning | |
227 | ||
228 | class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter): | |
229 | def __init__(self, params): | |
230 | input_port = self._add_input_port('in') | |
231 | self._add_output_port('out', input_port) | |
232 | ||
233 | ||
234 | graph = bt2.Graph() | |
235 | src = graph.add_component(MySource, 'src') | |
236 | flt = graph.add_component(MyFilter, 'flt') | |
237 | graph.connect_ports(src.output_ports['out'], flt.input_ports['in']) | |
238 | it = graph.create_output_port_message_iterator(flt.output_ports['out']) | |
239 | ||
240 | return it, MySourceIter | |
241 | ||
242 | def test_can_seek_beginning(self): | |
243 | it, MySourceIter = self._setup_seek_beginning_test() | |
244 | ||
245 | def _can_seek_beginning(self): | |
246 | nonlocal can_seek_beginning | |
247 | return can_seek_beginning | |
248 | ||
249 | MySourceIter._can_seek_beginning = property(_can_seek_beginning) | |
250 | ||
251 | can_seek_beginning = True | |
252 | self.assertTrue(it.can_seek_beginning) | |
253 | ||
254 | can_seek_beginning = False | |
255 | self.assertFalse(it.can_seek_beginning) | |
256 | ||
257 | # Once can_seek_beginning returns an error, verify that it raises when | |
258 | # _can_seek_beginning has/returns the wrong type. | |
259 | ||
260 | # Remove the _can_seek_beginning method, we now rely on the presence of | |
261 | # a _seek_beginning method to know whether the iterator can seek to | |
262 | # beginning or not. | |
263 | del MySourceIter._can_seek_beginning | |
264 | self.assertTrue(it.can_seek_beginning) | |
265 | ||
266 | del MySourceIter._seek_beginning | |
267 | self.assertFalse(it.can_seek_beginning) | |
268 | ||
269 | def test_seek_beginning(self): | |
270 | it, MySourceIter = self._setup_seek_beginning_test() | |
271 | ||
272 | msg = next(it) | |
273 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
274 | msg = next(it) | |
188edac1 | 275 | self.assertIsInstance(msg, bt2.message._PacketBeginningMessage) |
f00b8d40 SM |
276 | |
277 | it.seek_beginning() | |
278 | ||
279 | msg = next(it) | |
280 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
281 | ||
282 | # Verify that we can seek beginning after having reached the end. | |
283 | # | |
284 | # It currently does not work to seek an output port message iterator | |
285 | # once it's ended, but we should eventually make it work and uncomment | |
286 | # the following snippet. | |
287 | # | |
288 | # try: | |
289 | # while True: | |
290 | # next(it) | |
291 | # except bt2.Stop: | |
292 | # pass | |
293 | # | |
294 | # it.seek_beginning() | |
295 | # msg = next(it) | |
296 | # self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
297 | ||
298 | def test_seek_beginning_user_error(self): | |
299 | it, MySourceIter = self._setup_seek_beginning_test() | |
300 | ||
301 | def _seek_beginning_error(self): | |
302 | raise ValueError('ouch') | |
303 | ||
304 | MySourceIter._seek_beginning = _seek_beginning_error | |
305 | ||
306 | with self.assertRaises(bt2.Error): | |
307 | it.seek_beginning() | |
308 | ||
309 | ||
811644b8 | 310 | |
5602ef81 | 311 | class OutputPortMessageIteratorTestCase(unittest.TestCase): |
548bb510 | 312 | def test_component(self): |
5602ef81 | 313 | class MyIter(bt2._UserMessageIterator): |
c5f330cd | 314 | def __init__(self, self_port_output): |
548bb510 PP |
315 | self._at = 0 |
316 | ||
548bb510 | 317 | def __next__(self): |
c5f330cd | 318 | if self._at == 7: |
548bb510 PP |
319 | raise bt2.Stop |
320 | ||
c5f330cd SM |
321 | if self._at == 0: |
322 | msg = self._create_stream_beginning_message(test_obj._stream) | |
323 | elif self._at == 1: | |
324 | msg = self._create_packet_beginning_message(test_obj._packet) | |
325 | elif self._at == 5: | |
326 | msg = self._create_packet_end_message(test_obj._packet) | |
327 | elif self._at == 6: | |
328 | msg = self._create_stream_end_message(test_obj._stream) | |
329 | else: | |
330 | msg = self._create_event_message(test_obj._event_class, test_obj._packet) | |
331 | msg.event.payload_field['my_int'] = self._at * 3 | |
332 | ||
548bb510 | 333 | self._at += 1 |
5602ef81 | 334 | return msg |
548bb510 PP |
335 | |
336 | class MySource(bt2._UserSourceComponent, | |
5602ef81 | 337 | message_iterator_class=MyIter): |
548bb510 PP |
338 | def __init__(self, params): |
339 | self._add_output_port('out') | |
340 | ||
c5f330cd SM |
341 | trace_class = self._create_trace_class() |
342 | stream_class = trace_class.create_stream_class() | |
343 | ||
344 | # Create payload field class | |
345 | my_int_ft = trace_class.create_signed_integer_field_class(32) | |
346 | payload_ft = trace_class.create_structure_field_class() | |
347 | payload_ft += collections.OrderedDict([ | |
348 | ('my_int', my_int_ft), | |
349 | ]) | |
350 | ||
351 | event_class = stream_class.create_event_class(name='salut', payload_field_class=payload_ft) | |
352 | ||
353 | trace = trace_class() | |
354 | stream = trace.create_stream(stream_class) | |
355 | packet = stream.create_packet() | |
356 | ||
357 | test_obj._event_class = event_class | |
358 | test_obj._stream = stream | |
359 | test_obj._packet = packet | |
360 | ||
361 | test_obj = self | |
548bb510 PP |
362 | graph = bt2.Graph() |
363 | src = graph.add_component(MySource, 'src') | |
c5f330cd | 364 | msg_iter = graph.create_output_port_message_iterator(src.output_ports['out']) |
548bb510 | 365 | |
5602ef81 | 366 | for at, msg in enumerate(msg_iter): |
c5f330cd SM |
367 | if at == 0: |
368 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
369 | elif at == 1: | |
370 | self.assertIsInstance(msg, bt2.message._PacketBeginningMessage) | |
371 | elif at == 5: | |
372 | self.assertIsInstance(msg, bt2.message._PacketEndMessage) | |
373 | elif at == 6: | |
374 | self.assertIsInstance(msg, bt2.message._StreamEndMessage) | |
375 | else: | |
376 | self.assertIsInstance(msg, bt2.message._EventMessage) | |
e8ac1aae | 377 | self.assertEqual(msg.event.cls.name, 'salut') |
c5f330cd SM |
378 | field = msg.event.payload_field['my_int'] |
379 | self.assertEqual(field, at * 3) | |
f00b8d40 SM |
380 | |
381 | if __name__ == '__main__': | |
382 | unittest.main() |