Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : // test standalone header
21 : //
22 : #include <eventdispatcher/signal_handler.h>
23 :
24 :
25 : // self
26 : //
27 : #include "catch_main.h"
28 :
29 :
30 : // eventdispatcher
31 : //
32 : #include <eventdispatcher/communicator.h>
33 : #include <eventdispatcher/dispatcher.h>
34 :
35 :
36 : // C
37 : //
38 : #include <unistd.h>
39 :
40 :
41 : // last include
42 : //
43 : #include <snapdev/poison.h>
44 :
45 :
46 :
47 :
48 : namespace
49 : {
50 :
51 :
52 :
53 : int g_expected_sig = -1;
54 :
55 0 : bool signal_handler_callback(
56 : ed::signal_handler::callback_id_t callback_id
57 : , int callback_sig
58 : , siginfo_t const * info
59 : , ucontext_t const * ucontext)
60 : {
61 0 : CATCH_REQUIRE(g_expected_sig == callback_sig);
62 0 : g_expected_sig = -1;
63 :
64 0 : std::cerr << "--- signal_handler_callback() was called... id: "
65 0 : << callback_id
66 0 : << ", sig: " << callback_sig
67 0 : << ", info->si_pid: " << info->si_pid
68 0 : << ", ucontext->uc_sigmask: " << std::boolalpha << sigismember(&ucontext->uc_sigmask, SIGINT)
69 0 : << "\n";
70 :
71 : // call other callbacks
72 : //
73 0 : return true;
74 : }
75 :
76 :
77 0 : bool extra_signal_handler_callback(
78 : ed::signal_handler::callback_id_t callback_id
79 : , int callback_sig
80 : , siginfo_t const * info
81 : , ucontext_t const * ucontext)
82 : {
83 0 : std::cerr << "--- extra_signal_handler_callback() was called... id: "
84 0 : << callback_id
85 0 : << ", sig: " << callback_sig
86 0 : << ", info->si_pid: " << info->si_pid
87 0 : << ", ucontext->uc_sigmask: " << std::boolalpha << sigismember(&ucontext->uc_sigmask, SIGINT)
88 0 : << "\n";
89 :
90 : // call other callbacks
91 : //
92 0 : return true;
93 : }
94 :
95 :
96 3 : ed::signal_handler::pointer_t get_signal_handler()
97 : {
98 : static bool created(false);
99 :
100 3 : if(!created)
101 : {
102 1 : created = true;
103 :
104 1 : ed::signal_handler::pointer_t sh(ed::signal_handler::create_instance(
105 : ed::signal_handler::DEFAULT_SIGNAL_TERMINAL
106 : , ed::signal_handler::DEFAULT_SIGNAL_IGNORE
107 : , 123
108 : , SIGTERM
109 2 : , &signal_handler_callback));
110 :
111 1 : CATCH_REQUIRE(sh != nullptr);
112 1 : CATCH_REQUIRE(sh == ed::signal_handler::get_instance());
113 1 : }
114 :
115 3 : return ed::signal_handler::get_instance();
116 : }
117 :
118 :
119 : } // no name namespace
120 :
121 :
122 :
123 1 : CATCH_TEST_CASE("signal_handler_name", "[signal_handler]")
124 : {
125 1 : CATCH_START_SECTION("signal_handler_name: verify signal names")
126 : {
127 85 : for(int sig(-10); sig < 74; ++sig)
128 : {
129 84 : char const * system_name(sigabbrev_np(sig));
130 84 : if(system_name == nullptr)
131 : {
132 53 : system_name = "UNKNOWN";
133 : }
134 :
135 84 : char const * eventdispatcher_name(ed::signal_handler::get_signal_name(sig));
136 84 : if(eventdispatcher_name == nullptr)
137 : {
138 53 : eventdispatcher_name = "UNKNOWN";
139 : }
140 :
141 : //std::cerr << "--- compare sig=" << sig << " -> \"" << system_name << "\" vs \"" << eventdispatcher_name << "\".\n";
142 84 : CATCH_CHECK(strcmp(system_name, eventdispatcher_name) == 0);
143 : }
144 : }
145 1 : CATCH_END_SECTION()
146 1 : }
147 :
148 1 : CATCH_TEST_CASE("signal_handler", "[signal_handler]")
149 : {
150 1 : CATCH_START_SECTION("signal_handler: Create Signal Handler connection")
151 : {
152 1 : ed::signal_handler::pointer_t sh(get_signal_handler());
153 :
154 : // TODO: to test this properly we need to verify that the callback
155 : // get called after an add and not called anymore after a
156 : // remove
157 : //
158 1 : sh->add_callback(444, SIGILL, &extra_signal_handler_callback);
159 1 : sh->remove_callback(444);
160 :
161 1 : CATCH_CHECK(sh->get_show_stack() == ed::signal_handler::DEFAULT_SHOW_STACK);
162 1 : sh->set_show_stack(ed::signal_handler::SIGNAL_INTERRUPT);
163 1 : CATCH_CHECK(sh->get_show_stack() == ed::signal_handler::SIGNAL_INTERRUPT);
164 1 : sh->set_show_stack(ed::signal_handler::DEFAULT_SHOW_STACK);
165 1 : CATCH_CHECK(sh->get_show_stack() == ed::signal_handler::DEFAULT_SHOW_STACK);
166 1 : }
167 1 : CATCH_END_SECTION()
168 :
169 : // Catch2 v3 is compiled with the POSIX signal handler always in place...
170 : // The problem is that each time a test is run, the Catch2 signal is setup
171 : // and then released, so calling get_signal_handler() is already not
172 : // compatible
173 : //
174 : // CATCH_START_SECTION("signal_handler: Test sending a SIGTERM and see we capture it")
175 : // {
176 : // ed::signal_handler::pointer_t sh(get_signal_handler());
177 : //
178 : // g_expected_sig = SIGTERM;
179 : // kill(getpid(), SIGTERM);
180 : // CATCH_REQUIRE(g_expected_sig == -1);
181 : // }
182 : // CATCH_END_SECTION()
183 1 : }
184 :
185 :
186 2 : CATCH_TEST_CASE("signal_handler_errors", "[timer][error]")
187 : {
188 2 : CATCH_START_SECTION("signal_handler_errors: create_instance() can only be called once")
189 : {
190 1 : ed::signal_handler::pointer_t sh(get_signal_handler());
191 1 : CATCH_REQUIRE(sh != nullptr);
192 :
193 : // calling the create_instance() a second time is an error
194 : //
195 4 : CATCH_REQUIRE_THROWS_MATCHES(
196 : ed::signal_handler::create_instance(
197 : ed::signal_handler::DEFAULT_SIGNAL_TERMINAL
198 : , ed::signal_handler::DEFAULT_SIGNAL_IGNORE
199 : , 321
200 : , SIGPIPE
201 : , &signal_handler_callback)
202 : , ed::initialization_error
203 : , Catch::Matchers::ExceptionMessage(
204 : "event_dispatcher_exception: signal_handler::create_instance() must be called once before signal_handler::get_instance() ever gets called."));
205 1 : }
206 2 : CATCH_END_SECTION()
207 :
208 2 : CATCH_START_SECTION("signal_handler_errors: invalid signal number & callback pointer")
209 : {
210 1 : ed::signal_handler::pointer_t sh(get_signal_handler());
211 :
212 4 : CATCH_REQUIRE_THROWS_MATCHES(
213 : sh->add_callback(555, 123, &extra_signal_handler_callback)
214 : , ed::invalid_signal
215 : , Catch::Matchers::ExceptionMessage(
216 : "event_dispatcher_exception: signal_handler::add_callback() called with invalid signal number 123."));
217 :
218 3 : CATCH_REQUIRE_THROWS_MATCHES(
219 : sh->add_callback(555, SIGBUS, nullptr)
220 : , ed::invalid_callback
221 : , Catch::Matchers::ExceptionMessage(
222 : "event_dispatcher_exception: signal_handler::add_callback() called with nullptr as the callback."));
223 1 : }
224 2 : CATCH_END_SECTION()
225 2 : }
226 :
227 :
228 :
229 : // vim: ts=4 sw=4 et
|