be2cd48d9a2a2322767d5aaa691bfd33c9bc7be8
[silc.git] / lib / silcutil / tests / test_silcfsm.c
1 /* SILC FSM tests */
2
3 #include "silcincludes.h"
4 #include "silcfsm.h"
5
6 typedef void (*Callback)(void *context);
7
8 #define NUM_THREADS 200
9
10 typedef struct FooStruct *Foo;
11
12 typedef struct {
13   SilcFSMThreadStruct thread;
14   SilcFSMSemaStruct sema;
15   SilcBool finished;
16   int rounds;
17   Foo f;
18 } T;
19
20 struct FooStruct {
21   SilcBool error;
22   SilcFSM fsm;
23   SilcFSMThreadStruct thread;
24   int timeout;
25   SilcFSMSemaStruct sema;
26   SilcSchedule schedule;
27   Callback cb;
28   void *cb_context;
29   T threads[NUM_THREADS];
30   T threads2[NUM_THREADS];
31 };
32
33 SILC_FSM_STATE(test_st_start);
34 SILC_FSM_STATE(test_st_second);
35 SILC_FSM_STATE(test_st_third);
36 SILC_FSM_STATE(test_st_fourth);
37 SILC_FSM_STATE(test_st_fifth);
38 SILC_FSM_STATE(test_st_sixth);
39 SILC_FSM_STATE(test_st_seventh);
40 SILC_FSM_STATE(test_st_eighth);
41 SILC_FSM_STATE(test_st_ninth);
42 SILC_FSM_STATE(test_st_tenth);
43 SILC_FSM_STATE(test_st_finish);
44
45 SILC_FSM_STATE(test_thread_st_start);
46 SILC_FSM_STATE(test_thread_st_finish);
47 SILC_FSM_STATE(test_thread2_st_start);
48 SILC_FSM_STATE(test_thread2_st_finish);
49 SILC_FSM_STATE(test_thread3_st_start);
50 SILC_FSM_STATE(test_thread4_st_start);
51
52 SILC_TASK_CALLBACK(async_call_timeout)
53 {
54   Foo f = context;
55   SILC_LOG_DEBUG(("Async call cb, continuing FSM"));
56   f->cb(f->cb_context);
57 }
58
59 static void async_call(Callback cb, void *context)
60 {
61   Foo f = context;
62   f->cb = cb;
63   f->cb_context = context;
64   SILC_LOG_DEBUG(("Async call"));
65   silc_schedule_task_add(f->schedule, 0, async_call_timeout, f, 0, 200000,
66                          SILC_TASK_TIMEOUT);
67 }
68
69 SILC_FSM_STATE(test_st_start)
70 {
71   SILC_LOG_DEBUG(("test_st_start"));
72
73   /** Move to second state */
74   SILC_LOG_DEBUG(("Move to next state"));
75   silc_fsm_next(fsm, test_st_second);
76   return SILC_FSM_CONTINUE;
77 }
78
79 SILC_FSM_STATE(test_st_second)
80 {
81   SILC_LOG_DEBUG(("test_st_second"));
82
83   /** Move to third state, timeout */
84   SILC_LOG_DEBUG(("Move to next state with 2 second timeout"));
85   silc_fsm_next_later(fsm, test_st_third, 2, 0);
86   return SILC_FSM_WAIT;
87 }
88
89 static void async_call_cb(void *context)
90 {
91   Foo f = context;
92   SILC_LOG_DEBUG(("Callback, continue to next state"));
93   SILC_FSM_CALL_CONTINUE(f->fsm);
94 }
95
96 SILC_FSM_STATE(test_st_third)
97 {
98   Foo f = fsm_context;
99
100   SILC_LOG_DEBUG(("test_st_third"));
101
102   f->fsm = fsm;
103
104   /** Wait async callback*/
105   SILC_LOG_DEBUG(("Call async call"));
106   silc_fsm_next(fsm, test_st_fourth);
107   SILC_FSM_CALL(async_call(async_call_cb, f));
108 }
109
110 SILC_FSM_STATE(test_st_fourth)
111 {
112   Foo f = fsm_context;
113
114   SILC_LOG_DEBUG(("test_st_fourth"));
115
116   f->timeout = 1;
117
118   SILC_LOG_DEBUG(("Creating FSM thread"));
119   if (!silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE)) {
120     /** Error creating thread */
121     SILC_LOG_DEBUG(("Error creating thread"));
122     f->error = TRUE;
123     silc_fsm_next(fsm, test_st_finish);
124     return SILC_FSM_CONTINUE;
125   }
126   SILC_LOG_DEBUG(("Starting thread"));
127   silc_fsm_start(&f->thread, test_thread_st_start);
128
129   /** Waiting thread to terminate */
130   SILC_LOG_DEBUG(("Waiting for thread to terminate"));
131   silc_fsm_next(fsm, test_st_fifth);
132   SILC_FSM_THREAD_WAIT(&f->thread);
133 }
134
135 SILC_FSM_STATE(test_thread_st_start)
136 {
137   Foo f = fsm_context;
138
139   SILC_LOG_DEBUG(("test_thread_st_start"));
140
141   /** Move to final state, timeout */
142   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
143   silc_fsm_next_later(fsm, test_thread_st_finish, f->timeout, 0);
144   return SILC_FSM_WAIT;
145 }
146
147 SILC_FSM_STATE(test_thread_st_finish)
148 {
149   SILC_LOG_DEBUG(("test_thread_st_finish"));
150
151   SILC_LOG_DEBUG(("Finishing the thread"));
152   return SILC_FSM_FINISH;
153 }
154
155 SILC_FSM_STATE(test_st_fifth)
156 {
157   Foo f = fsm_context;
158   SILC_LOG_DEBUG(("test_st_fifth"));
159
160   SILC_LOG_DEBUG(("Thread terminated, start new real thread"));
161
162   f->timeout = 7;
163
164   SILC_LOG_DEBUG(("Creating FSM semaphore"));
165   silc_fsm_sema_init(&f->sema, fsm, 0);
166
167   SILC_LOG_DEBUG(("Creating FSM thread"));
168   if (!silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, TRUE)) {
169     /** Error creating real thread */
170     SILC_LOG_DEBUG(("Error creating thread"));
171     f->error = TRUE;
172     silc_fsm_next(fsm, test_st_finish);
173     return SILC_FSM_CONTINUE;
174   }
175   SILC_LOG_DEBUG(("Starting thread"));
176   silc_fsm_start(&f->thread, test_thread2_st_start);
177
178   /** Waiting thread to terminate, timeout */
179   SILC_LOG_DEBUG(("Waiting for thread to terminate for 5 seconds"));
180   silc_fsm_next(fsm, test_st_sixth);
181   SILC_FSM_SEMA_TIMEDWAIT(&f->sema, 5, 0);
182   return SILC_FSM_CONTINUE;
183 }
184
185 SILC_FSM_STATE(test_thread2_st_start)
186 {
187   Foo f = fsm_context;
188
189   SILC_LOG_DEBUG(("test_thread2_st_start"));
190
191   /** Move to final state, timeout */
192   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
193   silc_fsm_next_later(fsm, test_thread2_st_finish, f->timeout, 0);
194   return SILC_FSM_WAIT;
195 }
196
197 SILC_FSM_STATE(test_thread2_st_finish)
198 {
199   Foo f = fsm_context;
200   SILC_LOG_DEBUG(("test_thread2_st_finish"));
201
202   SILC_LOG_DEBUG(("Post semaphore"));
203   SILC_FSM_SEMA_POST(&f->sema);
204
205   SILC_LOG_DEBUG(("Finishing the thread"));
206   return SILC_FSM_FINISH;
207 }
208
209 SILC_FSM_STATE(test_st_sixth)
210 {
211   SILC_LOG_DEBUG(("test_st_sixth"));
212
213   SILC_LOG_DEBUG(("Thread wait timedout, OK"));
214
215   /** Move to next state, timeout */
216   SILC_LOG_DEBUG(("Continue to next state with 4 second timeout"));
217   silc_fsm_next_later(fsm, test_st_seventh, 4, 0);
218   return SILC_FSM_WAIT;
219 }
220
221 SILC_FSM_STATE(test_thread3_st_start)
222 {
223   T *t = fsm_context;
224
225   if (t->rounds == 0) {
226     SILC_FSM_SEMA_POST(&t->sema);
227     return SILC_FSM_FINISH;
228   }
229
230   t->rounds--;
231
232   /** Call in recursive */
233   silc_fsm_next(fsm, test_thread3_st_start);
234   return SILC_FSM_CONTINUE;
235 }
236
237 SILC_FSM_STATE(test_st_seventh)
238 {
239   Foo f = fsm_context;
240   int i;
241
242   SILC_LOG_DEBUG(("test_st_seventh"));
243
244
245   SILC_LOG_DEBUG(("Creating %d FSM threads", NUM_THREADS));
246   for (i = 0; i < NUM_THREADS; i++) {
247     f->threads[i].rounds = 10;
248     f->threads[i].f = f;
249     silc_fsm_sema_init(&f->threads[i].sema, fsm, 0);
250     if (!silc_fsm_thread_init(&f->threads[i].thread, fsm,
251                               &f->threads[i], NULL, NULL, FALSE)) {
252       /** Error creating thread */
253       SILC_LOG_DEBUG(("Error creating thread"));
254       f->error = TRUE;
255       silc_fsm_next(fsm, test_st_finish);
256       return SILC_FSM_CONTINUE;
257     }
258
259     silc_fsm_start(&f->threads[i].thread, test_thread3_st_start);
260   }
261
262   /** Move to wait threads */
263   silc_fsm_next(fsm, test_st_eighth);
264   return SILC_FSM_CONTINUE;
265 }
266
267 SILC_FSM_STATE(test_st_eighth)
268 {
269   Foo f = fsm_context;
270   int i;
271
272   for (i = 0; i < NUM_THREADS; i++) {
273     if (f->threads[i].finished == FALSE) {
274       SILC_FSM_SEMA_WAIT(&f->threads[i].sema);
275       f->threads[i].finished = TRUE;
276     }
277   }
278
279   for (i = 0; i < NUM_THREADS; i++)
280     silc_fsm_uninit(&f->threads[i].thread);
281
282   SILC_LOG_DEBUG(("All %d threads terminated", NUM_THREADS));
283
284   /** Move to next thread */
285   silc_fsm_next(fsm, test_st_ninth);
286   return SILC_FSM_CONTINUE;
287 }
288
289 SILC_FSM_STATE(test_thread4_st_start)
290 {
291   T *t = fsm_context;
292
293   if (t->rounds == 0) {
294     SILC_FSM_SEMA_POST(&t->sema);
295     return SILC_FSM_FINISH;
296   }
297
298   t->rounds--;
299
300   /** Call in recursive */
301   silc_fsm_next(fsm, test_thread4_st_start);
302   return SILC_FSM_CONTINUE;
303 }
304
305 SILC_FSM_STATE(test_st_ninth)
306 {
307   Foo f = fsm_context;
308   int i;
309
310   SILC_LOG_DEBUG(("test_st_ninth"));
311
312   SILC_LOG_DEBUG(("Creating FSM semaphore"));
313   silc_fsm_sema_init(&f->sema, fsm, NUM_THREADS + 1);
314
315   SILC_LOG_DEBUG(("Creating %d real FSM threads", NUM_THREADS));
316   for (i = 0; i < NUM_THREADS; i++) {
317     f->threads2[i].rounds = 10;
318     f->threads2[i].f = f;
319     silc_fsm_sema_init(&f->threads2[i].sema, fsm, 0);
320     if (!silc_fsm_thread_init(&f->threads2[i].thread, fsm,
321                               &f->threads2[i], NULL, NULL, TRUE)) {
322       /** Error creating real thread */
323       SILC_LOG_DEBUG(("Error creating real thread"));
324       f->error = TRUE;
325       silc_fsm_next(fsm, test_st_finish);
326       return SILC_FSM_CONTINUE;
327     }
328
329     silc_fsm_start(&f->threads2[i].thread, test_thread4_st_start);
330   }
331
332   /** Move to wait threads */
333   silc_fsm_next(fsm, test_st_tenth);
334   return SILC_FSM_CONTINUE;
335 }
336
337 SILC_FSM_STATE(test_st_tenth)
338 {
339   Foo f = fsm_context;
340   int i;
341
342   for (i = 0; i < NUM_THREADS; i++)
343     if (f->threads2[i].finished == FALSE) {
344       SILC_FSM_SEMA_WAIT(&f->threads2[i].sema);
345       f->threads2[i].finished = TRUE;
346     }
347
348   for (i = 0; i < NUM_THREADS; i++)
349     silc_fsm_uninit(&f->threads2[i].thread);
350
351   SILC_LOG_DEBUG(("All %d real threads terminated", NUM_THREADS));
352
353   /** Finished successfully */
354   silc_fsm_next_later(fsm, test_st_finish, 2, 0);
355   return SILC_FSM_WAIT;
356 }
357
358 SILC_FSM_STATE(test_st_finish)
359 {
360   SILC_LOG_DEBUG(("test_st_finish"));
361
362   SILC_LOG_DEBUG(("Finish machine"));
363   return SILC_FSM_FINISH;
364 }
365
366 static void destructor(SilcFSM fsm, void *fsm_context,
367                        void *destructor_context)
368 {
369   Foo f = destructor_context;
370   SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
371   silc_fsm_free(fsm);
372   silc_schedule_stop(f->schedule);
373 }
374
375 int main(int argc, char **argv)
376 {
377   SilcBool success = FALSE;
378   SilcSchedule schedule;
379   SilcFSM fsm;
380   Foo f;
381
382   if (argc > 1 && !strcmp(argv[1], "-d")) {
383     silc_log_debug(TRUE);
384     silc_log_debug_hexdump(TRUE);
385     silc_log_quick(TRUE);
386     silc_log_set_debug_string("*fsm*,*async*");
387   }
388
389   SILC_LOG_DEBUG(("Allocating scheduler"));
390   schedule = silc_schedule_init(0, NULL);
391
392   f = silc_calloc(1, sizeof(*f));
393   if (!f)
394     goto err;
395   f->schedule = schedule;
396
397   SILC_LOG_DEBUG(("Allocating FSM context"));
398   fsm = silc_fsm_alloc(f, destructor, f, schedule);
399   if (!fsm)
400     goto err;
401   silc_fsm_start(fsm, test_st_start);
402
403   SILC_LOG_DEBUG(("Running scheduler"));
404   silc_schedule(schedule);
405
406   if (f->error)
407     goto err;
408
409   silc_schedule_uninit(schedule);
410   silc_free(f);
411
412   success = TRUE;
413
414  err:
415   SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
416   fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
417
418   return success;
419 }