Commit | Line | Data |
---|---|---|
32d2d479 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 | ||
ae0bfae8 | 20 | from bt2 import value |
0d0edb5e | 21 | import collections |
f6a5e476 PP |
22 | import unittest |
23 | import copy | |
24 | import bt2 | |
25 | ||
26 | ||
fa4c33e3 | 27 | class UserMessageIteratorTestCase(unittest.TestCase): |
f6a5e476 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): | |
fa4c33e3 | 35 | next(self._msg_iter) |
f6a5e476 | 36 | |
a4dcfa96 SM |
37 | def _graph_is_configured(self): |
38 | self._msg_iter = self._input_ports['in'].create_message_iterator() | |
f6a5e476 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): | |
a4dcfa96 SM |
48 | the_output_port_from_source = None |
49 | the_output_port_from_iter = None | |
50 | ||
fa4c33e3 | 51 | class MyIter(bt2._UserMessageIterator): |
a4dcfa96 | 52 | def __init__(self, self_port_output): |
f6a5e476 | 53 | nonlocal initialized |
a4dcfa96 | 54 | nonlocal the_output_port_from_iter |
f6a5e476 | 55 | initialized = True |
a4dcfa96 | 56 | the_output_port_from_iter = self_port_output |
f6a5e476 PP |
57 | |
58 | class MySource(bt2._UserSourceComponent, | |
fa4c33e3 | 59 | message_iterator_class=MyIter): |
f6a5e476 | 60 | def __init__(self, params): |
a4dcfa96 | 61 | nonlocal the_output_port_from_source |
03ec9ebd | 62 | the_output_port_from_source = self._add_output_port('out', 'user data') |
f6a5e476 PP |
63 | |
64 | initialized = False | |
65 | graph = self._create_graph(MySource) | |
a4dcfa96 | 66 | graph.run() |
f6a5e476 | 67 | self.assertTrue(initialized) |
a4dcfa96 | 68 | self.assertEqual(the_output_port_from_source.addr, the_output_port_from_iter.addr) |
03ec9ebd | 69 | self.assertEqual(the_output_port_from_iter.user_data, 'user data') |
f6a5e476 PP |
70 | |
71 | def test_finalize(self): | |
fa4c33e3 | 72 | class MyIter(bt2._UserMessageIterator): |
f6a5e476 PP |
73 | def _finalize(self): |
74 | nonlocal finalized | |
75 | finalized = True | |
76 | ||
77 | class MySource(bt2._UserSourceComponent, | |
fa4c33e3 | 78 | message_iterator_class=MyIter): |
f6a5e476 PP |
79 | def __init__(self, params): |
80 | self._add_output_port('out') | |
81 | ||
82 | finalized = False | |
83 | graph = self._create_graph(MySource) | |
a4dcfa96 | 84 | graph.run() |
f6a5e476 PP |
85 | del graph |
86 | self.assertTrue(finalized) | |
87 | ||
88 | def test_component(self): | |
fa4c33e3 | 89 | class MyIter(bt2._UserMessageIterator): |
a4dcfa96 | 90 | def __init__(self, self_port_output): |
f6a5e476 PP |
91 | nonlocal salut |
92 | salut = self._component._salut | |
93 | ||
94 | class MySource(bt2._UserSourceComponent, | |
fa4c33e3 | 95 | message_iterator_class=MyIter): |
f6a5e476 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) | |
a4dcfa96 | 102 | graph.run() |
f6a5e476 PP |
103 | self.assertEqual(salut, 23) |
104 | ||
105 | def test_addr(self): | |
fa4c33e3 | 106 | class MyIter(bt2._UserMessageIterator): |
a4dcfa96 | 107 | def __init__(self, self_port_output): |
f6a5e476 PP |
108 | nonlocal addr |
109 | addr = self.addr | |
110 | ||
111 | class MySource(bt2._UserSourceComponent, | |
fa4c33e3 | 112 | message_iterator_class=MyIter): |
f6a5e476 PP |
113 | def __init__(self, params): |
114 | self._add_output_port('out') | |
115 | ||
116 | addr = None | |
117 | graph = self._create_graph(MySource) | |
a4dcfa96 | 118 | graph.run() |
f6a5e476 PP |
119 | self.assertIsNotNone(addr) |
120 | self.assertNotEqual(addr, 0) | |
121 | ||
4e853135 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), | |
137 | self._create_stream_activity_beginning_message(stream), | |
138 | self._create_packet_beginning_message(packet), | |
139 | event_message, | |
140 | event_message, | |
141 | ] | |
142 | ||
143 | def __next__(self): | |
144 | return self._msgs.pop(0) | |
145 | ||
146 | class MySource(bt2._UserSourceComponent, message_iterator_class=MyIter): | |
147 | def __init__(self, params): | |
148 | tc = self._create_trace_class() | |
149 | sc = tc.create_stream_class() | |
150 | ec = sc.create_event_class() | |
151 | self._add_output_port('out', (tc, sc, ec)) | |
152 | ||
153 | graph = bt2.Graph() | |
154 | src = graph.add_component(MySource, 'src') | |
155 | it = graph.create_output_port_message_iterator(src.output_ports['out']) | |
156 | ||
157 | # Skip beginning messages. | |
158 | next(it) | |
159 | next(it) | |
160 | next(it) | |
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 | ||
39ddfa44 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), | |
185 | self._create_stream_activity_beginning_message(stream), | |
186 | self._create_packet_beginning_message(packet), | |
187 | self._create_event_message(ec, packet), | |
188 | self._create_event_message(ec, packet), | |
189 | self._create_packet_end_message(packet), | |
190 | self._create_stream_activity_end_message(stream), | |
191 | self._create_stream_end_message(stream), | |
192 | ] | |
193 | self._at = 0 | |
194 | ||
195 | def _seek_beginning(self): | |
196 | self._at = 0 | |
197 | ||
198 | def __next__(self): | |
199 | if self._at < len(self._msgs): | |
200 | msg = self._msgs[self._at] | |
201 | self._at += 1 | |
202 | return msg | |
203 | else: | |
204 | raise StopIteration | |
205 | ||
206 | class MySource(bt2._UserSourceComponent, | |
207 | message_iterator_class=MySourceIter): | |
208 | def __init__(self, params): | |
209 | tc = self._create_trace_class() | |
210 | sc = tc.create_stream_class() | |
211 | ec = sc.create_event_class() | |
212 | ||
213 | self._add_output_port('out', (tc, sc, ec)) | |
214 | ||
215 | class MyFilterIter(bt2._UserMessageIterator): | |
216 | def __init__(self, port): | |
217 | input_port = port.user_data | |
218 | self._upstream_iter = input_port.create_message_iterator() | |
219 | ||
220 | def __next__(self): | |
221 | return next(self._upstream_iter) | |
222 | ||
223 | def _seek_beginning(self): | |
224 | self._upstream_iter.seek_beginning() | |
225 | ||
226 | @property | |
227 | def _can_seek_beginning(self): | |
228 | return self._upstream_iter.can_seek_beginning | |
229 | ||
230 | class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter): | |
231 | def __init__(self, params): | |
232 | input_port = self._add_input_port('in') | |
233 | self._add_output_port('out', input_port) | |
234 | ||
235 | ||
236 | graph = bt2.Graph() | |
237 | src = graph.add_component(MySource, 'src') | |
238 | flt = graph.add_component(MyFilter, 'flt') | |
239 | graph.connect_ports(src.output_ports['out'], flt.input_ports['in']) | |
240 | it = graph.create_output_port_message_iterator(flt.output_ports['out']) | |
241 | ||
242 | return it, MySourceIter | |
243 | ||
244 | def test_can_seek_beginning(self): | |
245 | it, MySourceIter = self._setup_seek_beginning_test() | |
246 | ||
247 | def _can_seek_beginning(self): | |
248 | nonlocal can_seek_beginning | |
249 | return can_seek_beginning | |
250 | ||
251 | MySourceIter._can_seek_beginning = property(_can_seek_beginning) | |
252 | ||
253 | can_seek_beginning = True | |
254 | self.assertTrue(it.can_seek_beginning) | |
255 | ||
256 | can_seek_beginning = False | |
257 | self.assertFalse(it.can_seek_beginning) | |
258 | ||
259 | # Once can_seek_beginning returns an error, verify that it raises when | |
260 | # _can_seek_beginning has/returns the wrong type. | |
261 | ||
262 | # Remove the _can_seek_beginning method, we now rely on the presence of | |
263 | # a _seek_beginning method to know whether the iterator can seek to | |
264 | # beginning or not. | |
265 | del MySourceIter._can_seek_beginning | |
266 | self.assertTrue(it.can_seek_beginning) | |
267 | ||
268 | del MySourceIter._seek_beginning | |
269 | self.assertFalse(it.can_seek_beginning) | |
270 | ||
271 | def test_seek_beginning(self): | |
272 | it, MySourceIter = self._setup_seek_beginning_test() | |
273 | ||
274 | msg = next(it) | |
275 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
276 | msg = next(it) | |
277 | self.assertIsInstance(msg, bt2.message._StreamActivityBeginningMessage) | |
278 | ||
279 | it.seek_beginning() | |
280 | ||
281 | msg = next(it) | |
282 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
283 | ||
284 | # Verify that we can seek beginning after having reached the end. | |
285 | # | |
286 | # It currently does not work to seek an output port message iterator | |
287 | # once it's ended, but we should eventually make it work and uncomment | |
288 | # the following snippet. | |
289 | # | |
290 | # try: | |
291 | # while True: | |
292 | # next(it) | |
293 | # except bt2.Stop: | |
294 | # pass | |
295 | # | |
296 | # it.seek_beginning() | |
297 | # msg = next(it) | |
298 | # self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
299 | ||
300 | def test_seek_beginning_user_error(self): | |
301 | it, MySourceIter = self._setup_seek_beginning_test() | |
302 | ||
303 | def _seek_beginning_error(self): | |
304 | raise ValueError('ouch') | |
305 | ||
306 | MySourceIter._seek_beginning = _seek_beginning_error | |
307 | ||
308 | with self.assertRaises(bt2.Error): | |
309 | it.seek_beginning() | |
310 | ||
311 | ||
f6a5e476 | 312 | |
fa4c33e3 | 313 | class OutputPortMessageIteratorTestCase(unittest.TestCase): |
0d0edb5e | 314 | def test_component(self): |
fa4c33e3 | 315 | class MyIter(bt2._UserMessageIterator): |
a4dcfa96 | 316 | def __init__(self, self_port_output): |
0d0edb5e PP |
317 | self._at = 0 |
318 | ||
0d0edb5e | 319 | def __next__(self): |
a4dcfa96 | 320 | if self._at == 7: |
0d0edb5e PP |
321 | raise bt2.Stop |
322 | ||
a4dcfa96 SM |
323 | if self._at == 0: |
324 | msg = self._create_stream_beginning_message(test_obj._stream) | |
325 | elif self._at == 1: | |
326 | msg = self._create_packet_beginning_message(test_obj._packet) | |
327 | elif self._at == 5: | |
328 | msg = self._create_packet_end_message(test_obj._packet) | |
329 | elif self._at == 6: | |
330 | msg = self._create_stream_end_message(test_obj._stream) | |
331 | else: | |
332 | msg = self._create_event_message(test_obj._event_class, test_obj._packet) | |
333 | msg.event.payload_field['my_int'] = self._at * 3 | |
334 | ||
0d0edb5e | 335 | self._at += 1 |
fa4c33e3 | 336 | return msg |
0d0edb5e PP |
337 | |
338 | class MySource(bt2._UserSourceComponent, | |
fa4c33e3 | 339 | message_iterator_class=MyIter): |
0d0edb5e PP |
340 | def __init__(self, params): |
341 | self._add_output_port('out') | |
342 | ||
a4dcfa96 SM |
343 | trace_class = self._create_trace_class() |
344 | stream_class = trace_class.create_stream_class() | |
345 | ||
346 | # Create payload field class | |
347 | my_int_ft = trace_class.create_signed_integer_field_class(32) | |
348 | payload_ft = trace_class.create_structure_field_class() | |
349 | payload_ft += collections.OrderedDict([ | |
350 | ('my_int', my_int_ft), | |
351 | ]) | |
352 | ||
353 | event_class = stream_class.create_event_class(name='salut', payload_field_class=payload_ft) | |
354 | ||
355 | trace = trace_class() | |
356 | stream = trace.create_stream(stream_class) | |
357 | packet = stream.create_packet() | |
358 | ||
359 | test_obj._event_class = event_class | |
360 | test_obj._stream = stream | |
361 | test_obj._packet = packet | |
362 | ||
363 | test_obj = self | |
0d0edb5e PP |
364 | graph = bt2.Graph() |
365 | src = graph.add_component(MySource, 'src') | |
a4dcfa96 | 366 | msg_iter = graph.create_output_port_message_iterator(src.output_ports['out']) |
0d0edb5e | 367 | |
fa4c33e3 | 368 | for at, msg in enumerate(msg_iter): |
a4dcfa96 SM |
369 | if at == 0: |
370 | self.assertIsInstance(msg, bt2.message._StreamBeginningMessage) | |
371 | elif at == 1: | |
372 | self.assertIsInstance(msg, bt2.message._PacketBeginningMessage) | |
373 | elif at == 5: | |
374 | self.assertIsInstance(msg, bt2.message._PacketEndMessage) | |
375 | elif at == 6: | |
376 | self.assertIsInstance(msg, bt2.message._StreamEndMessage) | |
377 | else: | |
378 | self.assertIsInstance(msg, bt2.message._EventMessage) | |
c88be1c8 | 379 | self.assertEqual(msg.event.cls.name, 'salut') |
a4dcfa96 SM |
380 | field = msg.event.payload_field['my_int'] |
381 | self.assertEqual(field, at * 3) | |
39ddfa44 SM |
382 | |
383 | if __name__ == '__main__': | |
384 | unittest.main() |