Commit | Line | Data |
---|---|---|
76727919 TT |
1 | /* Self tests for gdb::observers, GDB notifications to observers. |
2 | ||
88b9d363 | 3 | Copyright (C) 2003-2022 Free Software Foundation, Inc. |
76727919 TT |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
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 General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "defs.h" | |
268a13a5 TT |
21 | #include "gdbsupport/selftest.h" |
22 | #include "gdbsupport/observable.h" | |
76727919 TT |
23 | |
24 | namespace selftests { | |
25 | namespace observers { | |
26 | ||
6bd434d6 | 27 | static gdb::observers::observable<int> test_notification ("test_notification"); |
76727919 TT |
28 | |
29 | static int test_first_observer = 0; | |
30 | static int test_second_observer = 0; | |
31 | static int test_third_observer = 0; | |
32 | ||
9a6e099f MW |
33 | /* Counters for observers used for dependency tests. */ |
34 | static std::vector<int> dependency_test_counters; | |
35 | ||
36 | /* Tokens for observers used for dependency tests. */ | |
37 | static gdb::observers::token observer_token0; | |
38 | static gdb::observers::token observer_token1; | |
39 | static gdb::observers::token observer_token2; | |
40 | static gdb::observers::token observer_token3; | |
41 | static gdb::observers::token observer_token4; | |
42 | static gdb::observers::token observer_token5; | |
43 | ||
44 | /* Data for one observer used for checking that dependencies work as expected; | |
45 | dependencies are specified using their indices into the 'test_observers' | |
46 | vector here for simplicity and mapped to corresponding tokens later. */ | |
47 | struct dependency_observer_data | |
48 | { | |
49 | gdb::observers::token *token; | |
50 | ||
51 | /* Name of the observer to use on attach. */ | |
52 | const char *name; | |
53 | ||
54 | /* Indices of observers that this one directly depends on. */ | |
55 | std::vector<int> direct_dependencies; | |
56 | ||
57 | /* Indices for all dependencies, including transitive ones. */ | |
58 | std::vector<int> all_dependencies; | |
59 | ||
60 | /* Function to attach to the observable for this observer. */ | |
61 | std::function<void (int)> callback; | |
62 | }; | |
63 | ||
64 | static void observer_dependency_test_callback (size_t index); | |
65 | ||
66 | /* Data for observers to use for dependency tests, using some sample | |
67 | dependencies between the observers. */ | |
68 | static std::vector<dependency_observer_data> test_observers = { | |
69 | {&observer_token0, "test0", {}, {}, | |
70 | [] (int) { observer_dependency_test_callback (0); }}, | |
71 | {&observer_token1, "test1", {0}, {0}, | |
72 | [] (int) { observer_dependency_test_callback (1); }}, | |
73 | {&observer_token2, "test2", {1}, {0, 1}, | |
74 | [] (int) { observer_dependency_test_callback (2); }}, | |
75 | {&observer_token3, "test3", {1}, {0, 1}, | |
76 | [] (int) { observer_dependency_test_callback (3); }}, | |
77 | {&observer_token4, "test4", {2, 3, 5}, {0, 1, 2, 3, 5}, | |
78 | [] (int) { observer_dependency_test_callback (4); }}, | |
79 | {&observer_token5, "test5", {0}, {0}, | |
80 | [] (int) { observer_dependency_test_callback (5); }}, | |
81 | {nullptr, "test6", {4}, {0, 1, 2, 3, 4, 5}, | |
82 | [] (int) { observer_dependency_test_callback (6); }}, | |
83 | {nullptr, "test7", {0}, {0}, | |
84 | [] (int) { observer_dependency_test_callback (7); }}, | |
85 | }; | |
86 | ||
76727919 TT |
87 | static void |
88 | test_first_notification_function (int arg) | |
89 | { | |
90 | test_first_observer++; | |
91 | } | |
92 | ||
93 | static void | |
94 | test_second_notification_function (int arg) | |
95 | { | |
96 | test_second_observer++; | |
97 | } | |
98 | ||
99 | static void | |
100 | test_third_notification_function (int arg) | |
101 | { | |
102 | test_third_observer++; | |
103 | } | |
104 | ||
105 | static void | |
106 | notify_check_counters (int one, int two, int three) | |
107 | { | |
108 | /* Reset. */ | |
109 | test_first_observer = 0; | |
110 | test_second_observer = 0; | |
111 | test_third_observer = 0; | |
112 | /* Notify. */ | |
113 | test_notification.notify (0); | |
114 | /* Check. */ | |
115 | SELF_CHECK (one == test_first_observer); | |
116 | SELF_CHECK (two == test_second_observer); | |
117 | SELF_CHECK (three == test_third_observer); | |
118 | } | |
119 | ||
9a6e099f MW |
120 | /* Function for each observer to run when being notified during the dependency |
121 | tests. Verify that the observer's dependencies have been notified before the | |
122 | observer itself by checking their counters, then increase the observer's own | |
123 | counter. */ | |
124 | static void | |
125 | observer_dependency_test_callback (size_t index) | |
126 | { | |
127 | /* Check that dependencies have already been notified. */ | |
128 | for (int i : test_observers[index].all_dependencies) | |
129 | SELF_CHECK (dependency_test_counters[i] == 1); | |
130 | ||
131 | /* Increase own counter. */ | |
132 | dependency_test_counters[index]++; | |
133 | } | |
134 | ||
135 | /* Run a dependency test by attaching the observers in the specified order | |
136 | then notifying them. */ | |
137 | static void | |
138 | run_dependency_test (std::vector<int> insertion_order) | |
139 | { | |
140 | gdb::observers::observable<int> dependency_test_notification | |
141 | ("dependency_test_notification"); | |
142 | ||
143 | /* Reset counters. */ | |
144 | dependency_test_counters = std::vector<int> (test_observers.size (), 0); | |
145 | ||
146 | /* Attach all observers in the given order, specifying dependencies. */ | |
147 | for (int i : insertion_order) | |
148 | { | |
149 | const dependency_observer_data &o = test_observers[i]; | |
150 | ||
151 | /* Get tokens for dependencies using their indices. */ | |
152 | std::vector<const gdb::observers::token *> dependency_tokens; | |
153 | for (int index : o.all_dependencies) | |
154 | dependency_tokens.emplace_back (test_observers[index].token); | |
155 | ||
156 | if (o.token != nullptr) | |
157 | dependency_test_notification.attach | |
158 | (o.callback, *o.token, o.name, dependency_tokens); | |
159 | else | |
160 | dependency_test_notification.attach (o.callback, o.name, | |
161 | dependency_tokens); | |
162 | } | |
163 | ||
164 | /* Notify observers, they check that their dependencies were notified. */ | |
165 | dependency_test_notification.notify (1); | |
166 | } | |
167 | ||
168 | static void | |
169 | test_dependency () | |
170 | { | |
171 | /* Run dependency tests with different insertion orders. */ | |
172 | run_dependency_test ({0, 1, 2, 3, 4, 5, 6, 7}); | |
173 | run_dependency_test ({7, 6, 5, 4, 3, 2, 1, 0}); | |
174 | run_dependency_test ({0, 3, 2, 1, 7, 6, 4, 5}); | |
175 | } | |
176 | ||
76727919 TT |
177 | static void |
178 | run_tests () | |
179 | { | |
180 | /* First, try sending a notification without any observer | |
181 | attached. */ | |
182 | notify_check_counters (0, 0, 0); | |
183 | ||
3dcfdc58 | 184 | const gdb::observers::token token1 {}, token2 {} , token3 {}; |
76727919 TT |
185 | |
186 | /* Now, attach one observer, and send a notification. */ | |
c90e7d63 | 187 | test_notification.attach (&test_second_notification_function, token2, "test"); |
76727919 TT |
188 | notify_check_counters (0, 1, 0); |
189 | ||
190 | /* Remove the observer, and send a notification. */ | |
191 | test_notification.detach (token2); | |
192 | notify_check_counters (0, 0, 0); | |
193 | ||
194 | /* With a new observer. */ | |
c90e7d63 | 195 | test_notification.attach (&test_first_notification_function, token1, "test"); |
76727919 TT |
196 | notify_check_counters (1, 0, 0); |
197 | ||
198 | /* With 2 observers. */ | |
c90e7d63 | 199 | test_notification.attach (&test_second_notification_function, token2, "test"); |
76727919 TT |
200 | notify_check_counters (1, 1, 0); |
201 | ||
202 | /* With 3 observers. */ | |
c90e7d63 | 203 | test_notification.attach (&test_third_notification_function, token3, "test"); |
76727919 TT |
204 | notify_check_counters (1, 1, 1); |
205 | ||
206 | /* Remove middle observer. */ | |
207 | test_notification.detach (token2); | |
208 | notify_check_counters (1, 0, 1); | |
209 | ||
210 | /* Remove first observer. */ | |
211 | test_notification.detach (token1); | |
212 | notify_check_counters (0, 0, 1); | |
213 | ||
214 | /* Remove last observer. */ | |
215 | test_notification.detach (token3); | |
216 | notify_check_counters (0, 0, 0); | |
217 | ||
218 | /* Go back to 3 observers, and remove them in a different | |
219 | order... */ | |
c90e7d63 SM |
220 | test_notification.attach (&test_first_notification_function, token1, "test"); |
221 | test_notification.attach (&test_second_notification_function, token2, "test"); | |
222 | test_notification.attach (&test_third_notification_function, token3, "test"); | |
76727919 TT |
223 | notify_check_counters (1, 1, 1); |
224 | ||
225 | /* Remove the third observer. */ | |
226 | test_notification.detach (token3); | |
227 | notify_check_counters (1, 1, 0); | |
228 | ||
229 | /* Remove the second observer. */ | |
230 | test_notification.detach (token2); | |
231 | notify_check_counters (1, 0, 0); | |
232 | ||
233 | /* Remove first observer, no more observers. */ | |
234 | test_notification.detach (token1); | |
235 | notify_check_counters (0, 0, 0); | |
236 | } | |
237 | ||
238 | } /* namespace observers */ | |
239 | } /* namespace selftests */ | |
240 | ||
6c265988 | 241 | void _initialize_observer_selftest (); |
76727919 TT |
242 | void |
243 | _initialize_observer_selftest () | |
244 | { | |
245 | selftests::register_test ("gdb::observers", | |
246 | selftests::observers::run_tests); | |
9a6e099f MW |
247 | selftests::register_test ("gdb::observers dependency", |
248 | selftests::observers::test_dependency); | |
76727919 | 249 | } |