2005-01-27 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 #define BYTE_ORDER_OFFSET  0
32 #define BODY_LENGTH_OFFSET 4
33
34 static void
35 iter_recurse (DBusMessageDataIter *iter)
36 {
37   iter->depth += 1;
38   _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING);
39 }
40
41 static int
42 iter_get_sequence (DBusMessageDataIter *iter)
43 {
44   return iter->sequence_nos[iter->depth];
45 }
46
47 static void
48 iter_set_sequence (DBusMessageDataIter *iter,
49                    int                  sequence)
50 {
51   iter->sequence_nos[iter->depth] = sequence;
52 }
53
54 static void
55 iter_unrecurse (DBusMessageDataIter *iter)
56 {
57   iter->depth -= 1;
58   _dbus_assert (iter->depth >= 0);
59 }
60
61 static void
62 iter_next (DBusMessageDataIter *iter)
63 {
64   iter->sequence_nos[iter->depth] += 1;
65 }
66
67 static dbus_bool_t
68 iter_first_in_series (DBusMessageDataIter *iter)
69 {
70   int i;
71
72   i = iter->depth;
73   while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
74     {
75       if (iter->sequence_nos[i] != 0)
76         return FALSE;
77       ++i;
78     }
79   return TRUE;
80 }
81
82 typedef dbus_bool_t (* DBusInnerGeneratorFunc)   (DBusMessageDataIter *iter,
83                                                   DBusMessage        **message_p);
84 typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter,
85                                                   DBusString          *data,
86                                                   DBusValidity        *expected_validity);
87
88 static void
89 set_reply_serial (DBusMessage *message)
90 {
91   if (message == NULL)
92     _dbus_assert_not_reached ("oom");
93   if (!dbus_message_set_reply_serial (message, 100))
94     _dbus_assert_not_reached ("oom");
95 }
96
97 static dbus_bool_t
98 generate_trivial_inner (DBusMessageDataIter *iter,
99                         DBusMessage        **message_p)
100 {
101   DBusMessage *message;
102
103   switch (iter_get_sequence (iter))
104     {
105     case 0:
106       message = dbus_message_new_method_call ("org.freedesktop.TextEditor",
107                                               "/foo/bar",
108                                               "org.freedesktop.DocumentFactory",
109                                               "Create");
110       break;
111     case 1:
112       message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
113       set_reply_serial (message);
114       break;
115     case 2:
116       message = dbus_message_new_signal ("/foo/bar",
117                                          "org.freedesktop.DocumentFactory",
118                                          "Created");
119       break;
120     case 3:
121       message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
122
123       if (!dbus_message_set_error_name (message,
124                                         "org.freedesktop.TestErrorName"))
125         _dbus_assert_not_reached ("oom");
126       
127       {
128         DBusMessageIter iter;
129         const char *v_STRING = "This is an error";
130         
131         dbus_message_iter_init_append (message, &iter);
132         if (!dbus_message_iter_append_basic (&iter,
133                                              DBUS_TYPE_STRING,
134                                              &v_STRING))
135           _dbus_assert_not_reached ("oom");
136       }
137       
138       set_reply_serial (message);
139       break;
140     default:
141       return FALSE;
142     }
143   
144   if (message == NULL)
145     _dbus_assert_not_reached ("oom");
146
147   *message_p = message;
148   
149   return TRUE;
150 }
151
152 static dbus_bool_t
153 generate_many_bodies_inner (DBusMessageDataIter *iter,
154                             DBusMessage        **message_p)
155 {
156   DBusMessage *message;
157   DBusString signature;
158   DBusString body;
159   
160   message = dbus_message_new_method_call ("org.freedesktop.Foo",
161                                           "/",
162                                           "org.freedesktop.Blah",
163                                           "NahNahNah");
164   if (message == NULL)
165     _dbus_assert_not_reached ("oom");
166
167   set_reply_serial (message);
168
169   if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
170     _dbus_assert_not_reached ("oom");
171   
172   if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
173                                                 message->byte_order,
174                                                 &signature, &body))
175     {
176       const char *v_SIGNATURE;
177
178       v_SIGNATURE = _dbus_string_get_const_data (&signature);
179       if (!_dbus_header_set_field_basic (&message->header,
180                                          DBUS_HEADER_FIELD_SIGNATURE,
181                                          DBUS_TYPE_SIGNATURE,
182                                          &v_SIGNATURE))
183         _dbus_assert_not_reached ("oom");
184
185       if (!_dbus_string_move (&body, 0, &message->body, 0))
186         _dbus_assert_not_reached ("oom");
187
188       _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
189                                 _dbus_string_get_length (&message->body),
190                                 message->byte_order);
191       
192       *message_p = message;
193     }
194   else
195     {
196       dbus_message_unref (message);
197       *message_p = NULL;
198     }
199   
200   _dbus_string_free (&signature);
201   _dbus_string_free (&body);
202
203   return *message_p != NULL;
204 }
205
206 static dbus_bool_t
207 generate_outer (DBusMessageDataIter   *iter,
208                 DBusString            *data,
209                 DBusValidity          *expected_validity,
210                 DBusInnerGeneratorFunc func)
211 {
212   DBusMessage *message;
213
214   message = NULL;
215   if (!(*func)(iter, &message))
216     return FALSE;
217
218   iter_next (iter);
219   
220   _dbus_assert (message != NULL);
221
222   _dbus_message_set_serial (message, 1);
223   _dbus_message_lock (message);
224   
225   *expected_validity = DBUS_VALID;
226
227   /* move for efficiency, since we'll nuke the message anyway */
228   if (!_dbus_string_move (&message->header.data, 0,
229                           data, 0))
230     _dbus_assert_not_reached ("oom");
231
232   if (!_dbus_string_copy (&message->body, 0,
233                           data, _dbus_string_get_length (data)))
234     _dbus_assert_not_reached ("oom");
235
236   dbus_message_unref (message);
237
238   return TRUE;
239 }
240
241 static dbus_bool_t
242 generate_trivial (DBusMessageDataIter   *iter,
243                   DBusString            *data,
244                   DBusValidity          *expected_validity)
245 {
246   return generate_outer (iter, data, expected_validity,
247                          generate_trivial_inner);
248 }
249
250 static dbus_bool_t
251 generate_many_bodies (DBusMessageDataIter   *iter,
252                       DBusString            *data,
253                       DBusValidity          *expected_validity)
254 {
255   return generate_outer (iter, data, expected_validity,
256                          generate_many_bodies_inner);
257 }
258
259 static dbus_bool_t
260 generate_wrong_length (DBusMessageDataIter *iter,
261                        DBusString          *data,
262                        DBusValidity        *expected_validity)
263 {
264   int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1,
265                     1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 };
266   int adjust;
267   int len_seq;
268
269  restart:
270   len_seq = iter_get_sequence (iter);
271   if (len_seq == _DBUS_N_ELEMENTS (lengths))
272     return FALSE;
273
274   _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths));
275   
276   iter_recurse (iter);
277   if (!generate_many_bodies (iter, data, expected_validity))
278     {
279       iter_set_sequence (iter, 0); /* reset to first body */
280       iter_unrecurse (iter);
281       iter_next (iter);            /* next length adjustment */
282       goto restart;
283     }
284   iter_unrecurse (iter);
285
286   adjust = lengths[len_seq];
287
288   if (adjust < 0)
289     {
290       if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE)
291         _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE);
292       else
293         _dbus_string_shorten (data, - adjust);
294       *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON;
295     }
296   else
297     {      
298       if (!_dbus_string_lengthen (data, adjust))
299         _dbus_assert_not_reached ("oom");
300       *expected_validity = DBUS_INVALID_TOO_MUCH_DATA;
301     }
302
303   /* Fixup lengths */
304   {
305     int old_body_len;
306     int new_body_len;
307     int byte_order;
308     
309     _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE);
310     
311     byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
312     old_body_len = _dbus_marshal_read_uint32 (data,
313                                               BODY_LENGTH_OFFSET,
314                                               byte_order,
315                                               NULL);
316     _dbus_assert (old_body_len < _dbus_string_get_length (data));
317     new_body_len = old_body_len + adjust;
318     if (new_body_len < 0)
319       {
320         new_body_len = 0;
321         /* we just munged the header, and aren't sure how */
322         *expected_validity = DBUS_VALIDITY_UNKNOWN;
323       }
324
325     _dbus_verbose ("changing body len from %u to %u by adjust %d\n",
326                    old_body_len, new_body_len, adjust);
327     
328     _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
329                               new_body_len,
330                               byte_order);
331   }
332
333   return TRUE;
334 }
335
336 static dbus_bool_t
337 generate_byte_changed (DBusMessageDataIter *iter,
338                        DBusString          *data,
339                        DBusValidity        *expected_validity)
340 {
341   int byte_seq;
342   int v_BYTE;
343
344   /* This is a little convoluted to make the bodies the
345    * outer loop and each byte of each body the inner
346    * loop
347    */
348
349  restart:
350   if (!generate_many_bodies (iter, data, expected_validity))
351     return FALSE;
352
353   iter_recurse (iter);
354   byte_seq = iter_get_sequence (iter);
355   iter_next (iter);
356   iter_unrecurse (iter);
357
358   if (byte_seq == _dbus_string_get_length (data))
359     {
360       _dbus_string_set_length (data, 0);
361       /* reset byte count */
362       iter_recurse (iter);
363       iter_set_sequence (iter, 0);
364       iter_unrecurse (iter);
365       goto restart;
366     }
367   else
368     {
369       /* Undo the "next" in generate_many_bodies */
370       iter_set_sequence (iter, iter_get_sequence (iter) - 1);
371     }
372
373   _dbus_assert (byte_seq < _dbus_string_get_length (data));
374   v_BYTE = _dbus_string_get_byte (data, byte_seq);
375   v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */
376   _dbus_string_set_byte (data, byte_seq, v_BYTE);
377   *expected_validity = DBUS_VALIDITY_UNKNOWN;
378
379   return TRUE;
380 }
381
382 typedef struct
383 {
384   const char *name;
385   DBusMessageGeneratorFunc func;  
386 } DBusMessageGenerator;
387
388 static const DBusMessageGenerator generators[] = {
389   { "trivial example of each message type", generate_trivial },
390   { "assorted arguments", generate_many_bodies },
391   { "wrong body lengths", generate_wrong_length },
392   { "each byte modified", generate_byte_changed }
393 };
394
395 void
396 _dbus_message_data_free (DBusMessageData *data)
397 {
398   _dbus_string_free (&data->data);
399 }
400
401 void
402 _dbus_message_data_iter_init (DBusMessageDataIter *iter)
403 {
404   int i;
405   
406   iter->depth = 0;
407   i = 0;
408   while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
409     {
410       iter->sequence_nos[i] = 0;
411       ++i;
412     } 
413 }
414
415 dbus_bool_t
416 _dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter,
417                                       DBusMessageData     *data)
418 {
419   DBusMessageGeneratorFunc func;
420   int generator;
421
422  restart:
423   generator = iter_get_sequence (iter);
424   
425   if (generator == _DBUS_N_ELEMENTS (generators))
426     return FALSE;
427
428   iter_recurse (iter);
429   
430   if (iter_first_in_series (iter))
431     printf (" testing message loading: %s\n", generators[generator].name);
432   
433   func = generators[generator].func;
434
435   if (!_dbus_string_init (&data->data))
436     _dbus_assert_not_reached ("oom");
437   
438   if ((*func)(iter, &data->data, &data->expected_validity))
439     ;
440   else
441     {
442       iter_set_sequence (iter, 0);
443       iter_unrecurse (iter);
444       iter_next (iter); /* next generator */
445       _dbus_string_free (&data->data);
446       goto restart;
447     }
448   iter_unrecurse (iter);
449   
450   return TRUE;
451 }
452
453 #endif /* DBUS_BUILD_TESTS */