Added SILC Server library.
[silc.git] / lib / silcutil / tests / test_silcfsm.c
1 /* SILC FSM tests */
2
3 #include "silc.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   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE);
120   SILC_LOG_DEBUG(("Starting thread"));
121   /*** Start thread */
122   silc_fsm_start(&f->thread, test_thread_st_start);
123
124   /** Waiting thread to terminate */
125   SILC_LOG_DEBUG(("Waiting for thread to terminate"));
126   silc_fsm_next(fsm, test_st_fifth);
127   SILC_FSM_THREAD_WAIT(&f->thread);
128 }
129
130 SILC_FSM_STATE(test_thread_st_start)
131 {
132   Foo f = fsm_context;
133
134   SILC_LOG_DEBUG(("test_thread_st_start"));
135
136   /** Move to final state, timeout */
137   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
138   silc_fsm_next_later(fsm, test_thread_st_finish, f->timeout, 0);
139   return SILC_FSM_WAIT;
140 }
141
142 SILC_FSM_STATE(test_thread_st_finish)
143 {
144   SILC_LOG_DEBUG(("test_thread_st_finish"));
145
146   SILC_LOG_DEBUG(("Finishing the thread"));
147   return SILC_FSM_FINISH;
148 }
149
150 SILC_FSM_STATE(test_st_fifth)
151 {
152   Foo f = fsm_context;
153   SILC_LOG_DEBUG(("test_st_fifth"));
154
155   SILC_LOG_DEBUG(("Thread terminated, start new real thread"));
156
157   f->timeout = 7;
158
159   SILC_LOG_DEBUG(("Creating FSM semaphore"));
160   silc_fsm_sema_init(&f->sema, fsm, 0);
161
162   SILC_LOG_DEBUG(("Creating FSM thread"));
163   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, TRUE);
164   SILC_LOG_DEBUG(("Starting thread"));
165   silc_fsm_start(&f->thread, test_thread2_st_start);
166
167   /** Waiting thread to terminate, timeout */
168   SILC_LOG_DEBUG(("Waiting for thread to terminate for 5 seconds"));
169   silc_fsm_next(fsm, test_st_sixth);
170   SILC_FSM_SEMA_TIMEDWAIT(&f->sema, 5, 0, NULL);
171   return SILC_FSM_CONTINUE;
172 }
173
174 SILC_FSM_STATE(test_thread2_st_start)
175 {
176   Foo f = fsm_context;
177
178   SILC_LOG_DEBUG(("test_thread2_st_start"));
179
180   /** Move to final state, timeout */
181   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
182   silc_fsm_next_later(fsm, test_thread2_st_finish, f->timeout, 0);
183   return SILC_FSM_WAIT;
184 }
185
186 SILC_FSM_STATE(test_thread2_st_finish)
187 {
188   Foo f = fsm_context;
189   SILC_LOG_DEBUG(("test_thread2_st_finish"));
190
191   SILC_LOG_DEBUG(("Post semaphore"));
192   SILC_FSM_SEMA_POST(&f->sema);
193
194   SILC_LOG_DEBUG(("Finishing the thread"));
195   return SILC_FSM_FINISH;
196 }
197
198 SILC_FSM_STATE(test_st_sixth)
199 {
200   SILC_LOG_DEBUG(("test_st_sixth"));
201
202   SILC_LOG_DEBUG(("Thread wait timedout, OK"));
203
204   /** Move to next state, timeout */
205   SILC_LOG_DEBUG(("Continue to next state with 4 second timeout"));
206   silc_fsm_next_later(fsm, test_st_seventh, 4, 0);
207   return SILC_FSM_WAIT;
208 }
209
210 SILC_FSM_STATE(test_thread3_st_start)
211 {
212   T *t = fsm_context;
213
214   if (t->rounds == 0) {
215     SILC_FSM_SEMA_POST(&t->sema);
216     return SILC_FSM_FINISH;
217   }
218
219   t->rounds--;
220
221   /** Call in recursive */
222   silc_fsm_next(fsm, test_thread3_st_start);
223   return SILC_FSM_CONTINUE;
224 }
225
226 SILC_FSM_STATE(test_st_seventh)
227 {
228   Foo f = fsm_context;
229   int i;
230
231   SILC_LOG_DEBUG(("test_st_seventh"));
232
233
234   SILC_LOG_DEBUG(("Creating %d FSM threads", NUM_THREADS));
235   for (i = 0; i < NUM_THREADS; i++) {
236     f->threads[i].rounds = 10;
237     f->threads[i].f = f;
238     silc_fsm_sema_init(&f->threads[i].sema, fsm, 0);
239     silc_fsm_thread_init(&f->threads[i].thread, fsm,
240                          &f->threads[i], NULL, NULL, FALSE);
241     silc_fsm_start(&f->threads[i].thread, test_thread3_st_start);
242   }
243
244   /** Move to wait threads */
245   silc_fsm_next(fsm, test_st_eighth);
246   return SILC_FSM_CONTINUE;
247 }
248
249 SILC_FSM_STATE(test_st_eighth)
250 {
251   Foo f = fsm_context;
252   int i;
253
254   for (i = 0; i < NUM_THREADS; i++) {
255     if (f->threads[i].finished == FALSE) {
256       SILC_FSM_SEMA_WAIT(&f->threads[i].sema);
257       f->threads[i].finished = TRUE;
258     }
259   }
260
261   for (i = 0; i < NUM_THREADS; i++)
262     silc_fsm_uninit(&f->threads[i].thread);
263
264   SILC_LOG_DEBUG(("All %d threads terminated", NUM_THREADS));
265
266   /** Move to next thread */
267   silc_fsm_next(fsm, test_st_ninth);
268   return SILC_FSM_CONTINUE;
269 }
270
271 SILC_FSM_STATE(test_thread4_st_start)
272 {
273   T *t = fsm_context;
274
275   if (t->rounds == 0) {
276     SILC_FSM_SEMA_POST(&t->sema);
277     return SILC_FSM_FINISH;
278   }
279
280   t->rounds--;
281
282   /** Call in recursive */
283   silc_fsm_next(fsm, test_thread4_st_start);
284   return SILC_FSM_CONTINUE;
285 }
286
287 SILC_FSM_STATE(test_st_ninth)
288 {
289   Foo f = fsm_context;
290   int i;
291
292   SILC_LOG_DEBUG(("test_st_ninth"));
293
294   SILC_LOG_DEBUG(("Creating FSM semaphore"));
295   silc_fsm_sema_init(&f->sema, fsm, NUM_THREADS + 1);
296
297   SILC_LOG_DEBUG(("Creating %d real FSM threads", NUM_THREADS));
298   for (i = 0; i < NUM_THREADS; i++) {
299     f->threads2[i].rounds = 10;
300     f->threads2[i].f = f;
301     silc_fsm_sema_init(&f->threads2[i].sema, fsm, 0);
302     silc_fsm_thread_init(&f->threads2[i].thread, fsm,
303                          &f->threads2[i], NULL, NULL, TRUE);
304     silc_fsm_start(&f->threads2[i].thread, test_thread4_st_start);
305   }
306
307   /** Move to wait threads */
308   silc_fsm_next(fsm, test_st_tenth);
309   return SILC_FSM_CONTINUE;
310 }
311
312 SILC_FSM_STATE(test_st_tenth)
313 {
314   Foo f = fsm_context;
315   int i;
316
317   for (i = 0; i < NUM_THREADS; i++)
318     if (f->threads2[i].finished == FALSE) {
319       SILC_FSM_SEMA_WAIT(&f->threads2[i].sema);
320       f->threads2[i].finished = TRUE;
321     }
322
323   for (i = 0; i < NUM_THREADS; i++)
324     silc_fsm_uninit(&f->threads2[i].thread);
325
326   SILC_LOG_DEBUG(("All %d real threads terminated", NUM_THREADS));
327
328   /** Finished successfully */
329   silc_fsm_next_later(fsm, test_st_finish, 2, 0);
330   return SILC_FSM_WAIT;
331 }
332
333 SILC_FSM_STATE(test_st_finish)
334 {
335   SILC_LOG_DEBUG(("test_st_finish"));
336
337   SILC_LOG_DEBUG(("Finish machine"));
338   return SILC_FSM_FINISH;
339 }
340
341 static void destructor(SilcFSM fsm, void *fsm_context,
342                        void *destructor_context)
343 {
344   Foo f = destructor_context;
345   SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
346   silc_fsm_free(fsm);
347   silc_schedule_stop(f->schedule);
348 }
349
350 int main(int argc, char **argv)
351 {
352   SilcBool success = FALSE;
353   SilcSchedule schedule;
354   SilcFSM fsm;
355   Foo f;
356
357   if (argc > 1 && !strcmp(argv[1], "-d")) {
358     silc_log_debug(TRUE);
359     silc_log_debug_hexdump(TRUE);
360     silc_log_quick(TRUE);
361     silc_log_set_debug_string("*fsm*,*async*");
362   }
363
364   SILC_LOG_DEBUG(("Allocating scheduler"));
365   schedule = silc_schedule_init(0, NULL);
366
367   f = silc_calloc(1, sizeof(*f));
368   if (!f)
369     goto err;
370   f->schedule = schedule;
371
372   SILC_LOG_DEBUG(("Allocating FSM context"));
373   fsm = silc_fsm_alloc(f, destructor, f, schedule);
374   if (!fsm)
375     goto err;
376   silc_fsm_start(fsm, test_st_start);
377
378   SILC_LOG_DEBUG(("Running scheduler"));
379   silc_schedule(schedule);
380
381   if (f->error)
382     goto err;
383
384   silc_schedule_uninit(schedule);
385   silc_free(f);
386
387   success = TRUE;
388
389  err:
390   SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
391   fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
392
393   return success;
394 }