source: trunk/libtransmission/bencode-test.c @ 8414

Last change on this file since 8414 was 8414, checked in by charles, 13 years ago

(trunk) add an option to the JSON generator to disable the human-readable indentations. Keep indenting the data files such as settings.json, but don't indent the messages used for RPC. This cuts the cost of deflate()ing those RPC messages by about 80%...

  • Property svn:keywords set to Date Rev Author Id
File size: 15.0 KB
Line 
1#include <ctype.h>
2#include <errno.h>
3#include <stdio.h>
4#include <string.h>
5
6#include "event.h"
7
8#include "transmission.h"
9#include "bencode.h"
10#include "json.h"
11#include "utils.h" /* tr_free */
12
13#undef VERBOSE
14
15static int test = 0;
16
17#ifdef VERBOSE
18  #define check( A ) \
19    { \
20        ++test; \
21        if( A ){ \
22            fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
23        } else { \
24            fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
25            return test; \
26        } \
27    }
28#else
29  #define check( A ) \
30    { \
31        ++test; \
32        if( !( A ) ){ \
33            fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
34            return test; \
35        } \
36    }
37#endif
38
39static int
40testInt( void )
41{
42    uint8_t         buf[128];
43    int64_t         val;
44    int             err;
45    const uint8_t * end;
46
47    /* good int string */
48    tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
49    err = tr_bencParseInt( buf, buf + 4, &end, &val );
50    check( err == 0 );
51    check( val == 64 );
52    check( end == buf + 4 );
53
54    /* missing 'e' */
55    end = NULL;
56    val = 888;
57    err = tr_bencParseInt( buf, buf + 3, &end, &val );
58    check( err == EILSEQ );
59    check( val == 888 );
60    check( end == NULL );
61
62    /* empty buffer */
63    err = tr_bencParseInt( buf, buf + 0, &end, &val );
64    check( err == EILSEQ );
65    check( val == 888 );
66    check( end == NULL );
67
68    /* bad number */
69    tr_snprintf( (char*)buf, sizeof( buf ), "i6z4e" );
70    err = tr_bencParseInt( buf, buf + 5, &end, &val );
71    check( err == EILSEQ );
72    check( val == 888 );
73    check( end == NULL );
74
75    /* negative number */
76    tr_snprintf( (char*)buf, sizeof( buf ), "i-3e" );
77    err = tr_bencParseInt( buf, buf + 4, &end, &val );
78    check( err == 0 );
79    check( val == -3 );
80    check( end == buf + 4 );
81
82    /* zero */
83    tr_snprintf( (char*)buf, sizeof( buf ), "i0e" );
84    err = tr_bencParseInt( buf, buf + 4, &end, &val );
85    check( err == 0 );
86    check( val == 0 );
87    check( end == buf + 3 );
88
89    /* no leading zeroes allowed */
90    val = 0;
91    end = NULL;
92    tr_snprintf( (char*)buf, sizeof( buf ), "i04e" );
93    err = tr_bencParseInt( buf, buf + 4, &end, &val );
94    check( err == EILSEQ );
95    check( val == 0 );
96    check( end == NULL );
97
98    return 0;
99}
100
101static int
102testStr( void )
103{
104    uint8_t         buf[128];
105    int             err;
106    const uint8_t * end;
107    const uint8_t * str;
108    size_t          len;
109
110    /* good string */
111    tr_snprintf( (char*)buf, sizeof( buf ), "4:boat" );
112    err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
113    check( err == 0 );
114    check( !strncmp( (char*)str, "boat", len ) );
115    check( len == 4 );
116    check( end == buf + 6 );
117    str = NULL;
118    end = NULL;
119    len = 0;
120
121    /* string goes past end of buffer */
122    err = tr_bencParseStr( buf, buf + 5, &end, &str, &len );
123    check( err == EILSEQ );
124    check( str == NULL );
125    check( end == NULL );
126    check( !len );
127
128    /* empty string */
129    tr_snprintf( (char*)buf, sizeof( buf ), "0:" );
130    err = tr_bencParseStr( buf, buf + 2, &end, &str, &len );
131    check( err == 0 );
132    check( !*str );
133    check( !len );
134    check( end == buf + 2 );
135    str = NULL;
136    end = NULL;
137    len = 0;
138
139    /* short string */
140    tr_snprintf( (char*)buf, sizeof( buf ), "3:boat" );
141    err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
142    check( err == 0 );
143    check( !strncmp( (char*)str, "boa", len ) );
144    check( len == 3 );
145    check( end == buf + 5 );
146    str = NULL;
147    end = NULL;
148    len = 0;
149
150    return 0;
151}
152
153static int
154testString( const char * str,
155            int          isGood )
156{
157    tr_benc         val;
158    const uint8_t * end = NULL;
159    char *          saved;
160    const size_t    len = strlen( str );
161    int             savedLen;
162    int             err = tr_bencParse( str, str + len, &val, &end );
163
164    if( !isGood )
165    {
166        check( err );
167    }
168    else
169    {
170        check( !err );
171#if 0
172        fprintf( stderr, "in: [%s]\n", str );
173        fprintf( stderr, "out:\n%s", tr_bencSaveAsJSON( &val, NULL ) );
174#endif
175        check( end == (const uint8_t*)str + len );
176        saved = tr_bencSave( &val, &savedLen );
177        check( !strcmp( saved, str ) );
178        check( len == (size_t)savedLen );
179        tr_free( saved );
180        tr_bencFree( &val );
181    }
182    return 0;
183}
184
185static int
186testParse( void )
187{
188    tr_benc         val;
189    tr_benc *       child;
190    tr_benc *       child2;
191    uint8_t         buf[512];
192    const uint8_t * end;
193    int             err;
194    int             len;
195    int64_t         i;
196    char *          saved;
197
198    tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
199    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
200    check( !err );
201    check( tr_bencGetInt( &val, &i ) );
202    check( i == 64 );
203    check( end == buf + 4 );
204    tr_bencFree( &val );
205
206    tr_snprintf( (char*)buf, sizeof( buf ), "li64ei32ei16ee" );
207    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
208    check( !err );
209    check( end == buf + strlen( (char*)buf ) );
210    check( val.val.l.count == 3 );
211    check( tr_bencGetInt( &val.val.l.vals[0], &i ) );
212    check( i == 64 );
213    check( tr_bencGetInt( &val.val.l.vals[1], &i ) );
214    check( i == 32 );
215    check( tr_bencGetInt( &val.val.l.vals[2], &i ) );
216    check( i == 16 );
217    saved = tr_bencSave( &val, &len );
218    check( !strcmp( saved, (char*)buf ) );
219    tr_free( saved );
220    tr_bencFree( &val );
221
222    end = NULL;
223    tr_snprintf( (char*)buf, sizeof( buf ), "lllee" );
224    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
225    check( err );
226    check( end == NULL );
227
228    end = NULL;
229    tr_snprintf( (char*)buf, sizeof( buf ), "le" );
230    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
231    check( !err );
232    check( end == buf + 2 );
233    saved = tr_bencSave( &val, &len );
234    check( !strcmp( saved, "le" ) );
235    tr_free( saved );
236    tr_bencFree( &val );
237
238    if( ( err = testString( "llleee", TRUE ) ) )
239        return err;
240    if( ( err = testString( "d3:cow3:moo4:spam4:eggse", TRUE ) ) )
241        return err;
242    if( ( err = testString( "d4:spaml1:a1:bee", TRUE ) ) )
243        return err;
244    if( ( err =
245             testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee",
246                         TRUE ) ) )
247        return err;
248    if( ( err =
249             testString(
250                 "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee",
251                 TRUE ) ) )
252        return err;
253    if( ( err =
254             testString(
255                 "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e",
256                 TRUE ) ) )
257        return err;
258    if( ( err = testString( "d1:ai0e1:be", FALSE ) ) ) /* odd number of children
259                                                         */
260        return err;
261    if( ( err = testString( "", FALSE ) ) )
262        return err;
263    if( ( err = testString( " ", FALSE ) ) )
264        return err;
265
266    /* nested containers
267     * parse an unsorted dict
268     * save as a sorted dict */
269    end = NULL;
270    tr_snprintf( (char*)buf, sizeof( buf ), "lld1:bi32e1:ai64eeee" );
271    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
272    check( !err );
273    check( end == buf + strlen( (const char*)buf ) );
274    check( ( child = tr_bencListChild( &val, 0 ) ) );
275    check( ( child2 = tr_bencListChild( child, 0 ) ) );
276    saved = tr_bencSave( &val, &len );
277    check( !strcmp( saved, "lld1:ai64e1:bi32eeee" ) );
278    tr_free( saved );
279    tr_bencFree( &val );
280
281    /* too many endings */
282    end = NULL;
283    tr_snprintf( (char*)buf, sizeof( buf ), "leee" );
284    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
285    check( !err );
286    check( end == buf + 2 );
287    saved = tr_bencSave( &val, &len );
288    check( !strcmp( saved, "le" ) );
289    tr_free( saved );
290    tr_bencFree( &val );
291
292    /* no ending */
293    end = NULL;
294    tr_snprintf( (char*)buf, sizeof( buf ), "l1:a1:b1:c" );
295    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
296    check( err );
297
298    /* incomplete string */
299    end = NULL;
300    tr_snprintf( (char*)buf, sizeof( buf ), "1:" );
301    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
302    check( err );
303
304    return 0;
305}
306
307static void
308stripWhitespace( char * in )
309{
310    char * out;
311
312    for( out = in; *in; ++in )
313        if( !isspace( *in ) )
314            *out++ = *in;
315    *out = '\0';
316}
317
318static int
319testJSONSnippet( const char * benc_str,
320                 const char * expected )
321{
322    tr_benc top;
323    struct evbuffer * buf = tr_getBuffer( );
324    char * serialized;
325
326    tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL );
327    serialized = tr_bencSaveAsJSON( &top, buf, TRUE );
328    stripWhitespace( serialized );
329#if 0
330    fprintf( stderr, "benc: %s\n", benc_str );
331    fprintf( stderr, "json: %s\n", serialized );
332    fprintf( stderr, "want: %s\n", expected );
333#endif
334    check( !strcmp( serialized, expected ) );
335    tr_bencFree( &top );
336    tr_releaseBuffer( buf );
337    return 0;
338}
339
340static int
341testJSON( void )
342{
343    int          val;
344    const char * benc_str;
345    const char * expected;
346
347    benc_str = "i6e";
348    expected = "6";
349    if( ( val = testJSONSnippet( benc_str, expected ) ) )
350        return val;
351
352    benc_str = "d5:helloi1e5:worldi2ee";
353    expected = "{\"hello\":1,\"world\":2}";
354    if( ( val = testJSONSnippet( benc_str, expected ) ) )
355        return val;
356
357    benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3eee";
358    expected = "{\"foo\":[1,2,3],\"hello\":1,\"world\":2}";
359    if( ( val = testJSONSnippet( benc_str, expected ) ) )
360        return val;
361
362    benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eeee";
363    expected = "{\"foo\":[1,2,3,{\"a\":0}],\"hello\":1,\"world\":2}";
364    if( ( val = testJSONSnippet( benc_str, expected ) ) )
365        return val;
366
367    benc_str = "d4:argsd6:statusle7:status2lee6:result7:successe";
368    expected =
369        "{\"args\":{\"status\":[],\"status2\":[]},\"result\":\"success\"}";
370    if( ( val = testJSONSnippet( benc_str, expected ) ) )
371        return val;
372
373    return 0;
374}
375
376static int 
377testMerge( void )
378{ 
379    tr_benc dest, src; 
380    int64_t i; 
381    const char * s; 
382
383    /* initial dictionary (default values)  */
384    tr_bencInitDict( &dest, 10 ); 
385    tr_bencDictAddInt( &dest, "i1", 1 ); 
386    tr_bencDictAddInt( &dest, "i2", 2 ); 
387    tr_bencDictAddInt( &dest, "i4", -35 ); /* remains untouched */
388    tr_bencDictAddStr( &dest, "s5", "abc" ); 
389    tr_bencDictAddStr( &dest, "s6", "def" ); 
390    tr_bencDictAddStr( &dest, "s7", "127.0.0.1" ); /* remains untouched */
391
392    /* new dictionary, will overwrite items in dest  */
393    tr_bencInitDict( &src, 10 ); 
394    tr_bencDictAddInt( &src, "i1", 1 );     /* same value */
395    tr_bencDictAddInt( &src, "i2", 4 );     /* new value */
396    tr_bencDictAddInt( &src, "i3", 3 );     /* new key:value */
397    tr_bencDictAddStr( &src, "s5", "abc" ); /* same value */
398    tr_bencDictAddStr( &src, "s6", "xyz" ); /* new value */
399    tr_bencDictAddStr( &src, "s8", "ghi" ); /* new key:value */
400
401    tr_bencMergeDicts( &dest, /*const*/ &src ); 
402
403    check( tr_bencDictFindInt( &dest, "i1", &i )); 
404    check( i == 1); 
405    check( tr_bencDictFindInt( &dest, "i2", &i )); 
406    check( i == 4); 
407    check( tr_bencDictFindInt( &dest, "i3", &i )); 
408    check( i == 3); 
409    check( tr_bencDictFindInt( &dest, "i4", &i )); 
410    check( i == -35); 
411    check( tr_bencDictFindStr( &dest, "s5", &s )); 
412    check( strcmp( "abc", s ) == 0 ); 
413    check( tr_bencDictFindStr( &dest, "s6", &s )); 
414    check( strcmp( "xyz", s ) == 0 ); 
415    check( tr_bencDictFindStr( &dest, "s7", &s )); 
416    check( strcmp( "127.0.0.1", s ) == 0 ); 
417    check( tr_bencDictFindStr( &dest, "s8", &s )); 
418    check( strcmp( "ghi", s ) == 0 ); 
419
420    tr_bencFree( &dest ); 
421    tr_bencFree( &src ); 
422    return 0; 
423} 
424
425static int
426testStackSmash( int depth )
427{
428    int             i;
429    int             len;
430    int             err;
431    uint8_t *       in;
432    const uint8_t * end;
433    tr_benc         val;
434    char *          saved;
435
436    in = tr_new( uint8_t, depth * 2 + 1 );
437    for( i = 0; i < depth; ++i )
438    {
439        in[i] = 'l';
440        in[depth + i] = 'e';
441    }
442    in[depth * 2] = '\0';
443    err = tr_bencParse( in, in + ( depth * 2 ), &val, &end );
444    check( !err );
445    check( end == in + ( depth * 2 ) );
446    saved = tr_bencSave( &val, &len );
447    check( !strcmp( saved, (char*)in ) );
448    tr_free( in );
449    tr_free( saved );
450    tr_bencFree( &val );
451
452    return 0;
453}
454
455static int
456testBool( void )
457{
458    tr_benc top;
459    int64_t intVal;
460    tr_bool boolVal;
461
462    tr_bencInitDict( &top, 0 );
463
464    tr_bencDictAddBool( &top, "key1", FALSE );
465    tr_bencDictAddBool( &top, "key2", 0 );
466    tr_bencDictAddInt ( &top, "key3", TRUE );
467    tr_bencDictAddInt ( &top, "key4", 1 );
468    check( tr_bencDictFindBool( &top, "key1", &boolVal ) )
469    check( !boolVal )
470    check( tr_bencDictFindBool( &top, "key2", &boolVal ) )
471    check( !boolVal )
472    check( tr_bencDictFindBool( &top, "key3", &boolVal ) )
473    check( boolVal )
474    check( tr_bencDictFindBool( &top, "key4", &boolVal ) )
475    check( boolVal )
476    check( tr_bencDictFindInt( &top, "key1", &intVal ) )
477    check( !intVal)
478    check( tr_bencDictFindInt( &top, "key2", &intVal ) )
479    check( !intVal )
480    check( tr_bencDictFindInt( &top, "key3", &intVal ) )
481    check( intVal )
482    check( tr_bencDictFindInt( &top, "key4", &intVal ) )
483    check( intVal )
484
485    return 0;
486}
487
488static int
489testParse2( void )
490{
491    tr_benc top;
492    tr_benc top2;
493    int64_t intVal;
494    const char * strVal;
495    double realVal;
496    tr_bool boolVal;
497    int len;
498    char * benc;
499    const uint8_t * end;
500
501    tr_bencInitDict( &top, 0 );
502    tr_bencDictAddBool( &top, "this-is-a-bool", TRUE );
503    tr_bencDictAddInt( &top, "this-is-an-int", 1234 );
504    tr_bencDictAddReal( &top, "this-is-a-real", 0.5 );
505    tr_bencDictAddStr( &top, "this-is-a-string", "this-is-a-string" );
506
507    benc = tr_bencSave( &top, &len );
508    check( !strcmp( benc, "d14:this-is-a-booli1e14:this-is-a-real8:0.50000016:this-is-a-string16:this-is-a-string14:this-is-an-inti1234ee" ) )
509    check( !tr_bencParse( benc, benc+len, &top2, &end ) )
510    check( (char*)end == benc + len )
511    check( tr_bencIsDict( &top2 ) )
512    check( tr_bencDictFindInt( &top, "this-is-an-int", &intVal ) )
513    check( intVal == 1234 )
514    check( tr_bencDictFindBool( &top, "this-is-a-bool", &boolVal ) )
515    check( boolVal == TRUE )
516    check( tr_bencDictFindStr( &top, "this-is-a-string", &strVal ) )
517    check( !strcmp( strVal, "this-is-a-string" ) )
518    check( tr_bencDictFindReal( &top, "this-is-a-real", &realVal ) )
519    check( (int)(realVal*100) == 50 )
520
521    tr_bencFree( &top2 );
522    tr_free( benc );
523    tr_bencFree( &top );
524
525    return 0;
526}
527
528int
529main( void )
530{
531    int i;
532
533    if(( i = testInt( )))
534        return i;
535
536    if(( i = testStr( )))
537        return i;
538
539    if(( i = testParse( )))
540        return i;
541
542    if(( i = testJSON( )))
543        return i;
544
545    if(( i = testMerge( )))
546        return i;
547
548    if(( i = testBool( )))
549        return i;
550
551    if(( i = testParse2( )))
552        return i;
553
554#ifndef WIN32
555    i = testStackSmash( 1000000 );
556#else
557    i = testStackSmash( 100000 );
558#endif
559    if( i )
560        return i;
561
562    return 0;
563}
564
Note: See TracBrowser for help on using the repository browser.