xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/unittests/observable-selftests.c (revision 4439cfd0acf9c7dc90625e5cd83b2317a9ab8967)
1 /* Self tests for gdb::observers, GDB notifications to observers.
2 
3    Copyright (C) 2003-2023 Free Software Foundation, Inc.
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"
21 #include "gdbsupport/selftest.h"
22 #include "gdbsupport/observable.h"
23 
24 namespace selftests {
25 namespace observers {
26 
27 static gdb::observers::observable<int> test_notification ("test_notification");
28 
29 static int test_first_observer = 0;
30 static int test_second_observer = 0;
31 static int test_third_observer = 0;
32 
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 
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 
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 
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 
184   const gdb::observers::token token1 {}, token2 {} , token3 {};
185 
186   /* Now, attach one observer, and send a notification.  */
187   test_notification.attach (&test_second_notification_function, token2, "test");
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.  */
195   test_notification.attach (&test_first_notification_function, token1, "test");
196   notify_check_counters (1, 0, 0);
197 
198   /* With 2 observers.  */
199   test_notification.attach (&test_second_notification_function, token2, "test");
200   notify_check_counters (1, 1, 0);
201 
202   /* With 3 observers.  */
203   test_notification.attach (&test_third_notification_function, token3, "test");
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...  */
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");
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 
241 void _initialize_observer_selftest ();
242 void
243 _initialize_observer_selftest ()
244 {
245   selftests::register_test ("gdb::observers",
246 			    selftests::observers::run_tests);
247   selftests::register_test ("gdb::observers dependency",
248 			    selftests::observers::test_dependency);
249 }
250