Mir

Merge lp:~alan-griffiths/mir/fatal-error into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: kevin gunn
Approved revision: no longer in the source branch.
Merged at revision: 1822
Proposed branch: lp:~alan-griffiths/mir/fatal-error
Merge into: lp:mir
Diff against target: 527 lines (+292/-39)
10 files modified
include/platform/mir/fatal.h (+76/-0)
src/platform/CMakeLists.txt (+2/-0)
src/platform/fatal/CMakeLists.txt (+3/-0)
src/platform/fatal/fatal.cpp (+58/-0)
src/platform/graphics/mesa/CMakeLists.txt (+1/-2)
src/platform/graphics/mesa/display_buffer.cpp (+9/-8)
src/platform/graphics/mesa/real_kms_output.cpp (+18/-29)
tests/acceptance-tests/test_server_shutdown.cpp (+65/-0)
tests/unit-tests/CMakeLists.txt (+1/-0)
tests/unit-tests/test_fatal.cpp (+59/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/fatal-error
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel van Vugt Disapprove
Robert Carr (community) Approve
Cemil Azizoglu (community) Approve
Kevin DuBois (community) Approve
Alexandros Frantzis (community) Approve
Review via email: mp+229086@code.launchpad.net

Commit message

platform: provide support for customizing Mir's behavior when a fatal_error occurs.

Description of the change

platform: provide support for customizing Mir's behavior when a fatal_error occurs.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) Author info for a few files is incorrect:
   Author: Alan Griffiths <email address hidden>
Actually it was mostly copied from my branch, so please document as appropriate.

(2) The default action must be abort, not exceptions:
67 +void (*mir::fatal_error)(char const* reason, ...){&mir::fatal_error_except};

This is so that we can declare bug LP: #1316867 fixed and more importantly so that shell developers don't (by inaction) accidentally regress on it, preventing the creation of usable bug-reports. Catchable exceptions on unrecoverable errors must be an option at most, and never the default. Also keep in mind that we have upstart monitoring unity8 so it will restart cleanly either way. But only abort() gives you the extra bonus of usable bug-reports.

If abort() is not the default and not going to be always-on in usc and unity8 then there's no point in the proposal at all. It only gives you clean bug reports in debug builds and not in production (?!).

The good news is I have an alternative exceptions approach in mind that should make everyone happy. I'll prototype it when more important tasks are out the way.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

P.S. The new approach I have in mind is to remove all the blanket catch() statements and ensure we only catch things in Mir that we have logic to recover from. Everything else that is unrecoverable should pass straight through as an uncaught exception (with cleaner stack and usable bug reports in theory). This idea will work so long as the shell itself is not insane enough to catch exceptions it doesn't know how to handle properly.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

2) For all the reasons stated at great length on your previous proposal abort should *not* be the default action and must be /explicitly requested/ by users of the library.

1) As you wrote less than half the code in those two files; and you disagree with the rest of the team on on (2) I assumed you'd prefer not to be credited. But I'm happy to change them.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

Seems like a reasonable compromise to me, if the user wants us to behave badly for the purpose of maximizing debug info, they can set us up to abort, otherwise, we'd use the exception mechanism.

review: Approve
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

We can set up u-s-c and u8 to abort but as discussed previously mir should throw an exception by default.

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

LGTM. Aborting in libraries can be a pain for users (see: libdbus).

Not convinced all actions are really fatal errors, i.e. failed to schedule page flip, it's not recoverable, but why could it not be intermittent?

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) OK, yeah I don't approve of this new form so don't want to be listed as sole author. Although it's slightly inconsiderate considering the amount of code copied from my branch to list yourself first.

(2) Alright, you like exceptions and I like guaranteed post-mortem analysis. Let us agree to disagree.

So now I have an additional question: What does this branch solve or fix? Certainly not the bug I set out to fix in: https://code.launchpad.net/~vanvugt/mir/fatal-error/+merge/219471
Unless you explicitly turn "abort" on in downstream projects...

If abort isn't turned on by default then following simple logic, either:
  (a) USC will want to turn it on always. So there's no reason to not make it the default in Mir for safety's sake, and go with my original proposal; or
  (b) USC will not want to turn it on. This leads to no bugs being fixed. No improvement on the current situation. So why does this proposal exist at all?

I'm not trying to be negative. The reasoning doesn't seem add up. Is it (a) or (b), or am I foolish and you all know about an option (c) I'm not seeing?

review: Disapprove
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

An in case anyone thinks (c) is to turn abort on after a crash and retry, that's just silly. You can't go back and ask users to reproduce a bug they generally can't reproduce on demand. I know this from many years of joining projects where developers believed that and it was simply proven to be ineffective in every case. You have to catch crashes with a clean bug-report the first time. Because there typically either won't be a second time for any given user, or it will be so long in the future that you've left the bug in the wild for too long and they've given up on your product completely.

Serious bugs need to generate clean crash reports the first time with no special configuration or preparation by the user.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

racarr: Absolutely everything should be recoverable (like failure to schedule a page flip). We just lack such logic at the moment. The "fatal-error" approach is a temporary solution until smarter recovery logic exists in the failing module(s). And an important one because it gives us vital feedback in the form of clean bug reports showing us how such an apparently impossible situation occurred.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> (1) OK, yeah I don't approve of this new form so don't want to be listed as
> sole author. Although it's slightly inconsiderate considering the amount of
> code copied from my branch to list yourself first.

Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'include/platform/mir/fatal.h'
2--- include/platform/mir/fatal.h 1970-01-01 00:00:00 +0000
3+++ include/platform/mir/fatal.h 2014-08-05 11:38:58 +0000
4@@ -0,0 +1,76 @@
5+/*
6+ * Copyright © 2014 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify
9+ * it under the terms of the GNU Lesser General Public License version 3 as
10+ * published by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ * ---
21+ * Fatal error handling - Fatal errors are situations we don't expect to ever
22+ * happen and don't have logic to gracefully recover from. The most useful
23+ * thing you can do in that situation is abort to get a clean core file and
24+ * stack trace to maximize the chances of it being readable.
25+ *
26+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
27+ * Alan Griffiths <alan@octopull.co.uk>
28+ */
29+
30+#ifndef MIR_FATAL_H_
31+#define MIR_FATAL_H_
32+
33+namespace mir
34+{
35+/**
36+ * fatal_error() is strictly for "this should never happen" situations that
37+ * you cannot recover from. By default it points at fatal_error_except().
38+ * Note the reason parameter is a simple char* so its value is clearly visible
39+ * in stack trace output.
40+ * \param [in] reason A printf-style format string.
41+ */
42+extern void (*fatal_error)(char const* reason, ...);
43+
44+/**
45+ * Throws an exception that will typically kill the Mir server and propagate from
46+ * mir::run_mir.
47+ * \param [in] reason A printf-style format string.
48+ */
49+void fatal_error_except(char const* reason, ...);
50+
51+/**
52+ * An alternative to fatal_error_except() that kills the program and dump core
53+ * as cleanly as possible.
54+ * \param [in] reason A printf-style format string.
55+ */
56+void fatal_error_abort(char const* reason, ...);
57+
58+// Utility class to override & restore existing error handler
59+class FatalErrorStrategy
60+{
61+public:
62+ explicit FatalErrorStrategy(void (*fatal_error_handler)(char const* reason, ...)) :
63+ old_fatal_error_handler(fatal_error)
64+ {
65+ fatal_error = fatal_error_handler;
66+ }
67+
68+ ~FatalErrorStrategy()
69+ {
70+ fatal_error = old_fatal_error_handler;
71+ }
72+
73+private:
74+ void (*old_fatal_error_handler)(char const* reason, ...);
75+ FatalErrorStrategy(FatalErrorStrategy const&) = delete;
76+ FatalErrorStrategy& operator=(FatalErrorStrategy const&) = delete;
77+};
78+} // namespace mir
79+
80+#endif // MIR_FATAL_H_
81
82=== modified file 'src/platform/CMakeLists.txt'
83--- src/platform/CMakeLists.txt 2014-07-24 13:44:09 +0000
84+++ src/platform/CMakeLists.txt 2014-08-05 11:38:58 +0000
85@@ -5,6 +5,7 @@
86 shared_library_loader.cpp
87 $<TARGET_OBJECTS:mirplatformgraphicscommon>
88 $<TARGET_OBJECTS:miroptions>
89+ $<TARGET_OBJECTS:mirfatal>
90 )
91
92 target_link_libraries(mirplatform
93@@ -17,3 +18,4 @@
94
95 add_subdirectory(graphics/)
96 add_subdirectory(options)
97+add_subdirectory(fatal)
98
99=== added directory 'src/platform/fatal'
100=== added file 'src/platform/fatal/CMakeLists.txt'
101--- src/platform/fatal/CMakeLists.txt 1970-01-01 00:00:00 +0000
102+++ src/platform/fatal/CMakeLists.txt 2014-08-05 11:38:58 +0000
103@@ -0,0 +1,3 @@
104+add_library(mirfatal OBJECT
105+ fatal.cpp
106+)
107
108=== added file 'src/platform/fatal/fatal.cpp'
109--- src/platform/fatal/fatal.cpp 1970-01-01 00:00:00 +0000
110+++ src/platform/fatal/fatal.cpp 2014-08-05 11:38:58 +0000
111@@ -0,0 +1,58 @@
112+/*
113+ * Copyright © 2014 Canonical Ltd.
114+ *
115+ * This program is free software: you can redistribute it and/or modify it
116+ * under the terms of the GNU Lesser General Public License version 3,
117+ * as published by the Free Software Foundation.
118+ *
119+ * This program is distributed in the hope that it will be useful,
120+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
121+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122+ * GNU Lesser General Public License for more details.
123+ *
124+ * You should have received a copy of the GNU Lesser General Public License
125+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
126+ *
127+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
128+ * Alan Griffiths <alan@octopull.co.uk>
129+ */
130+
131+#include "mir/fatal.h"
132+
133+#include <boost/throw_exception.hpp>
134+#include <boost/exception/info.hpp>
135+
136+#include <stdexcept>
137+#include <cstdlib>
138+#include <cstdio>
139+#include <cstdarg>
140+
141+void mir::fatal_error_abort(char const* reason, ...)
142+{
143+ va_list args;
144+
145+ // Keep this as simple as possible, avoiding any object construction and
146+ // minimizing the potential for heap operations between the error location
147+ // and the abort().
148+ va_start(args, reason);
149+ fprintf(stderr, "Mir fatal error: ");
150+ vfprintf(stderr, reason, args);
151+ fprintf(stderr, "\n");
152+ va_end(args);
153+
154+ std::abort();
155+}
156+
157+void mir::fatal_error_except(char const* reason, ...)
158+{
159+ char buffer[1024];
160+ va_list args;
161+
162+ va_start(args, reason);
163+ vsnprintf(buffer, sizeof buffer, reason, args);
164+ va_end(args);
165+
166+ BOOST_THROW_EXCEPTION(std::runtime_error(buffer));
167+}
168+
169+void (*mir::fatal_error)(char const* reason, ...){&mir::fatal_error_except};
170
171=== modified file 'src/platform/graphics/mesa/CMakeLists.txt'
172--- src/platform/graphics/mesa/CMakeLists.txt 2014-07-29 15:42:47 +0000
173+++ src/platform/graphics/mesa/CMakeLists.txt 2014-08-05 11:38:58 +0000
174@@ -46,8 +46,7 @@
175 LINK_FLAGS "-Wl,--exclude-libs=ALL -Wl,--version-script,${symbol_map}"
176 )
177
178-target_link_libraries(
179- mirplatformgraphicsmesa
180+target_link_libraries(mirplatformgraphicsmesa
181 mirplatform
182
183 ${Boost_PROGRAM_OPTIONS_LIBRARY}
184
185=== modified file 'src/platform/graphics/mesa/display_buffer.cpp'
186--- src/platform/graphics/mesa/display_buffer.cpp 2014-07-09 08:22:57 +0000
187+++ src/platform/graphics/mesa/display_buffer.cpp 2014-08-05 11:38:58 +0000
188@@ -22,6 +22,7 @@
189 #include "mir/graphics/display_report.h"
190 #include "bypass.h"
191 #include "gbm_buffer.h"
192+#include "mir/fatal.h"
193
194 #include <boost/throw_exception.hpp>
195 #include <GLES2/gl2.h>
196@@ -143,18 +144,18 @@
197 glClear(GL_COLOR_BUFFER_BIT);
198
199 if (!egl.swap_buffers())
200- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to perform initial surface buffer swap"));
201+ fatal_error("Failed to perform initial surface buffer swap");
202
203 listener->report_successful_egl_buffer_swap_on_construction();
204
205 scheduled_bufobj = get_front_buffer_object();
206 if (!scheduled_bufobj)
207- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get frontbuffer"));
208+ fatal_error("Failed to get frontbuffer");
209
210 for (auto& output : outputs)
211 {
212 if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))
213- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));
214+ fatal_error("Failed to set DRM crtc");
215 }
216
217 egl.release_current();
218@@ -256,7 +257,7 @@
219 * corresponding to the front buffer.
220 */
221 if (!bypass_buf && !egl.swap_buffers())
222- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to perform initial surface buffer swap"));
223+ fatal_error("Failed to perform initial surface buffer swap");
224
225 mgm::BufferObject *bufobj;
226 if (bypass_buf)
227@@ -271,7 +272,7 @@
228 }
229
230 if (!bufobj)
231- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get front buffer object"));
232+ fatal_error("Failed to get front buffer object");
233
234 /*
235 * Schedule the current front buffer object for display, and wait
236@@ -284,14 +285,14 @@
237 {
238 if (!bypass_buf)
239 bufobj->release();
240- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to schedule page flip"));
241+ fatal_error("Failed to schedule page flip");
242 }
243 else if (needs_set_crtc)
244 {
245 for (auto& output : outputs)
246 {
247 if (!output->set_crtc(bufobj->get_drm_fb_id()))
248- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));
249+ fatal_error("Failed to set DRM crtc");
250 }
251 needs_set_crtc = false;
252 }
253@@ -397,7 +398,7 @@
254 {
255 if (!egl.make_current())
256 {
257- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to make EGL surface current"));
258+ fatal_error("Failed to make EGL surface current");
259 }
260 }
261
262
263=== modified file 'src/platform/graphics/mesa/real_kms_output.cpp'
264--- src/platform/graphics/mesa/real_kms_output.cpp 2014-07-09 10:48:47 +0000
265+++ src/platform/graphics/mesa/real_kms_output.cpp 2014-08-05 11:38:58 +0000
266@@ -18,11 +18,7 @@
267
268 #include "real_kms_output.h"
269 #include "page_flipper.h"
270-
271-#include <boost/throw_exception.hpp>
272-#include <boost/exception/info.hpp>
273-
274-#include <stdexcept>
275+#include "mir/fatal.h"
276 #include <vector>
277 #include <string.h> // strcmp
278
279@@ -166,7 +162,7 @@
280 connector = resources.connector(connector_id);
281
282 if (!connector)
283- BOOST_THROW_EXCEPTION(std::runtime_error("No DRM connector found\n"));
284+ fatal_error("No DRM connector found");
285
286 // TODO: What if we can't locate the DPMS property?
287 for (int i = 0; i < connector->count_props; i++)
288@@ -202,9 +198,10 @@
289 bool mgm::RealKMSOutput::set_crtc(uint32_t fb_id)
290 {
291 if (!ensure_crtc())
292- BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
293- connector_name(connector.get()) +
294- " has no associated CRTC to set a framebuffer on"));
295+ {
296+ fatal_error("Output %s has no associated CRTC to set a framebuffer on",
297+ connector_name(connector.get()).c_str());
298+ }
299
300 auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
301 fb_id, fb_offset.dx.as_int(), fb_offset.dy.as_int(),
302@@ -235,12 +232,8 @@
303 0, 0, 0, nullptr, 0, nullptr);
304 if (result)
305 {
306- std::string const msg =
307- "Couldn't clear output " + connector_name(connector.get());
308-
309- BOOST_THROW_EXCEPTION(
310- ::boost::enable_error_info(std::runtime_error(msg))
311- << (boost::error_info<KMSOutput, decltype(result)>(result)));
312+ fatal_error("Couldn't clear output %s (drmModeSetCrtc = %d)",
313+ connector_name(connector.get()).c_str(), result);
314 }
315
316 current_crtc = nullptr;
317@@ -252,10 +245,10 @@
318 if (power_mode != mir_power_mode_on)
319 return true;
320 if (!current_crtc)
321- BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
322- connector_name(connector.get()) +
323- " has no associated CRTC to schedule page flips on"));
324-
325+ {
326+ fatal_error("Output %s has no associated CRTC to schedule page flips on",
327+ connector_name(connector.get()).c_str());
328+ }
329 return page_flipper->schedule_flip(current_crtc->crtc_id, fb_id);
330 }
331
332@@ -265,10 +258,10 @@
333 if (power_mode != mir_power_mode_on)
334 return;
335 if (!current_crtc)
336- BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
337- connector_name(connector.get()) +
338- " has no associated CRTC to wait on"));
339-
340+ {
341+ fatal_error("Output %s has no associated CRTC to wait on",
342+ connector_name(connector.get()).c_str());
343+ }
344 page_flipper->wait_for_flip(current_crtc->crtc_id);
345 }
346
347@@ -283,9 +276,7 @@
348 gbm_bo_get_width(buffer),
349 gbm_bo_get_height(buffer)))
350 {
351- BOOST_THROW_EXCEPTION(
352- ::boost::enable_error_info(std::runtime_error("drmModeSetCursor() failed"))
353- << (boost::error_info<KMSOutput, decltype(result)>(result)));
354+ fatal_error("drmModeSetCursor failed (returned %d)", result);
355 }
356
357 has_cursor_ = true;
358@@ -300,9 +291,7 @@
359 destination.x.as_uint32_t(),
360 destination.y.as_uint32_t()))
361 {
362- BOOST_THROW_EXCEPTION(
363- ::boost::enable_error_info(std::runtime_error("drmModeMoveCursor() failed"))
364- << (boost::error_info<KMSOutput, decltype(result)>(result)));
365+ fatal_error("drmModeMoveCursor failed (returned %d)", result);
366 }
367 }
368 }
369
370=== modified file 'tests/acceptance-tests/test_server_shutdown.cpp'
371--- tests/acceptance-tests/test_server_shutdown.cpp 2014-07-21 03:35:31 +0000
372+++ tests/acceptance-tests/test_server_shutdown.cpp 2014-08-05 11:38:58 +0000
373@@ -23,6 +23,7 @@
374 #include "mir/compositor/display_buffer_compositor_factory.h"
375 #include "mir/input/composite_event_filter.h"
376 #include "mir/run_mir.h"
377+#include "mir/fatal.h"
378
379 #include "mir_test_framework/display_server_test_fixture.h"
380 #include "mir_test/fake_event_hub_input_configuration.h"
381@@ -419,6 +420,70 @@
382 });
383 }
384
385+TEST_F(ServerShutdown, server_removes_endpoint_on_mir_fatal_error_abort)
386+{ // Even fatal errors sometimes need to be caught for critical cleanup...
387+ struct ServerConfig : TestingServerConfiguration
388+ {
389+ void on_start() override
390+ {
391+ sync.wait_for_signal_ready_for();
392+ mir::fatal_error("Bang");
393+ }
394+
395+ mir::FatalErrorStrategy on_error{mir::fatal_error_abort};
396+ mtf::CrossProcessSync sync;
397+ };
398+
399+ ServerConfig server_config;
400+ launch_server_process(server_config);
401+
402+ run_in_test_process([&]
403+ {
404+ ASSERT_TRUE(file_exists(server_config.the_socket_file()));
405+
406+ server_config.sync.signal_ready();
407+
408+ auto result = wait_for_shutdown_server_process();
409+ EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason);
410+ // Under valgrind the server process is reported as being terminated
411+ // by SIGKILL because of multithreading madness.
412+ // TODO: Investigate if we can do better than this workaround
413+ EXPECT_TRUE(result.signal == SIGABRT || result.signal == SIGKILL);
414+
415+ EXPECT_FALSE(file_exists(server_config.the_socket_file()));
416+ });
417+}
418+
419+
420+TEST_F(ServerShutdown, server_removes_endpoint_on_mir_fatal_error_except)
421+{ // Even fatal errors sometimes need to be caught for critical cleanup...
422+ struct ServerConfig : TestingServerConfiguration
423+ {
424+ void on_start() override
425+ {
426+ sync.wait_for_signal_ready_for();
427+ mir::fatal_error("Bang");
428+ }
429+
430+ mtf::CrossProcessSync sync;
431+ };
432+
433+ ServerConfig server_config;
434+ launch_server_process(server_config);
435+
436+ run_in_test_process([&]
437+ {
438+ ASSERT_TRUE(file_exists(server_config.the_socket_file()));
439+
440+ server_config.sync.signal_ready();
441+
442+ auto result = wait_for_shutdown_server_process();
443+ EXPECT_EQ(mtf::TerminationReason::child_terminated_normally, result.reason);
444+
445+ EXPECT_FALSE(file_exists(server_config.the_socket_file()));
446+ });
447+}
448+
449 struct OnSignal : ServerShutdown, ::testing::WithParamInterface<int> {};
450
451 TEST_P(OnSignal, removes_endpoint_on_signal)
452
453=== modified file 'tests/unit-tests/CMakeLists.txt'
454--- tests/unit-tests/CMakeLists.txt 2014-07-31 12:38:17 +0000
455+++ tests/unit-tests/CMakeLists.txt 2014-08-05 11:38:58 +0000
456@@ -19,6 +19,7 @@
457 test_thread_name.cpp
458 test_default_emergency_cleanup.cpp
459 test_basic_observers.cpp
460+ test_fatal.cpp
461 test_fd.cpp
462 )
463
464
465=== added file 'tests/unit-tests/test_fatal.cpp'
466--- tests/unit-tests/test_fatal.cpp 1970-01-01 00:00:00 +0000
467+++ tests/unit-tests/test_fatal.cpp 2014-08-05 11:38:58 +0000
468@@ -0,0 +1,59 @@
469+/*
470+ * Copyright © 2014 Canonical Ltd.
471+ *
472+ * This program is free software: you can redistribute it and/or modify it
473+ * under the terms of the GNU General Public License version 3,
474+ * as published by the Free Software Foundation.
475+ *
476+ * This program is distributed in the hope that it will be useful,
477+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
478+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
479+ * GNU General Public License for more details.
480+ *
481+ * You should have received a copy of the GNU General Public License
482+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
483+ *
484+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
485+ * Alan Griffiths <alan@octopull.co.uk>
486+ */
487+
488+#include "mir/fatal.h"
489+#include <gtest/gtest.h>
490+#include <gmock/gmock.h>
491+#include <csignal>
492+
493+using namespace testing;
494+
495+TEST(FatalTest, abort_formats_message_to_stderr)
496+{
497+ mir::FatalErrorStrategy on_error{mir::fatal_error_abort};
498+
499+ EXPECT_DEATH({mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb");},
500+ "Mary had 1 little lamb");
501+}
502+
503+TEST(FatalTest, abort_raises_sigabrt)
504+{
505+ mir::FatalErrorStrategy on_error{mir::fatal_error_abort};
506+
507+ EXPECT_EXIT({mir::fatal_error("Hello world");},
508+ KilledBySignal(SIGABRT),
509+ "Hello world");
510+}
511+
512+TEST(FatalTest, throw_formats_message_to_what)
513+{
514+ EXPECT_THROW(
515+ mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb"),
516+ std::runtime_error);
517+
518+ try
519+ {
520+ mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb");
521+ }
522+ catch (std::exception const& x)
523+ {
524+ EXPECT_THAT(x.what(), StrEq("Mary had 1 little lamb"));
525+ }
526+}
527+

Subscribers

People subscribed via source and target branches