working again
[oweals/gnunet.git] / src / util / test_common_logging_runtime_loglevels.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/test_common_logging_runtime_loglevels.c
23  * @brief testcase for the logging module  (runtime log level adjustment)
24  * @author LRN
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28
29 #define VERBOSE GNUNET_NO
30
31 /**
32  * How much time the child is allowed to waste on skipped log calls, at most.
33  * Raspberry Pi takes 33 microseconds tops, this is 3x that value.
34  */
35 #define MAX_SKIP_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 100).rel_value_us
36
37 /**
38  * How much time non-skipped log call should take, at least.
39  * Keep in sync with the value in the dummy!
40  */
41 #define OUTPUT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 200).rel_value_us
42
43 static int ok;
44
45 static int phase = 0;
46
47 static struct GNUNET_OS_Process *proc;
48
49 /* Pipe to read from started processes stdout (on read end) */
50 static struct GNUNET_DISK_PipeHandle *pipe_stdout;
51
52 static GNUNET_SCHEDULER_TaskIdentifier die_task;
53
54 static GNUNET_SCHEDULER_TaskIdentifier read_task;
55
56 static void
57 runone (void);
58
59
60 static void
61 end_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
62 {
63   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending phase %d, ok is %d\n", phase,
64               ok);
65   if (NULL != proc)
66   {
67     if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
68     {
69       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
70     }
71     GNUNET_OS_process_wait (proc);
72     GNUNET_OS_process_destroy (proc);
73     proc = NULL;
74   }
75   if (GNUNET_SCHEDULER_NO_TASK != read_task)
76   {
77     GNUNET_SCHEDULER_cancel (read_task);
78     read_task = GNUNET_SCHEDULER_NO_TASK;
79   }
80   GNUNET_DISK_pipe_close (pipe_stdout);
81   if (ok == 1)
82   {
83     if (phase < 9)
84     {
85       phase += 1;
86       runone ();
87     }
88     else
89       ok = 0;
90   }
91   else
92     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failing\n");
93 }
94
95
96 static char *
97 read_output_line (int phase_from1, int phase_to1, int phase_from2,
98                   int phase_to2, char c, const char *expect_level,
99                   long delay_morethan, long delay_lessthan, int phase,
100                   char *p,
101                   int *len, long *delay, char level[8])
102 {
103   char *r = p;
104   char t[7];
105   int i, j, stop = 0;
106   int level_matches;
107   int delay_is_sane;
108   int delay_is_a_dummy;
109   int delay_outside_of_range;
110
111   j = 0;
112   int stage = 0;
113
114   if (!(phase >= phase_from1 && phase <= phase_to1) &&
115       !(phase >= phase_from2 && phase <= phase_to2))
116     return p;
117 #if 0
118   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
119               "Trying to match '%c%s \\d\\r\\n' on %s\n", c, expect_level, p);
120 #endif
121   for (i = 0; i < *len && !stop; i++)
122   {
123     switch (stage)
124     {
125     case 0:                    /* read first char */
126       if (r[i] != c)
127       {
128         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected '%c', but got '%c'\n", c,
129                     r[i]);
130         GNUNET_break (0);
131         return NULL;
132       }
133       stage += 1;
134       break;
135     case 1:                    /* read at most 7 char-long error level string, finished by ' ' */
136       if (r[i] == ' ')
137       {
138         level[j] = '\0';
139         stage += 1;
140         j = 0;
141       }
142       else if (i == 8)
143       {
144         GNUNET_break (0);
145         ok = 2;
146         return NULL;
147       }
148       else
149         level[j++] = r[i];
150       break;
151     case 2:                    /* read the delay, finished by '\n' */
152       t[j++] = r[i];
153 #if WINDOWS
154       if (r[i] == '\r' && r[i + 1] == '\n')
155       {
156         i += 1;
157         t[j - 1] = '\0';
158         *delay = strtol (t, NULL, 10);
159         stop = 1;
160       }
161 #else
162       if (r[i] == '\n')
163       {
164         t[j - 1] = '\0';
165         *delay = strtol (t, NULL, 10);
166         stop = 1;
167       }
168 #endif
169       break;
170     }
171   }
172   level_matches = (strcmp (expect_level, level) == 0);
173   delay_is_sane = (*delay >= 0) && (*delay <= 1000000);
174   delay_is_a_dummy = (c == 'L');
175   /* Delay must be either less than 'lessthan' (log call is skipped)
176    * or more than 'morethan' (log call is not skipped)
177    */
178   delay_outside_of_range = ((*delay < delay_lessthan) || (*delay > delay_morethan));
179   if (delay_is_a_dummy)
180     delay_outside_of_range = 1;
181
182   if (!stop)
183     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Wrong log format?\n");
184   if (!level_matches)
185     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Wrong log level\n");
186   if (!delay_is_sane)
187     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Delay %ld is insane\n", *delay);
188   if (!delay_outside_of_range)
189     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190                 "Delay %ld is not outside of range (%ld ; %ld)\n",
191                 *delay, delay_lessthan, delay_morethan, c);
192   if (!stop || !level_matches || !delay_is_sane || !delay_outside_of_range)
193     return NULL;
194   *len = *len - i;
195   return &r[i];
196 }
197
198 /**
199  * Up to 8 non-skipped GNUNET_log() calls
200  * + extra line with delay for each one
201  */
202 #define LOG_MAX_NUM_LINES (8 * 2)
203 /**
204  * Actual message is 17 chars at most
205  */
206 #define LOG_MAX_LINE_LENGTH (17)
207
208 #define LOG_BUFFER_SIZE LOG_MAX_NUM_LINES * LOG_MAX_LINE_LENGTH
209
210 static char buf[LOG_BUFFER_SIZE];
211
212 static char *buf_ptr;
213
214 static int bytes;
215
216
217 static void
218 read_call (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
219 {
220   const struct GNUNET_DISK_FileHandle *stdout_read_handle = cls;
221   char level[8];
222   long delay;
223   long delays[8];
224   int rd;
225
226   read_task = GNUNET_SCHEDULER_NO_TASK;
227   rd = GNUNET_DISK_file_read (stdout_read_handle, buf_ptr,
228                               sizeof (buf) - bytes);
229   if (rd > 0)
230   {
231     buf_ptr += rd;
232     bytes += rd;
233 #if VERBOSE
234     FPRINTF (stderr, "got %d bytes, reading more\n", rd);
235 #endif
236     read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
237                                                 stdout_read_handle, &read_call,
238                                                 (void*) stdout_read_handle);
239     return;
240   }
241
242 #if VERBOSE
243   FPRINTF (stderr, "bytes is %d:%s\n", bytes, buf);
244 #endif
245
246   /* +------CHILD OUTPUT--
247    * |      SOFT     HARD
248    * |    E W I D  E W I D
249    * | 0E *        * *
250    * | 1W * *      * *
251    * P 2I * * *    * *
252    * H 3D * * * *  * *
253    * A
254    * S 4E *        *
255    * E 5W * *      * *
256    * | 6I * * *    * * *
257    * | 7D * * * *  * * * *
258    * | 8  * *      * *
259    * | 9  * *      * *
260    */
261   char *p = buf;
262
263   if (bytes == LOG_BUFFER_SIZE ||
264       !(p =
265         read_output_line (0, 3, 4, 9, 'L', "ERROR", -1,
266                           1, phase, p,
267                           &bytes, &delay, level)) ||
268       !(p =
269         read_output_line (0, 3, 4, 9, '1', "ERROR", OUTPUT_DELAY,
270                           MAX_SKIP_DELAY, phase, p,
271                           &bytes, &delays[0], level)) ||
272       !(p =
273         read_output_line (1, 3, 5, 9, 'L', "WARNING", -1,
274                           1, phase, p,
275                           &bytes, &delay, level)) ||
276       !(p =
277         read_output_line (0, 3, 4, 9, '1', "WARNING", OUTPUT_DELAY,
278                           MAX_SKIP_DELAY, phase, p,
279                           &bytes, &delays[1], level)) ||
280       !(p =
281         read_output_line (2, 3, 6, 7, 'L', "INFO", -1,
282                           1, phase, p,
283                           &bytes, &delay, level)) ||
284       !(p =
285         read_output_line (0, 3, 4, 9, '1', "INFO", OUTPUT_DELAY,
286                           MAX_SKIP_DELAY, phase, p,
287                           &bytes, &delays[2], level)) ||
288       !(p =
289         read_output_line (3, 3, 7, 7, 'L', "DEBUG", -1,
290                           1, phase, p,
291                           &bytes, &delay, level)) ||
292       !(p =
293         read_output_line (0, 3, 4, 9, '1', "DEBUG", OUTPUT_DELAY,
294                           MAX_SKIP_DELAY, phase, p,
295                           &bytes, &delays[3], level)) ||
296       !(p =
297         read_output_line (0, 3, 4, 9, 'L', "ERROR", -1,
298                           1, phase, p,
299                           &bytes, &delay, level)) ||
300       !(p =
301         read_output_line (0, 3, 4, 9, '2', "ERROR", OUTPUT_DELAY,
302                           MAX_SKIP_DELAY, phase, p,
303                           &bytes, &delays[4], level)) ||
304       !(p =
305         read_output_line (0, 3, 5, 9, 'L', "WARNING", -1,
306                           1, phase, p,
307                           &bytes, &delay, level)) ||
308       !(p =
309         read_output_line (0, 3, 4, 9, '2', "WARNING", OUTPUT_DELAY,
310                           MAX_SKIP_DELAY, phase, p,
311                           &bytes, &delays[5], level)) ||
312       !(p =
313         read_output_line (-1, -1, 6, 7, 'L', "INFO", -1,
314                           1, phase, p,
315                           &bytes, &delay, level)) ||
316       !(p =
317         read_output_line (0, 3, 4, 9, '2', "INFO", OUTPUT_DELAY,
318                           MAX_SKIP_DELAY, phase, p,
319                           &bytes, &delays[6], level)) ||
320       !(p =
321         read_output_line (-1, -1, 7, 7, 'L', "DEBUG", -1,
322                           1, phase, p,
323                           &bytes, &delay, level)) ||
324       !(p =
325         read_output_line (0, 3, 4, 9, '2', "DEBUG", OUTPUT_DELAY,
326                           MAX_SKIP_DELAY, phase, p,
327                           &bytes, &delays[7], level)))
328   {
329     if (bytes == LOG_BUFFER_SIZE)
330       FPRINTF (stderr, "%s",  "Ran out of buffer space!\n");
331     GNUNET_break (0);
332     ok = 2;
333     GNUNET_SCHEDULER_cancel (die_task);
334     GNUNET_SCHEDULER_add_now (&end_task, NULL);
335     return;
336   }
337
338   GNUNET_SCHEDULER_cancel (die_task);
339   GNUNET_SCHEDULER_add_now (&end_task, NULL);
340 }
341
342
343 static void
344 runone ()
345 {
346   const struct GNUNET_DISK_FileHandle *stdout_read_handle;
347
348   pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
349
350   if (pipe_stdout == NULL)
351   {
352     GNUNET_break (0);
353     ok = 2;
354     return;
355   }
356
357   putenv ("GNUNET_LOG=");
358   putenv ("GNUNET_FORCE_LOG=");
359   putenv ("GNUNET_FORCE_LOGFILE=");
360   switch (phase)
361   {
362   case 0:
363     putenv ("GNUNET_LOG=;;;;ERROR");
364     break;
365   case 1:
366     putenv ("GNUNET_LOG=;;;;WARNING");
367     break;
368   case 2:
369     putenv ("GNUNET_LOG=;;;;INFO");
370     break;
371   case 3:
372     putenv ("GNUNET_LOG=;;;;DEBUG");
373     break;
374   case 4:
375     putenv ("GNUNET_FORCE_LOG=;;;;ERROR");
376     break;
377   case 5:
378     putenv ("GNUNET_FORCE_LOG=;;;;WARNING");
379     break;
380   case 6:
381     putenv ("GNUNET_FORCE_LOG=;;;;INFO");
382     break;
383   case 7:
384     putenv ("GNUNET_FORCE_LOG=;;;;DEBUG");
385     break;
386   case 8:
387     putenv ("GNUNET_LOG=blah;;;;ERROR");
388     break;
389   case 9:
390     putenv ("GNUNET_FORCE_LOG=blah;;;;ERROR");
391     break;
392   }
393
394   proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, pipe_stdout,
395 #if MINGW
396                                   "test_common_logging_dummy",
397 #else
398                                   "./test_common_logging_dummy",
399 #endif
400                                   "test_common_logging_dummy", NULL);
401   GNUNET_assert (NULL != proc);
402   putenv ("GNUNET_FORCE_LOG=");
403   putenv ("GNUNET_LOG=");
404
405   /* Close the write end of the read pipe */
406   GNUNET_DISK_pipe_close_end (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
407
408   stdout_read_handle =
409       GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ);
410
411   die_task =
412       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
413                                     (GNUNET_TIME_UNIT_SECONDS, 10), &end_task,
414                                     NULL);
415
416   bytes = 0;
417   buf_ptr = buf;
418   memset (&buf, 0, sizeof (buf));
419
420   read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
421                                               stdout_read_handle, &read_call,
422                                               (void*) stdout_read_handle);
423 }
424
425
426 static void
427 task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
428 {
429   phase = 0;
430   runone ();
431 }
432
433
434 int
435 main (int argc, char *argv[])
436 {
437   GNUNET_log_setup ("test-common-logging-runtime-loglevels",
438                     "WARNING",
439                     NULL);
440   ok = 1;
441   GNUNET_SCHEDULER_run (&task, &ok);
442   return ok;
443 }
444
445 /* end of test_common_logging_runtime_loglevels.c */