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.
19 from bt2
import native_bt
24 class FailingIter(bt2
._UserMessageIterator
):
26 raise ValueError('User message iterator is failing')
29 class SourceWithFailingIter(
30 bt2
._UserSourceComponent
, message_iterator_class
=FailingIter
32 def __init__(self
, params
):
33 self
._add
_output
_port
('out')
36 class SourceWithFailingInit(
37 bt2
._UserSourceComponent
, message_iterator_class
=FailingIter
39 def __init__(self
, params
):
40 raise ValueError('Source is failing')
43 class WorkingSink(bt2
._UserSinkComponent
):
44 def __init__(self
, params
):
45 self
._in
= self
._add
_input
_port
('in')
47 def _graph_is_configured(self
):
48 self
._iter
= self
._in
.create_message_iterator()
54 class SinkWithExceptionChaining(bt2
._UserSinkComponent
):
55 def __init__(self
, params
):
56 self
._in
= self
._add
_input
_port
('in')
58 def _graph_is_configured(self
):
59 self
._iter
= self
._in
.create_message_iterator()
63 print(self
._iter
.__next
__)
65 except bt2
.Error
as e
:
68 raise ValueError('oops') from e
71 class SinkWithFailingQuery(bt2
._UserSinkComponent
):
72 def _graph_is_configured(self
):
79 def _query(executor
, obj
, params
, log_level
):
80 raise ValueError('Query is failing')
83 class ErrorTestCase(unittest
.TestCase
):
84 def _run_failing_graph(self
, source_cc
, sink_cc
):
85 with self
.assertRaises(bt2
.Error
) as ctx
:
87 src
= graph
.add_component(source_cc
, 'src')
88 snk
= graph
.add_component(sink_cc
, 'snk')
89 graph
.connect_ports(src
.output_ports
['out'], snk
.input_ports
['in'])
94 def test_current_thread_error_none(self
):
95 # When a bt2.Error is raised, it steals the current thread's error.
96 # Verify that it is now NULL.
97 exc
= self
._run
_failing
_graph
(SourceWithFailingInit
, WorkingSink
)
98 self
.assertIsNone(native_bt
.current_thread_take_error())
101 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
103 # The exact number of causes is not too important (it can change if we
104 # append more or less causes along the way), but the idea is to verify is
105 # has a value that makes sense.
106 self
.assertEqual(len(exc
), 4)
109 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
112 # Each cause is an instance of _ErrorCause (including subclasses).
113 self
.assertIsInstance(c
, bt2
.error
._ErrorCause
)
115 def test_getitem(self
):
116 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
118 for i
in range(len(exc
)):
120 # Each cause is an instance of _ErrorCause (including subclasses).
121 self
.assertIsInstance(c
, bt2
.error
._ErrorCause
)
123 def test_getitem_indexerror(self
):
124 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
126 with self
.assertRaises(IndexError):
129 def test_exception_chaining(self
):
130 # Test that if we do:
134 # except bt2.Error as exc:
135 # raise ValueError('oh noes') from exc
137 # We are able to fetch the causes of the original bt2.Error in the
138 # exception chain. Also, each exception in the chain should become one
140 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
142 self
.assertEqual(len(exc
), 5)
144 self
.assertIsInstance(exc
[0], bt2
.error
._MessageIteratorErrorCause
)
145 self
.assertEqual(exc
[0].component_class_name
, 'SourceWithFailingIter')
146 self
.assertIn('ValueError: User message iterator is failing', exc
[0].message
)
148 self
.assertIsInstance(exc
[1], bt2
.error
._ErrorCause
)
150 self
.assertIsInstance(exc
[2], bt2
.error
._ComponentErrorCause
)
151 self
.assertEqual(exc
[2].component_class_name
, 'SinkWithExceptionChaining')
153 'bt2.error.Error: unexpected error: cannot advance the message iterator',
157 self
.assertIsInstance(exc
[3], bt2
.error
._ComponentErrorCause
)
158 self
.assertEqual(exc
[3].component_class_name
, 'SinkWithExceptionChaining')
159 self
.assertIn('ValueError: oops', exc
[3].message
)
161 self
.assertIsInstance(exc
[4], bt2
.error
._ErrorCause
)
163 def _common_cause_tests(self
, cause
):
164 self
.assertIsInstance(cause
.module_name
, str)
165 self
.assertIsInstance(cause
.file_name
, str)
166 self
.assertIsInstance(cause
.line_number
, int)
168 def test_unknown_error_cause(self
):
169 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
171 self
.assertIs(type(cause
), bt2
.error
._ErrorCause
)
172 self
._common
_cause
_tests
(cause
)
174 def test_component_error_cause(self
):
175 exc
= self
._run
_failing
_graph
(SourceWithFailingInit
, SinkWithExceptionChaining
)
177 self
.assertIs(type(cause
), bt2
.error
._ComponentErrorCause
)
178 self
._common
_cause
_tests
(cause
)
180 self
.assertIn('Source is failing', cause
.message
)
181 self
.assertEqual(cause
.component_name
, 'src')
182 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SOURCE
)
183 self
.assertEqual(cause
.component_class_name
, 'SourceWithFailingInit')
184 self
.assertIsNone(cause
.plugin_name
)
186 def test_component_class_error_cause(self
):
187 q
= bt2
.QueryExecutor()
189 with self
.assertRaises(bt2
.Error
) as ctx
:
190 q
.query(SinkWithFailingQuery
, 'hello')
192 cause
= ctx
.exception
[0]
193 self
.assertIs(type(cause
), bt2
.error
._ComponentClassErrorCause
)
194 self
._common
_cause
_tests
(cause
)
196 self
.assertIn('Query is failing', cause
.message
)
198 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SINK
)
199 self
.assertEqual(cause
.component_class_name
, 'SinkWithFailingQuery')
200 self
.assertIsNone(cause
.plugin_name
)
202 def test_message_iterator_error_cause(self
):
203 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
205 self
.assertIs(type(cause
), bt2
.error
._MessageIteratorErrorCause
)
206 self
._common
_cause
_tests
(cause
)
208 self
.assertIn('User message iterator is failing', cause
.message
)
209 self
.assertEqual(cause
.component_name
, 'src')
210 self
.assertEqual(cause
.component_output_port_name
, 'out')
211 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SOURCE
)
212 self
.assertEqual(cause
.component_class_name
, 'SourceWithFailingIter')
213 self
.assertIsNone(cause
.plugin_name
)