2005-02-05 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-message-factory.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-message-factory.c Generator of valid and invalid message data for test suite
3  *
4  * Copyright (C) 2005 Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include <config.h>
24
25 #ifdef DBUS_BUILD_TESTS
26 #include "dbus-message-factory.h"
27 #include "dbus-message-private.h"
28 #include "dbus-test.h"
29 #include <stdio.h>
30
31 typedef enum
32   {
33     CHANGE_TYPE_ADJUST,
34     CHANGE_TYPE_ABSOLUTE
35   } ChangeType;
36
37 #define BYTE_ORDER_OFFSET  0
38 #define BODY_LENGTH_OFFSET 4
39
40 static void
41 iter_recurse (DBusMessageDataIter *iter)
42 {
43   iter->depth += 1;
44   _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING);
45   _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
46 }
47
48 static int
49 iter_get_sequence (DBusMessageDataIter *iter)
50 {
51   _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
52   return iter->sequence_nos[iter->depth];
53 }
54
55 static void
56 iter_set_sequence (DBusMessageDataIter *iter,
57                    int                  sequence)
58 {
59   _dbus_assert (sequence >= 0);
60   iter->sequence_nos[iter->depth] = sequence;
61 }
62
63 static void
64 iter_unrecurse (DBusMessageDataIter *iter)
65 {
66   iter->depth -= 1;
67   _dbus_assert (iter->depth >= 0);
68 }
69
70 static void
71 iter_next (DBusMessageDataIter *iter)
72 {
73   iter->sequence_nos[iter->depth] += 1;
74 }
75
76 static dbus_bool_t
77 iter_first_in_series (DBusMessageDataIter *iter)
78 {
79   int i;
80
81   i = iter->depth;
82   while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
83     {
84       if (iter->sequence_nos[i] != 0)
85         return FALSE;
86       ++i;
87     }
88   return TRUE;
89 }
90
91 typedef dbus_bool_t (* DBusInnerGeneratorFunc)   (DBusMessageDataIter *iter,
92                                                   DBusMessage        **message_p);
93 typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter,
94                                                   DBusString          *data,
95                                                   DBusValidity        *expected_validity);
96
97 static void
98 set_reply_serial (DBusMessage *message)
99 {
100   if (message == NULL)
101     _dbus_assert_not_reached ("oom");
102   if (!dbus_message_set_reply_serial (message, 100))
103     _dbus_assert_not_reached ("oom");
104 }
105
106 static dbus_bool_t
107 generate_trivial_inner (DBusMessageDataIter *iter,
108                         DBusMessage        **message_p)
109 {
110   DBusMessage *message;
111
112   switch (iter_get_sequence (iter))
113     {
114     case 0:
115       message = dbus_message_new_method_call ("org.freedesktop.TextEditor",
116                                               "/foo/bar",
117                                               "org.freedesktop.DocumentFactory",
118                                               "Create");
119       break;
120     case 1:
121       message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
122       set_reply_serial (message);
123       break;
124     case 2:
125       message = dbus_message_new_signal ("/foo/bar",
126                                          "org.freedesktop.DocumentFactory",
127                                          "Created");
128       break;
129     case 3:
130       message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
131
132       if (!dbus_message_set_error_name (message,
133                                         "org.freedesktop.TestErrorName"))
134         _dbus_assert_not_reached ("oom");
135       
136       {
137         DBusMessageIter iter;
138         const char *v_STRING = "This is an error";
139         
140         dbus_message_iter_init_append (message, &iter);
141         if (!dbus_message_iter_append_basic (&iter,
142                                              DBUS_TYPE_STRING,
143                                              &v_STRING))
144           _dbus_assert_not_reached ("oom");
145       }
146       
147       set_reply_serial (message);
148       break;
149     default:
150       return FALSE;
151     }
152   
153   if (message == NULL)
154     _dbus_assert_not_reached ("oom");
155
156   *message_p = message;
157   
158   return TRUE;
159 }
160
161 static dbus_bool_t
162 generate_many_bodies_inner (DBusMessageDataIter *iter,
163                             DBusMessage        **message_p)
164 {
165   DBusMessage *message;
166   DBusString signature;
167   DBusString body;
168   
169   message = dbus_message_new_method_call ("org.freedesktop.Foo",
170                                           "/",
171                                           "org.freedesktop.Blah",
172                                           "NahNahNah");
173   if (message == NULL)
174     _dbus_assert_not_reached ("oom");
175
176   set_reply_serial (message);
177
178   if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
179     _dbus_assert_not_reached ("oom");
180   
181   if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
182                                                 message->byte_order,
183                                                 &signature, &body))
184     {
185       const char *v_SIGNATURE;
186
187       v_SIGNATURE = _dbus_string_get_const_data (&signature);
188       if (!_dbus_header_set_field_basic (&message->header,
189                                          DBUS_HEADER_FIELD_SIGNATURE,
190                                          DBUS_TYPE_SIGNATURE,
191                                          &v_SIGNATURE))
192         _dbus_assert_not_reached ("oom");
193
194       if (!_dbus_string_move (&body, 0, &message->body, 0))
195         _dbus_assert_not_reached ("oom");
196
197       _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
198                                 _dbus_string_get_length (&message->body),
199                                 message->byte_order);
200       
201       *message_p = message;
202     }
203   else
204     {
205       dbus_message_unref (message);
206       *message_p = NULL;
207     }
208   
209   _dbus_string_free (&signature);
210   _dbus_string_free (&body);
211
212   return *message_p != NULL;
213 }
214
215 static dbus_bool_t
216 generate_outer (DBusMessageDataIter   *iter,
217                 DBusString            *data,
218                 DBusValidity          *expected_validity,
219                 DBusInnerGeneratorFunc func)
220 {
221   DBusMessage *message;
222
223   message = NULL;
224   if (!(*func)(iter, &message))
225     return FALSE;
226
227   iter_next (iter);
228   
229   _dbus_assert (message != NULL);
230
231   _dbus_message_set_serial (message, 1);
232   _dbus_message_lock (message);
233   
234   *expected_validity = DBUS_VALID;
235
236   /* move for efficiency, since we'll nuke the message anyway */
237   if (!_dbus_string_move (&message->header.data, 0,
238                           data, 0))
239     _dbus_assert_not_reached ("oom");
240
241   if (!_dbus_string_copy (&message->body, 0,
242                           data, _dbus_string_get_length (data)))
243     _dbus_assert_not_reached ("oom");
244
245   dbus_message_unref (message);
246
247   return TRUE;
248 }
249
250 static dbus_bool_t
251 generate_trivial (DBusMessageDataIter   *iter,
252                   DBusString            *data,
253                   DBusValidity          *expected_validity)
254 {
255   return generate_outer (iter, data, expected_validity,
256                          generate_trivial_inner);
257 }
258
259 static dbus_bool_t
260 generate_many_bodies (DBusMessageDataIter   *iter,
261                       DBusString            *data,
262                       DBusValidity          *expected_validity)
263 {
264   return generate_outer (iter, data, expected_validity,
265                          generate_many_bodies_inner);
266 }
267
268 static dbus_bool_t
269 generate_wrong_length (DBusMessageDataIter *iter,
270                        DBusString          *data,
271                        DBusValidity        *expected_validity)
272 {
273   int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1,
274                     1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 };
275   int adjust;
276   int len_seq;
277
278  restart:
279   len_seq = iter_get_sequence (iter);
280   if (len_seq == _DBUS_N_ELEMENTS (lengths))
281     return FALSE;
282
283   _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths));
284   
285   iter_recurse (iter);
286   if (!generate_many_bodies (iter, data, expected_validity))
287     {
288       iter_set_sequence (iter, 0); /* reset to first body */
289       iter_unrecurse (iter);
290       iter_next (iter);            /* next length adjustment */
291       goto restart;
292     }
293   iter_unrecurse (iter);
294
295   adjust = lengths[len_seq];
296
297   if (adjust < 0)
298     {
299       if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE)
300         _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE);
301       else
302         _dbus_string_shorten (data, - adjust);
303       *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON;
304     }
305   else
306     {      
307       if (!_dbus_string_lengthen (data, adjust))
308         _dbus_assert_not_reached ("oom");
309       *expected_validity = DBUS_INVALID_TOO_MUCH_DATA;
310     }
311
312   /* Fixup lengths */
313   {
314     int old_body_len;
315     int new_body_len;
316     int byte_order;
317     
318     _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE);
319     
320     byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
321     old_body_len = _dbus_marshal_read_uint32 (data,
322                                               BODY_LENGTH_OFFSET,
323                                               byte_order,
324                                               NULL);
325     _dbus_assert (old_body_len < _dbus_string_get_length (data));
326     new_body_len = old_body_len + adjust;
327     if (new_body_len < 0)
328       {
329         new_body_len = 0;
330         /* we just munged the header, and aren't sure how */
331         *expected_validity = DBUS_VALIDITY_UNKNOWN;
332       }
333
334     _dbus_verbose ("changing body len from %u to %u by adjust %d\n",
335                    old_body_len, new_body_len, adjust);
336     
337     _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
338                               new_body_len,
339                               byte_order);
340   }
341
342   return TRUE;
343 }
344
345 static dbus_bool_t
346 generate_byte_changed (DBusMessageDataIter *iter,
347                        DBusString          *data,
348                        DBusValidity        *expected_validity)
349 {
350   int byte_seq;
351   int v_BYTE;
352
353   /* This is a little convoluted to make the bodies the
354    * outer loop and each byte of each body the inner
355    * loop
356    */
357
358  restart:
359   if (!generate_many_bodies (iter, data, expected_validity))
360     return FALSE;
361
362   iter_recurse (iter);
363   byte_seq = iter_get_sequence (iter);
364   iter_next (iter);
365   iter_unrecurse (iter);
366   
367   if (byte_seq == _dbus_string_get_length (data))
368     {
369       _dbus_string_set_length (data, 0);
370       /* reset byte count */
371       iter_recurse (iter);
372       iter_set_sequence (iter, 0);
373       iter_unrecurse (iter);
374       goto restart;
375     }
376   else
377     {
378       /* Undo the "next" in generate_many_bodies */
379       iter_set_sequence (iter, iter_get_sequence (iter) - 1);
380     }
381
382   _dbus_assert (byte_seq < _dbus_string_get_length (data));
383   v_BYTE = _dbus_string_get_byte (data, byte_seq);
384   v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */
385   _dbus_string_set_byte (data, byte_seq, v_BYTE);
386   *expected_validity = DBUS_VALIDITY_UNKNOWN;
387
388   return TRUE;
389 }
390
391 typedef struct
392 {
393   ChangeType type;
394   dbus_uint32_t value; /* cast to signed for adjusts */
395 } UIntChange;
396
397 static const UIntChange uint32_changes[] = {
398   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -1 },
399   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -2 },
400   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -3 },
401   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 1 },
402   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 2 },
403   { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 3 },
404   { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX },
405   { CHANGE_TYPE_ABSOLUTE, 0 },
406   { CHANGE_TYPE_ABSOLUTE, 1 },
407   { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 1 },
408   { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 5 }
409 };
410
411 static dbus_bool_t
412 generate_uint32_changed (DBusMessageDataIter *iter,
413                          DBusString          *data,
414                          DBusValidity        *expected_validity)
415 {
416   int body_seq;
417   int byte_seq;
418   int change_seq;
419   dbus_uint32_t v_UINT32;
420   int byte_order;
421   const UIntChange *change;
422   int base_depth;
423
424   /* Outer loop is each body, next loop is each change,
425    * inner loop is each change location
426    */
427
428   base_depth = iter->depth;
429   
430  next_body:
431   _dbus_assert (iter->depth == (base_depth + 0));
432   _dbus_string_set_length (data, 0);
433   body_seq = iter_get_sequence (iter);
434   
435   if (!generate_many_bodies (iter, data, expected_validity))
436     return FALSE;
437
438   _dbus_assert (iter->depth == (base_depth + 0));
439
440   iter_set_sequence (iter, body_seq); /* undo the "next" from generate_many_bodies */
441   iter_recurse (iter);
442  next_change:
443   _dbus_assert (iter->depth == (base_depth + 1));
444   change_seq = iter_get_sequence (iter);
445   
446   if (change_seq == _DBUS_N_ELEMENTS (uint32_changes))
447     {
448       /* Reset change count */
449       iter_set_sequence (iter, 0);
450       iter_unrecurse (iter);
451       iter_next (iter);
452       goto next_body;
453     }
454
455   _dbus_assert (iter->depth == (base_depth + 1));
456   
457   iter_recurse (iter);
458   _dbus_assert (iter->depth == (base_depth + 2));
459   byte_seq = iter_get_sequence (iter);
460   /* skip 4 bytes at a time */
461   iter_next (iter);
462   iter_next (iter);
463   iter_next (iter);
464   iter_next (iter);
465   iter_unrecurse (iter);
466
467   _dbus_assert (_DBUS_ALIGN_VALUE (byte_seq, 4) == (unsigned) byte_seq);
468   if (byte_seq >= (_dbus_string_get_length (data) - 4))
469     {
470       /* reset byte count */
471       _dbus_assert (iter->depth == (base_depth + 1));
472       iter_recurse (iter);
473       _dbus_assert (iter->depth == (base_depth + 2));
474       iter_set_sequence (iter, 0);
475       iter_unrecurse (iter);
476       _dbus_assert (iter->depth == (base_depth + 1));
477       iter_next (iter);
478       goto next_change;
479     }
480   
481   _dbus_assert (byte_seq <= (_dbus_string_get_length (data) - 4));
482
483   byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
484   
485   v_UINT32 = _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL);
486
487   change = &uint32_changes[change_seq];
488
489   if (change->type == CHANGE_TYPE_ADJUST)
490     {
491       v_UINT32 += (int) change->value;
492     }
493   else
494     {
495       v_UINT32 = change->value;
496     }
497
498 #if 0
499   printf ("body %d change %d pos %d ",
500           body_seq, change_seq, byte_seq);
501
502   if (change->type == CHANGE_TYPE_ADJUST)
503     printf ("adjust by %d", (int) change->value);
504   else
505     printf ("set to %u", change->value);
506   
507   printf (" \t%u -> %u\n",
508           _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL),
509           v_UINT32);
510 #endif
511   
512   _dbus_marshal_set_uint32 (data, byte_seq, v_UINT32, byte_order);
513   *expected_validity = DBUS_VALIDITY_UNKNOWN;
514
515   _dbus_assert (iter->depth == (base_depth + 1));
516   iter_unrecurse (iter);
517   _dbus_assert (iter->depth == (base_depth + 0));
518           
519   return TRUE;
520 }
521
522 typedef struct
523 {
524   const char *name;
525   DBusMessageGeneratorFunc func;  
526 } DBusMessageGenerator;
527
528 static const DBusMessageGenerator generators[] = {
529   { "trivial example of each message type", generate_trivial },
530   { "assorted arguments", generate_many_bodies },
531   { "each uint32 modified", generate_uint32_changed },
532   { "wrong body lengths", generate_wrong_length },
533   { "each byte modified", generate_byte_changed }
534 };
535
536 void
537 _dbus_message_data_free (DBusMessageData *data)
538 {
539   _dbus_string_free (&data->data);
540 }
541
542 void
543 _dbus_message_data_iter_init (DBusMessageDataIter *iter)
544 {
545   int i;
546   
547   iter->depth = 0;
548   i = 0;
549   while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
550     {
551       iter->sequence_nos[i] = 0;
552       ++i;
553     }
554   iter->count = 0;
555 }
556
557 dbus_bool_t
558 _dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter,
559                                       DBusMessageData     *data)
560 {
561   DBusMessageGeneratorFunc func;
562   int generator;
563
564  restart:
565   generator = iter_get_sequence (iter);
566   
567   if (generator == _DBUS_N_ELEMENTS (generators))
568     return FALSE;
569
570   iter_recurse (iter);
571   
572   if (iter_first_in_series (iter))
573     {
574       printf (" testing message loading: %s ", generators[generator].name);
575       fflush (stdout);
576     }
577   
578   func = generators[generator].func;
579
580   if (!_dbus_string_init (&data->data))
581     _dbus_assert_not_reached ("oom");
582   
583   if ((*func)(iter, &data->data, &data->expected_validity))
584     ;
585   else
586     {
587       iter_set_sequence (iter, 0);
588       iter_unrecurse (iter);
589       iter_next (iter); /* next generator */
590       _dbus_string_free (&data->data);
591       printf ("%d test loads cumulative\n", iter->count);
592       goto restart;
593     }
594   iter_unrecurse (iter);
595
596   iter->count += 1;
597   return TRUE;
598 }
599
600 #endif /* DBUS_BUILD_TESTS */