SILC FSM API changes.
[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   SilcFSMEventStruct 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   SilcFSMEventStruct sema;
26   SilcSchedule schedule;
27   Callback cb;
28   void *cb_context;
29   T threads[NUM_THREADS];
30   T threads2[NUM_THREADS];
31   int c;
32 };
33
34 SILC_FSM_STATE(test_st_start);
35 SILC_FSM_STATE(test_st_second);
36 SILC_FSM_STATE(test_st_second_timeout);
37 SILC_FSM_STATE(test_st_third);
38 SILC_FSM_STATE(test_st_fourth);
39 SILC_FSM_STATE(test_st_fifth);
40 SILC_FSM_STATE(test_st_sixth);
41 SILC_FSM_STATE(test_st_seventh);
42 SILC_FSM_STATE(test_st_eighth);
43 SILC_FSM_STATE(test_st_ninth);
44 SILC_FSM_STATE(test_st_tenth);
45 SILC_FSM_STATE(test_st_finish);
46
47 SILC_FSM_STATE(test_thread_st_start);
48 SILC_FSM_STATE(test_thread_st_finish);
49 SILC_FSM_STATE(test_thread2_st_start);
50 SILC_FSM_STATE(test_thread2_st_finish);
51 SILC_FSM_STATE(test_thread3_st_start);
52 SILC_FSM_STATE(test_thread4_st_start);
53
54 SILC_TASK_CALLBACK(async_call_timeout)
55 {
56   Foo f = context;
57   SILC_LOG_DEBUG(("Async call cb, continuing FSM"));
58   f->cb(f->cb_context);
59 }
60
61 static void async_call(Callback cb, void *context)
62 {
63   Foo f = context;
64   f->cb = cb;
65   f->cb_context = context;
66   SILC_LOG_DEBUG(("Async call"));
67   silc_schedule_task_add(f->schedule, 0, async_call_timeout, f, 0, 200000,
68                          SILC_TASK_TIMEOUT);
69 }
70
71 SILC_FSM_STATE(test_st_start)
72 {
73   SILC_LOG_DEBUG(("test_st_start"));
74
75   /** Move to second state */
76   SILC_LOG_DEBUG(("Move to next state"));
77   silc_fsm_next(fsm, test_st_second);
78   SILC_FSM_CONTINUE;
79 }
80
81 SILC_FSM_STATE(test_st_second)
82 {
83   SILC_LOG_DEBUG(("test_st_second"));
84
85   /** Move to second timeout state, timeout */
86   SILC_LOG_DEBUG(("Move to next state with 2 second timeout"));
87   silc_fsm_next_later(fsm, test_st_second_timeout, 2, 0);
88   SILC_FSM_WAIT;
89 }
90
91 SILC_TASK_CALLBACK(test_second_timeout)
92 {
93   Foo f = context;
94   SILC_LOG_DEBUG(("test_second_timeout"));
95
96   SILC_LOG_DEBUG(("Interrupt 3 second wait and continue immediately"));
97   f->c++;
98   silc_fsm_next(f->fsm, test_st_third);
99   silc_fsm_continue(f->fsm);
100 }
101
102 SILC_FSM_STATE(test_st_second_timeout)
103 {
104   Foo f = fsm_context;
105
106   SILC_LOG_DEBUG(("test_st_second_timeout"));
107
108   /** Move to third state, timeout */
109   SILC_LOG_DEBUG(("Move to next state with 3 second timeout"));
110   SILC_LOG_DEBUG(("The timeout will be interrupted with silc_fsm_continue"));
111   silc_fsm_next_later(fsm, test_st_third, 3, 0);
112   silc_schedule_task_add_timeout(silc_fsm_get_schedule(fsm),
113                                  test_second_timeout, f, 2, 500000);
114   SILC_FSM_WAIT;
115 }
116
117 static void async_call_cb(void *context)
118 {
119   Foo f = context;
120   SILC_LOG_DEBUG(("Callback, continue to next state"));
121   SILC_FSM_CALL_CONTINUE(f->fsm);
122 }
123
124 SILC_FSM_STATE(test_st_third)
125 {
126   Foo f = fsm_context;
127
128   SILC_LOG_DEBUG(("test_st_third"));
129
130   f->c++;
131   assert(f->c == 2);
132
133   f->fsm = fsm;
134
135   /** Wait async callback*/
136   SILC_LOG_DEBUG(("Call async call"));
137   silc_fsm_next(fsm, test_st_fourth);
138   SILC_FSM_CALL(async_call(async_call_cb, f));
139 }
140
141 SILC_FSM_STATE(test_st_fourth)
142 {
143   Foo f = fsm_context;
144
145   SILC_LOG_DEBUG(("test_st_fourth"));
146
147   f->timeout = 1;
148
149   SILC_LOG_DEBUG(("Creating FSM thread"));
150   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE);
151   SILC_LOG_DEBUG(("Starting thread"));
152   /*** Start thread */
153   silc_fsm_start(&f->thread, test_thread_st_start);
154
155   /** Waiting thread to terminate */
156   SILC_LOG_DEBUG(("Waiting for thread to terminate"));
157   silc_fsm_next(fsm, test_st_fifth);
158   SILC_FSM_THREAD_WAIT(&f->thread);
159 }
160
161 SILC_FSM_STATE(test_thread_st_start)
162 {
163   Foo f = fsm_context;
164
165   SILC_LOG_DEBUG(("test_thread_st_start"));
166
167   /** Move to final state, timeout */
168   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
169   silc_fsm_next_later(fsm, test_thread_st_finish, f->timeout, 0);
170   SILC_FSM_WAIT;
171 }
172
173 SILC_FSM_STATE(test_thread_st_finish)
174 {
175   SILC_LOG_DEBUG(("test_thread_st_finish"));
176
177   SILC_LOG_DEBUG(("Finishing the thread"));
178   SILC_FSM_FINISH;
179 }
180
181 SILC_FSM_STATE(test_st_fifth)
182 {
183   Foo f = fsm_context;
184   SILC_LOG_DEBUG(("test_st_fifth"));
185
186   SILC_LOG_DEBUG(("Thread terminated, start new real thread"));
187
188   f->timeout = 7;
189
190   SILC_LOG_DEBUG(("Creating FSM event"));
191   silc_fsm_event_init(&f->sema, fsm);
192
193   SILC_LOG_DEBUG(("Creating FSM thread"));
194   silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, TRUE);
195   SILC_LOG_DEBUG(("Starting thread"));
196   silc_fsm_start(&f->thread, test_thread2_st_start);
197
198   /** Waiting thread to terminate, timeout */
199   SILC_LOG_DEBUG(("Waiting for thread to terminate for 5 seconds"));
200   silc_fsm_next(fsm, test_st_sixth);
201   SILC_FSM_EVENT_TIMEDWAIT(&f->sema, 5, 0, NULL);
202   SILC_FSM_CONTINUE;
203 }
204
205 SILC_FSM_STATE(test_thread2_st_start)
206 {
207   Foo f = fsm_context;
208
209   SILC_LOG_DEBUG(("test_thread2_st_start"));
210
211   /** Move to final state, timeout */
212   SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
213   silc_fsm_next_later(fsm, test_thread2_st_finish, f->timeout, 0);
214   SILC_FSM_WAIT;
215 }
216
217 SILC_FSM_STATE(test_thread2_st_finish)
218 {
219   Foo f = fsm_context;
220   SILC_LOG_DEBUG(("test_thread2_st_finish"));
221
222   SILC_LOG_DEBUG(("Post semaphore"));
223   SILC_FSM_EVENT_SIGNAL(&f->sema);
224
225   SILC_LOG_DEBUG(("Finishing the thread"));
226   SILC_FSM_FINISH;
227 }
228
229 SILC_FSM_STATE(test_st_sixth)
230 {
231   SILC_LOG_DEBUG(("test_st_sixth"));
232
233   SILC_LOG_DEBUG(("Thread wait timedout, OK"));
234
235   /** Move to next state, timeout */
236   SILC_LOG_DEBUG(("Continue to next state with 4 second timeout"));
237   silc_fsm_next_later(fsm, test_st_seventh, 4, 0);
238   SILC_FSM_WAIT;
239 }
240
241 SILC_FSM_STATE(test_thread3_st_start)
242 {
243   T *t = fsm_context;
244
245   if (t->rounds == 0) {
246     SILC_FSM_EVENT_SIGNAL(&t->sema);
247     SILC_FSM_FINISH;
248   }
249
250   t->rounds--;
251
252   /** Call in recursive */
253   silc_fsm_next(fsm, test_thread3_st_start);
254   SILC_FSM_CONTINUE;
255 }
256
257 SILC_FSM_STATE(test_st_seventh)
258 {
259   Foo f = fsm_context;
260   int i;
261
262   SILC_LOG_DEBUG(("test_st_seventh"));
263
264
265   SILC_LOG_DEBUG(("Creating %d FSM threads", NUM_THREADS));
266   for (i = 0; i < NUM_THREADS; i++) {
267     f->threads[i].rounds = 10;
268     f->threads[i].f = f;
269     silc_fsm_event_init(&f->threads[i].sema, fsm);
270     silc_fsm_thread_init(&f->threads[i].thread, fsm,
271                          &f->threads[i], NULL, NULL, FALSE);
272     silc_fsm_start(&f->threads[i].thread, test_thread3_st_start);
273   }
274
275   /** Move to wait threads */
276   silc_fsm_next(fsm, test_st_eighth);
277   SILC_FSM_CONTINUE;
278 }
279
280 SILC_FSM_STATE(test_st_eighth)
281 {
282   Foo f = fsm_context;
283   int i;
284
285   for (i = 0; i < NUM_THREADS; i++) {
286     if (f->threads[i].finished == FALSE) {
287       SILC_FSM_EVENT_WAIT(&f->threads[i].sema);
288       f->threads[i].finished = TRUE;
289     }
290   }
291
292   SILC_LOG_DEBUG(("All %d threads terminated", NUM_THREADS));
293
294   /** Move to next thread */
295   silc_fsm_next(fsm, test_st_ninth);
296   SILC_FSM_CONTINUE;
297 }
298
299 SILC_FSM_STATE(test_thread4_st_start)
300 {
301   T *t = fsm_context;
302
303   if (t->rounds == 0) {
304     SILC_FSM_EVENT_SIGNAL(&t->sema);
305     SILC_FSM_FINISH;
306   }
307
308   t->rounds--;
309
310   /** Call in recursive */
311   silc_fsm_next(fsm, test_thread4_st_start);
312   SILC_FSM_CONTINUE;
313 }
314
315 SILC_FSM_STATE(test_st_ninth)
316 {
317   Foo f = fsm_context;
318   int i;
319
320   SILC_LOG_DEBUG(("test_st_ninth"));
321
322   SILC_LOG_DEBUG(("Creating FSM event"));
323   silc_fsm_event_init(&f->sema, fsm);
324
325   SILC_LOG_DEBUG(("Creating %d real FSM threads", NUM_THREADS));
326   for (i = 0; i < NUM_THREADS; i++) {
327     f->threads2[i].rounds = 10;
328     f->threads2[i].f = f;
329     silc_fsm_event_init(&f->threads2[i].sema, fsm);
330     silc_fsm_thread_init(&f->threads2[i].thread, fsm,
331                          &f->threads2[i], NULL, NULL, TRUE);
332     silc_fsm_start(&f->threads2[i].thread, test_thread4_st_start);
333   }
334
335   /** Move to wait threads */
336   silc_fsm_next(fsm, test_st_tenth);
337   SILC_FSM_CONTINUE;
338 }
339
340 SILC_FSM_STATE(test_st_tenth)
341 {
342   Foo f = fsm_context;
343   int i;
344
345   for (i = 0; i < NUM_THREADS; i++)
346     if (f->threads2[i].finished == FALSE) {
347       SILC_FSM_EVENT_WAIT(&f->threads2[i].sema);
348       f->threads2[i].finished = TRUE;
349     }
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   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   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   f->fsm = 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 }