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

Last change on this file since 12545 was 12391, checked in by jordan, 11 years ago

(trunk libT) tweak the ABI of tr_bencToBuf() to match the way it's being used

  • Property svn:keywords set to Date Rev Author Id
File size: 15.1 KB
Line 
1#include <ctype.h>
2#include <errno.h>
3#include <stdio.h>
4#include <string.h>
5
6#include <event2/buffer.h>
7
8#include "transmission.h"
9#include "bencode.h"
10#include "utils.h" /* tr_free */
11
12/* #define VERBOSE */
13
14static int test = 0;
15
16#ifdef VERBOSE
17  #define check( A ) \
18    { \
19        ++test; \
20        if( A ){ \
21            fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
22        } else { \
23            fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
24            return test; \
25        } \
26    }
27#else
28  #define check( A ) \
29    { \
30        ++test; \
31        if( !( A ) ){ \
32            fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
33            return test; \
34        } \
35    }
36#endif
37
38static int
39testInt( void )
40{
41    uint8_t         buf[128];
42    int64_t         val;
43    int             err;
44    const uint8_t * end;
45
46    /* good int string */
47    tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
48    err = tr_bencParseInt( buf, buf + 4, &end, &val );
49    check( err == 0 );
50    check( val == 64 );
51    check( end == buf + 4 );
52
53    /* missing 'e' */
54    end = NULL;
55    val = 888;
56    err = tr_bencParseInt( buf, buf + 3, &end, &val );
57    check( err == EILSEQ );
58    check( val == 888 );
59    check( end == NULL );
60
61    /* empty buffer */
62    err = tr_bencParseInt( buf, buf + 0, &end, &val );
63    check( err == EILSEQ );
64    check( val == 888 );
65    check( end == NULL );
66
67    /* bad number */
68    tr_snprintf( (char*)buf, sizeof( buf ), "i6z4e" );
69    err = tr_bencParseInt( buf, buf + 5, &end, &val );
70    check( err == EILSEQ );
71    check( val == 888 );
72    check( end == NULL );
73
74    /* negative number */
75    tr_snprintf( (char*)buf, sizeof( buf ), "i-3e" );
76    err = tr_bencParseInt( buf, buf + 4, &end, &val );
77    check( err == 0 );
78    check( val == -3 );
79    check( end == buf + 4 );
80
81    /* zero */
82    tr_snprintf( (char*)buf, sizeof( buf ), "i0e" );
83    err = tr_bencParseInt( buf, buf + 4, &end, &val );
84    check( err == 0 );
85    check( val == 0 );
86    check( end == buf + 3 );
87
88    /* no leading zeroes allowed */
89    val = 0;
90    end = NULL;
91    tr_snprintf( (char*)buf, sizeof( buf ), "i04e" );
92    err = tr_bencParseInt( buf, buf + 4, &end, &val );
93    check( err == EILSEQ );
94    check( val == 0 );
95    check( end == NULL );
96
97    return 0;
98}
99
100static int
101testStr( void )
102{
103    uint8_t         buf[128];
104    int             err;
105    const uint8_t * end;
106    const uint8_t * str;
107    size_t          len;
108
109    /* good string */
110    tr_snprintf( (char*)buf, sizeof( buf ), "4:boat" );
111    err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
112    check( err == 0 );
113    check( !strncmp( (char*)str, "boat", len ) );
114    check( len == 4 );
115    check( end == buf + 6 );
116    str = NULL;
117    end = NULL;
118    len = 0;
119
120    /* string goes past end of buffer */
121    err = tr_bencParseStr( buf, buf + 5, &end, &str, &len );
122    check( err == EILSEQ );
123    check( str == NULL );
124    check( end == NULL );
125    check( !len );
126
127    /* empty string */
128    tr_snprintf( (char*)buf, sizeof( buf ), "0:" );
129    err = tr_bencParseStr( buf, buf + 2, &end, &str, &len );
130    check( err == 0 );
131    check( !*str );
132    check( !len );
133    check( end == buf + 2 );
134    str = NULL;
135    end = NULL;
136    len = 0;
137
138    /* short string */
139    tr_snprintf( (char*)buf, sizeof( buf ), "3:boat" );
140    err = tr_bencParseStr( buf, buf + 6, &end, &str, &len );
141    check( err == 0 );
142    check( !strncmp( (char*)str, "boa", len ) );
143    check( len == 3 );
144    check( end == buf + 5 );
145    str = NULL;
146    end = NULL;
147    len = 0;
148
149    return 0;
150}
151
152static int
153testString( const char * str,
154            int          isGood )
155{
156    tr_benc         val;
157    const uint8_t * end = NULL;
158    char *          saved;
159    const size_t    len = strlen( str );
160    int             savedLen;
161    int             err = tr_bencParse( str, str + len, &val, &end );
162
163    if( !isGood )
164    {
165        check( err );
166    }
167    else
168    {
169        check( !err );
170#if 0
171        fprintf( stderr, "in: [%s]\n", str );
172        fprintf( stderr, "out:\n%s", tr_bencToStr( &val, TR_FMT_JSON, NULL ) );
173#endif
174        check( end == (const uint8_t*)str + len );
175        saved = tr_bencToStr( &val, TR_FMT_BENC, &savedLen );
176        check( !strcmp( saved, str ) );
177        check( len == (size_t)savedLen );
178        tr_free( saved );
179        tr_bencFree( &val );
180    }
181    return 0;
182}
183
184static int
185testParse( void )
186{
187    tr_benc         val;
188    tr_benc *       child;
189    tr_benc *       child2;
190    uint8_t         buf[512];
191    const uint8_t * end;
192    int             err;
193    int             len;
194    int64_t         i;
195    char *          saved;
196
197    tr_snprintf( (char*)buf, sizeof( buf ), "i64e" );
198    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
199    check( !err );
200    check( tr_bencGetInt( &val, &i ) );
201    check( i == 64 );
202    check( end == buf + 4 );
203    tr_bencFree( &val );
204
205    tr_snprintf( (char*)buf, sizeof( buf ), "li64ei32ei16ee" );
206    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
207    check( !err );
208    check( end == buf + strlen( (char*)buf ) );
209    check( val.val.l.count == 3 );
210    check( tr_bencGetInt( &val.val.l.vals[0], &i ) );
211    check( i == 64 );
212    check( tr_bencGetInt( &val.val.l.vals[1], &i ) );
213    check( i == 32 );
214    check( tr_bencGetInt( &val.val.l.vals[2], &i ) );
215    check( i == 16 );
216    saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
217    check( !strcmp( saved, (char*)buf ) );
218    tr_free( saved );
219    tr_bencFree( &val );
220
221    end = NULL;
222    tr_snprintf( (char*)buf, sizeof( buf ), "lllee" );
223    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
224    check( err );
225    check( end == NULL );
226
227    end = NULL;
228    tr_snprintf( (char*)buf, sizeof( buf ), "le" );
229    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
230    check( !err );
231    check( end == buf + 2 );
232    saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
233    check( !strcmp( saved, "le" ) );
234    tr_free( saved );
235    tr_bencFree( &val );
236
237    if( ( err = testString( "llleee", true ) ) )
238        return err;
239    if( ( err = testString( "d3:cow3:moo4:spam4:eggse", true ) ) )
240        return err;
241    if( ( err = testString( "d4:spaml1:a1:bee", true ) ) )
242        return err;
243    if( ( err =
244             testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee",
245                         true ) ) )
246        return err;
247    if( ( err =
248             testString(
249                 "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee",
250                 true ) ) )
251        return err;
252    if( ( err =
253             testString(
254                 "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e",
255                 true ) ) )
256        return err;
257    if( ( err = testString( "d1:ai0e1:be", false ) ) ) /* odd number of children
258                                                         */
259        return err;
260    if( ( err = testString( "", false ) ) )
261        return err;
262    if( ( err = testString( " ", false ) ) )
263        return err;
264
265    /* nested containers
266     * parse an unsorted dict
267     * save as a sorted dict */
268    end = NULL;
269    tr_snprintf( (char*)buf, sizeof( buf ), "lld1:bi32e1:ai64eeee" );
270    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
271    check( !err );
272    check( end == buf + strlen( (const char*)buf ) );
273    check( ( child = tr_bencListChild( &val, 0 ) ) );
274    check( ( child2 = tr_bencListChild( child, 0 ) ) );
275    saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
276    check( !strcmp( saved, "lld1:ai64e1:bi32eeee" ) );
277    tr_free( saved );
278    tr_bencFree( &val );
279
280    /* too many endings */
281    end = NULL;
282    tr_snprintf( (char*)buf, sizeof( buf ), "leee" );
283    err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end );
284    check( !err );
285    check( end == buf + 2 );
286    saved = tr_bencToStr( &val, TR_FMT_BENC, &len );
287    check( !strcmp( saved, "le" ) );
288    tr_free( saved );
289    tr_bencFree( &val );
290
291    /* no ending */
292    end = NULL;
293    tr_snprintf( (char*)buf, sizeof( buf ), "l1:a1:b1:c" );
294    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
295    check( err );
296
297    /* incomplete string */
298    end = NULL;
299    tr_snprintf( (char*)buf, sizeof( buf ), "1:" );
300    err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end );
301    check( err );
302
303    return 0;
304}
305
306static void
307stripWhitespace( char * in )
308{
309    char * out;
310
311    for( out = in; *in; ++in )
312        if( !isspace( *in ) )
313            *out++ = *in;
314    *out = '\0';
315}
316
317static int
318testJSONSnippet( const char * benc_str,
319                 const char * expected )
320{
321    tr_benc top;
322    struct evbuffer * buf = evbuffer_new( );
323    char * serialized;
324
325    tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL );
326    buf = tr_bencToBuf( &top, TR_FMT_JSON );
327    serialized = (char*) evbuffer_pullup( buf, -1 );
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    evbuffer_free( 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_bencToStr( &val, TR_FMT_BENC, &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    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    tr_bencFree( &top );
486    return 0;
487}
488
489static int
490testParse2( void )
491{
492    tr_benc top;
493    tr_benc top2;
494    int64_t intVal;
495    const char * strVal;
496    double realVal;
497    bool boolVal;
498    int len;
499    char * benc;
500    const uint8_t * end;
501
502    tr_bencInitDict( &top, 0 );
503    tr_bencDictAddBool( &top, "this-is-a-bool", true );
504    tr_bencDictAddInt( &top, "this-is-an-int", 1234 );
505    tr_bencDictAddReal( &top, "this-is-a-real", 0.5 );
506    tr_bencDictAddStr( &top, "this-is-a-string", "this-is-a-string" );
507
508    benc = tr_bencToStr( &top, TR_FMT_BENC, &len );
509    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" ) )
510    check( !tr_bencParse( benc, benc+len, &top2, &end ) )
511    check( (char*)end == benc + len )
512    check( tr_bencIsDict( &top2 ) )
513    check( tr_bencDictFindInt( &top, "this-is-an-int", &intVal ) )
514    check( intVal == 1234 )
515    check( tr_bencDictFindBool( &top, "this-is-a-bool", &boolVal ) )
516    check( boolVal == true )
517    check( tr_bencDictFindStr( &top, "this-is-a-string", &strVal ) )
518    check( !strcmp( strVal, "this-is-a-string" ) )
519    check( tr_bencDictFindReal( &top, "this-is-a-real", &realVal ) )
520    check( (int)(realVal*100) == 50 )
521
522    tr_bencFree( &top2 );
523    tr_free( benc );
524    tr_bencFree( &top );
525
526    return 0;
527}
528
529int
530main( void )
531{
532    int i;
533
534    if(( i = testInt( )))
535        return i;
536
537    if(( i = testStr( )))
538        return i;
539
540    if(( i = testParse( )))
541        return i;
542
543    if(( i = testJSON( )))
544        return i;
545
546    if(( i = testMerge( )))
547        return i;
548
549    if(( i = testBool( )))
550        return i;
551
552    if(( i = testParse2( )))
553        return i;
554
555#ifndef WIN32
556    i = testStackSmash( 1000000 );
557#else
558    i = testStackSmash( 100000 );
559#endif
560    if( i )
561        return i;
562
563    return 0;
564}
565
Note: See TracBrowser for help on using the repository browser.