build: fprintf, sprintf, sscanf need stdio.h
[platform/upstream/gstreamer.git] / gst / debugutils / tests.c
1 /* GStreamer
2  * Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
3  *
4  * includes code based on glibc 2.2.3's crypt/md5.c,
5  * Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. 
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "tests.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32
33 /*
34  *** LENGTH ***
35  */
36
37 typedef struct
38 {
39   gint64 value;
40 }
41 LengthTest;
42
43 static GParamSpec *
44 length_get_spec (const GstTestInfo * info, gboolean compare_value)
45 {
46   if (compare_value) {
47     return g_param_spec_int64 ("expected-length", "expected length",
48         "expected length of stream", -1, G_MAXINT64, -1,
49         G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
50   } else {
51     return g_param_spec_int64 ("length", "length", "length of stream",
52         -1, G_MAXINT64, -1, G_PARAM_READABLE);
53   }
54 }
55
56 static gpointer
57 length_new (const GstTestInfo * info)
58 {
59   return g_new0 (LengthTest, 1);
60 }
61
62 static void
63 length_add (gpointer test, GstBuffer * buffer)
64 {
65   LengthTest *t = test;
66
67   t->value += GST_BUFFER_SIZE (buffer);
68 }
69
70 static gboolean
71 length_finish (gpointer test, GValue * value)
72 {
73   LengthTest *t = test;
74
75   if (g_value_get_int64 (value) == -1)
76     return TRUE;
77
78   return t->value == g_value_get_int64 (value);
79 }
80
81 static void
82 length_get_value (gpointer test, GValue * value)
83 {
84   LengthTest *t = test;
85
86   g_value_set_int64 (value, t ? t->value : -1);
87 }
88
89 /*
90  *** BUFFER COUNT ***
91  */
92
93 static GParamSpec *
94 buffer_count_get_spec (const GstTestInfo * info, gboolean compare_value)
95 {
96   if (compare_value) {
97     return g_param_spec_int64 ("expected-buffer-count", "expected buffer count",
98         "expected number of buffers in stream",
99         -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
100   } else {
101     return g_param_spec_int64 ("buffer-count", "buffer count",
102         "number of buffers in stream", -1, G_MAXINT64, -1, G_PARAM_READABLE);
103   }
104 }
105
106 static void
107 buffer_count_add (gpointer test, GstBuffer * buffer)
108 {
109   LengthTest *t = test;
110
111   t->value++;
112 }
113
114 /*
115  *** TIMESTAMP / DURATION MATCHING ***
116  */
117
118 typedef struct
119 {
120   guint64 diff;
121   guint count;
122   GstClockTime expected;
123 }
124 TimeDurTest;
125
126 static GParamSpec *
127 timedur_get_spec (const GstTestInfo * info, gboolean compare_value)
128 {
129   if (compare_value) {
130     return g_param_spec_int64 ("allowed-timestamp-deviation",
131         "allowed timestamp deviation",
132         "allowed average difference in usec between timestamp of next buffer "
133         "and expected timestamp from analyzing last buffer",
134         -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
135   } else {
136     return g_param_spec_int64 ("timestamp-deviation",
137         "timestamp deviation",
138         "average difference in usec between timestamp of next buffer "
139         "and expected timestamp from analyzing last buffer",
140         -1, G_MAXINT64, -1, G_PARAM_READABLE);
141   }
142 }
143
144 static gpointer
145 timedur_new (const GstTestInfo * info)
146 {
147   TimeDurTest *ret = g_new0 (TimeDurTest, 1);
148
149   ret->expected = GST_CLOCK_TIME_NONE;
150
151   return ret;
152 }
153
154 static void
155 timedur_add (gpointer test, GstBuffer * buffer)
156 {
157   TimeDurTest *t = test;
158
159   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
160       GST_CLOCK_TIME_IS_VALID (t->expected)) {
161     t->diff += labs (GST_BUFFER_TIMESTAMP (buffer) - t->expected);
162     t->count++;
163   }
164   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
165       GST_BUFFER_DURATION_IS_VALID (buffer)) {
166     t->expected = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
167   } else {
168     t->expected = GST_CLOCK_TIME_NONE;
169   }
170 }
171
172 static gboolean
173 timedur_finish (gpointer test, GValue * value)
174 {
175   TimeDurTest *t = test;
176
177   if (g_value_get_int64 (value) == -1)
178     return TRUE;
179
180   return (t->diff / MAX (1, t->count)) <= g_value_get_int64 (value);
181 }
182
183 static void
184 timedur_get_value (gpointer test, GValue * value)
185 {
186   TimeDurTest *t = test;
187
188   g_value_set_int64 (value, t ? (t->diff / MAX (1, t->count)) : -1);
189 }
190
191 /*
192  *** MD5 ***
193  */
194
195 typedef struct
196 {
197   /* md5 information */
198   guint32 A;
199   guint32 B;
200   guint32 C;
201   guint32 D;
202
203   guint32 total[2];
204   guint32 buflen;
205   gchar buffer[128];
206
207   gchar result[33];
208 }
209 MD5Test;
210
211 static void md5_process_block (const void *buffer, size_t len, MD5Test * ctx);
212 static void md5_read_ctx (MD5Test * ctx, gchar * result);
213
214 static GParamSpec *
215 md5_get_spec (const GstTestInfo * info, gboolean compare_value)
216 {
217   if (compare_value) {
218     return g_param_spec_string ("expected-md5", "expected md5",
219         "expected md5 of processing the whole data",
220         "---", G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
221   } else {
222     return g_param_spec_string ("md5", "md5",
223         "md5 of processing the whole data", "---", G_PARAM_READABLE);
224   }
225 }
226
227 /* This array contains the bytes used to pad the buffer to the next
228    64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
229 static const guchar fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */  };
230
231 /* MD5 functions */
232 /* Initialize structure containing state of computation.
233    (RFC 1321, 3.3: Step 3)  */
234 static gpointer
235 md5_new (const GstTestInfo * info)
236 {
237   MD5Test *ctx = g_new (MD5Test, 1);
238
239   ctx->A = 0x67452301;
240   ctx->B = 0xefcdab89;
241   ctx->C = 0x98badcfe;
242   ctx->D = 0x10325476;
243
244   ctx->total[0] = ctx->total[1] = 0;
245   ctx->buflen = 0;
246
247   memset (ctx->result, 0, 33);
248
249   return ctx;
250 }
251
252 /* Process the remaining bytes in the internal buffer and the usual
253    prolog according to the standard and write the result to RESBUF.
254
255    IMPORTANT: On some systems it is required that RESBUF is correctly
256    aligned for a 32 bits value.  */
257 static gboolean
258 md5_finish (gpointer test, GValue * value)
259 {
260   MD5Test *ctx = test;
261   const gchar *str_val = g_value_get_string (value);
262
263   /* Take yet unprocessed bytes into account.  */
264   guint32 bytes = ctx->buflen;
265   size_t pad;
266
267   /* Now count remaining bytes.  */
268   ctx->total[0] += bytes;
269   if (ctx->total[0] < bytes)
270     ++ctx->total[1];
271
272   pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
273   memcpy (&ctx->buffer[bytes], fillbuf, pad);
274
275   /* Put the 64-bit file length in *bits* at the end of the buffer.  */
276   *(guint32 *) & ctx->buffer[bytes + pad] = GUINT32_TO_LE (ctx->total[0] << 3);
277   *(guint32 *) & ctx->buffer[bytes + pad + 4] =
278       GUINT32_TO_LE ((ctx->total[1] << 3) | (ctx->total[0] >> 29));
279
280   /* Process last bytes.  */
281   md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
282
283   md5_read_ctx (ctx, ctx->result);
284   if (g_str_equal (str_val, "---"))
285     return TRUE;
286   if (g_str_equal (str_val, ctx->result))
287     return TRUE;
288   return FALSE;
289 }
290
291 /* Put result from CTX in first 16 bytes following RESBUF.  The result
292    must be in little endian byte order.
293
294    IMPORTANT: On some systems it is required that RESBUF is correctly
295    aligned for a 32 bits value.  */
296 static void
297 md5_read_ctx (MD5Test * ctx, gchar * result)
298 {
299   guint32 resbuf[4];
300   guint i;
301
302   resbuf[0] = GUINT32_TO_LE (ctx->A);
303   resbuf[1] = GUINT32_TO_LE (ctx->B);
304   resbuf[2] = GUINT32_TO_LE (ctx->C);
305   resbuf[3] = GUINT32_TO_LE (ctx->D);
306
307   for (i = 0; i < 16; i++)
308     sprintf (result + i * 2, "%02x", ((guchar *) resbuf)[i]);
309 }
310
311 static void
312 md5_add (gpointer test, GstBuffer * gstbuffer)
313 {
314   const void *buffer = GST_BUFFER_DATA (gstbuffer);
315   gsize len = GST_BUFFER_SIZE (gstbuffer);
316   MD5Test *ctx = test;
317
318   /*const void aligned_buffer = buffer; */
319
320   /* When we already have some bits in our internal buffer concatenate
321      both inputs first.  */
322   if (ctx->buflen != 0) {
323     gsize left_over = ctx->buflen;
324     gsize add = 128 - left_over > len ? len : 128 - left_over;
325
326     /* Only put full words in the buffer.  */
327     /* Forcing alignment here appears to be only an optimization.
328      * The glibc source uses __alignof__, which seems to be a
329      * gratuitous usage of a GCC extension, when sizeof() will
330      * work fine.  (And don't question the sanity of using
331      * sizeof(guint32) instead of 4. */
332     /* add -= add % __alignof__ (guint32); */
333     add -= add % sizeof (guint32);
334
335     memcpy (&ctx->buffer[left_over], buffer, add);
336     ctx->buflen += add;
337
338     if (ctx->buflen > 64) {
339       md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
340
341       ctx->buflen &= 63;
342       /* The regions in the following copy operation cannot overlap.  */
343       memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen);
344     }
345
346     buffer = (const char *) buffer + add;
347     len -= add;
348   }
349
350   /* Process available complete blocks.  */
351   if (len > 64) {
352     md5_process_block (buffer, len & ~63, ctx);
353     buffer = (const char *) buffer + (len & ~63);
354     len &= 63;
355   }
356
357   /* Move remaining bytes in internal buffer.  */
358   if (len > 0) {
359     size_t left_over = ctx->buflen;
360
361     memcpy (&ctx->buffer[left_over], buffer, len);
362     left_over += len;
363     if (left_over >= 64) {
364       md5_process_block (ctx->buffer, 64, ctx);
365       left_over -= 64;
366       memcpy (ctx->buffer, &ctx->buffer[64], left_over);
367     }
368     ctx->buflen = left_over;
369   }
370 }
371
372
373 /* These are the four functions used in the four steps of the MD5 algorithm
374    and defined in the RFC 1321.  The first function is a little bit optimized
375    (as found in Colin Plumbs public domain implementation).  */
376 /* #define FF(b, c, d) ((b & c) | (~b & d)) */
377 #define FF(b, c, d) (d ^ (b & (c ^ d)))
378 #define FG(b, c, d) FF (d, b, c)
379 #define FH(b, c, d) (b ^ c ^ d)
380 #define FI(b, c, d) (c ^ (b | ~d))
381
382 static void
383 md5_process_block (const void *buffer, size_t len, MD5Test * ctx)
384 {
385 /* Process LEN bytes of BUFFER, accumulating context into CTX.
386    It is assumed that LEN % 64 == 0.  */
387   guint32 correct_words[16];
388   const guint32 *words = buffer;
389   size_t nwords = len / sizeof (guint32);
390   const guint32 *endp = words + nwords;
391   guint32 A = ctx->A;
392   guint32 B = ctx->B;
393   guint32 C = ctx->C;
394   guint32 D = ctx->D;
395
396   /* First increment the byte count.  RFC 1321 specifies the possible
397      length of the file up to 2^64 bits.  Here we only compute the
398      number of bytes.  Do a double word increment.  */
399   ctx->total[0] += len;
400   if (ctx->total[0] < len)
401     ++ctx->total[1];
402
403   /* Process all bytes in the buffer with 64 bytes in each round of
404      the loop.  */
405   while (words < endp) {
406     guint32 *cwp = correct_words;
407     guint32 A_save = A;
408     guint32 B_save = B;
409     guint32 C_save = C;
410     guint32 D_save = D;
411
412     /* First round: using the given function, the context and a constant
413        the next context is computed.  Because the algorithms processing
414        unit is a 32-bit word and it is determined to work on words in
415        little endian byte order we perhaps have to change the byte order
416        before the computation.  To reduce the work for the next steps
417        we store the swapped words in the array CORRECT_WORDS.  */
418
419 #define OP(a, b, c, d, s, T)                                            \
420       do                                                                \
421         {                                                               \
422           a += FF (b, c, d) + (*cwp++ = GUINT32_TO_LE (*words)) + T;            \
423           ++words;                                                      \
424           CYCLIC (a, s);                                                \
425           a += b;                                                       \
426         }                                                               \
427       while (0)
428
429     /* It is unfortunate that C does not provide an operator for
430        cyclic rotation.  Hope the C compiler is smart enough.  */
431 #define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
432
433     /* Before we start, one word to the strange constants.
434        They are defined in RFC 1321 as
435
436        T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
437      */
438
439     /* Round 1.  */
440     OP (A, B, C, D, 7, 0xd76aa478);
441     OP (D, A, B, C, 12, 0xe8c7b756);
442     OP (C, D, A, B, 17, 0x242070db);
443     OP (B, C, D, A, 22, 0xc1bdceee);
444     OP (A, B, C, D, 7, 0xf57c0faf);
445     OP (D, A, B, C, 12, 0x4787c62a);
446     OP (C, D, A, B, 17, 0xa8304613);
447     OP (B, C, D, A, 22, 0xfd469501);
448     OP (A, B, C, D, 7, 0x698098d8);
449     OP (D, A, B, C, 12, 0x8b44f7af);
450     OP (C, D, A, B, 17, 0xffff5bb1);
451     OP (B, C, D, A, 22, 0x895cd7be);
452     OP (A, B, C, D, 7, 0x6b901122);
453     OP (D, A, B, C, 12, 0xfd987193);
454     OP (C, D, A, B, 17, 0xa679438e);
455     OP (B, C, D, A, 22, 0x49b40821);
456
457     /* For the second to fourth round we have the possibly swapped words
458        in CORRECT_WORDS.  Redefine the macro to take an additional first
459        argument specifying the function to use.  */
460 #undef OP
461 #define OP(f, a, b, c, d, k, s, T)                                      \
462       do                                                                \
463         {                                                               \
464           a += f (b, c, d) + correct_words[k] + T;                      \
465           CYCLIC (a, s);                                                \
466           a += b;                                                       \
467         }                                                               \
468       while (0)
469
470     /* Round 2.  */
471     OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
472     OP (FG, D, A, B, C, 6, 9, 0xc040b340);
473     OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
474     OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
475     OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
476     OP (FG, D, A, B, C, 10, 9, 0x02441453);
477     OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
478     OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
479     OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
480     OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
481     OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
482     OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
483     OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
484     OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
485     OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
486     OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
487
488     /* Round 3.  */
489     OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
490     OP (FH, D, A, B, C, 8, 11, 0x8771f681);
491     OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
492     OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
493     OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
494     OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
495     OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
496     OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
497     OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
498     OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
499     OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
500     OP (FH, B, C, D, A, 6, 23, 0x04881d05);
501     OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
502     OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
503     OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
504     OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
505
506     /* Round 4.  */
507     OP (FI, A, B, C, D, 0, 6, 0xf4292244);
508     OP (FI, D, A, B, C, 7, 10, 0x432aff97);
509     OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
510     OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
511     OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
512     OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
513     OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
514     OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
515     OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
516     OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
517     OP (FI, C, D, A, B, 6, 15, 0xa3014314);
518     OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
519     OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
520     OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
521     OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
522     OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
523
524     /* Add the starting values of the context.  */
525     A += A_save;
526     B += B_save;
527     C += C_save;
528     D += D_save;
529   }
530
531   /* Put checksum in context given as argument.  */
532   ctx->A = A;
533   ctx->B = B;
534   ctx->C = C;
535   ctx->D = D;
536 }
537
538 static void
539 md5_get_value (gpointer test, GValue * value)
540 {
541   MD5Test *ctx = test;
542
543   if (!ctx) {
544     g_value_set_string (value, "---");
545   } else if (ctx->result[0] == 0) {
546     gchar *str = g_new (gchar, 33);
547
548     str[32] = 0;
549     md5_read_ctx (ctx, str);
550     g_value_take_string (value, str);
551   } else {
552     g_value_set_string (value, ctx->result);
553   }
554 }
555
556 /*
557  *** TESTINFO ***
558  */
559
560 const GstTestInfo tests[] = {
561   {length_get_spec, length_new, length_add,
562       length_finish, length_get_value, g_free},
563   {buffer_count_get_spec, length_new, buffer_count_add,
564       length_finish, length_get_value, g_free},
565   {timedur_get_spec, timedur_new, timedur_add,
566       timedur_finish, timedur_get_value, g_free},
567   {md5_get_spec, md5_new, md5_add,
568       md5_finish, md5_get_value, g_free}
569 };