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
._create
_input
_port
_message
_iterator
(self
._in
)
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
._create
_input
_port
_message
_iterator
(self
._in
)
64 except bt2
._Error
as e
:
65 raise ValueError('oops') from e
68 class SinkWithFailingQuery(bt2
._UserSinkComponent
):
69 def _graph_is_configured(self
):
76 def _query(executor
, obj
, params
, log_level
):
77 raise ValueError('Query is failing')
80 class ErrorTestCase(unittest
.TestCase
):
81 def _run_failing_graph(self
, source_cc
, sink_cc
):
82 with self
.assertRaises(bt2
._Error
) as ctx
:
84 src
= graph
.add_component(source_cc
, 'src')
85 snk
= graph
.add_component(sink_cc
, 'snk')
86 graph
.connect_ports(src
.output_ports
['out'], snk
.input_ports
['in'])
91 def test_current_thread_error_none(self
):
92 # When a bt2._Error is raised, it steals the current thread's error.
93 # Verify that it is now NULL.
94 exc
= self
._run
_failing
_graph
(SourceWithFailingInit
, WorkingSink
)
95 self
.assertIsNone(native_bt
.current_thread_take_error())
98 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
100 # The exact number of causes is not too important (it can change if we
101 # append more or less causes along the way), but the idea is to verify is
102 # has a value that makes sense.
103 self
.assertEqual(len(exc
), 4)
106 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
109 # Each cause is an instance of _ErrorCause (including subclasses).
110 self
.assertIsInstance(c
, bt2
._ErrorCause
)
112 def test_getitem(self
):
113 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
115 for i
in range(len(exc
)):
117 # Each cause is an instance of _ErrorCause (including subclasses).
118 self
.assertIsInstance(c
, bt2
._ErrorCause
)
120 def test_getitem_indexerror(self
):
121 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, WorkingSink
)
123 with self
.assertRaises(IndexError):
126 def test_exception_chaining(self
):
127 # Test that if we do:
131 # except bt2._Error as exc:
132 # raise ValueError('oh noes') from exc
134 # We are able to fetch the causes of the original bt2._Error in the
135 # exception chain. Also, each exception in the chain should become one
137 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
139 self
.assertEqual(len(exc
), 5)
141 self
.assertIsInstance(exc
[0], bt2
._MessageIteratorErrorCause
)
142 self
.assertEqual(exc
[0].component_class_name
, 'SourceWithFailingIter')
143 self
.assertIn('ValueError: User message iterator is failing', exc
[0].message
)
145 self
.assertIsInstance(exc
[1], bt2
._ErrorCause
)
147 self
.assertIsInstance(exc
[2], bt2
._ComponentErrorCause
)
148 self
.assertEqual(exc
[2].component_class_name
, 'SinkWithExceptionChaining')
150 'unexpected error: cannot advance the message iterator', exc
[2].message
153 self
.assertIsInstance(exc
[3], bt2
._ComponentErrorCause
)
154 self
.assertEqual(exc
[3].component_class_name
, 'SinkWithExceptionChaining')
155 self
.assertIn('ValueError: oops', exc
[3].message
)
157 self
.assertIsInstance(exc
[4], bt2
._ErrorCause
)
159 def _common_cause_tests(self
, cause
):
160 self
.assertIsInstance(cause
.module_name
, str)
161 self
.assertIsInstance(cause
.file_name
, str)
162 self
.assertIsInstance(cause
.line_number
, int)
164 def test_unknown_error_cause(self
):
165 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
167 self
.assertIs(type(cause
), bt2
._ErrorCause
)
168 self
._common
_cause
_tests
(cause
)
170 def test_component_error_cause(self
):
171 exc
= self
._run
_failing
_graph
(SourceWithFailingInit
, SinkWithExceptionChaining
)
173 self
.assertIs(type(cause
), bt2
._ComponentErrorCause
)
174 self
._common
_cause
_tests
(cause
)
176 self
.assertIn('Source is failing', cause
.message
)
177 self
.assertEqual(cause
.component_name
, 'src')
178 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SOURCE
)
179 self
.assertEqual(cause
.component_class_name
, 'SourceWithFailingInit')
180 self
.assertIsNone(cause
.plugin_name
)
182 def test_component_class_error_cause(self
):
183 q
= bt2
.QueryExecutor()
185 with self
.assertRaises(bt2
._Error
) as ctx
:
186 q
.query(SinkWithFailingQuery
, 'hello')
188 cause
= ctx
.exception
[0]
189 self
.assertIs(type(cause
), bt2
._ComponentClassErrorCause
)
190 self
._common
_cause
_tests
(cause
)
192 self
.assertIn('Query is failing', cause
.message
)
194 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SINK
)
195 self
.assertEqual(cause
.component_class_name
, 'SinkWithFailingQuery')
196 self
.assertIsNone(cause
.plugin_name
)
198 def test_message_iterator_error_cause(self
):
199 exc
= self
._run
_failing
_graph
(SourceWithFailingIter
, SinkWithExceptionChaining
)
201 self
.assertIs(type(cause
), bt2
._MessageIteratorErrorCause
)
202 self
._common
_cause
_tests
(cause
)
204 self
.assertIn('User message iterator is failing', cause
.message
)
205 self
.assertEqual(cause
.component_name
, 'src')
206 self
.assertEqual(cause
.component_output_port_name
, 'out')
207 self
.assertEqual(cause
.component_class_type
, bt2
.ComponentClassType
.SOURCE
)
208 self
.assertEqual(cause
.component_class_name
, 'SourceWithFailingIter')
209 self
.assertIsNone(cause
.plugin_name
)