Distribute CUT to make unit tests easier to run.
[beanstalkd.git] / tests / cutgen.c
blobf406ac4d34e95a4c7eb483db2aab22ea74de3be8
1 /*
2 * CUT 2.3
3 * Copyright (c) 2001-2002 Samuel A. Falvo II, William D. Tanksley
4 * See LICENSE.TXT for details.
6 * $Log: cutgen.c,v $
7 * Revision 1.4 2003/03/18 05:53:50 sfalvo
8 * ADD: cutgen.c: cut_exit() -- common exit point; returns proper error code
9 * at all times.
11 * FIX: cutgen.c: Factored all instances of exit() to invoke cut_exit()
12 * instead. This fixes the bug #703793.
14 * Revision 1.3 2003/03/13 04:27:54 sfalvo
15 * ADD: LICENSE.TXT -- zlib license
17 * ADD: README cut.h cutgen.c -- Changelog token for CVS
19 * FIX: test/bringup-failure -- reflects new usage for bringups and
20 * teardowns in CUT 2.2.
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
29 #define DO_NOT_PROCESS "..."
31 #define SEARCH_TOKEN_TEST "__CUT__"
32 #define SEARCH_TOKEN_BRINGUP "__CUT_BRINGUP__"
33 #define SEARCH_TOKEN_TAKEDOWN "__CUT_TAKEDOWN__"
35 #define MAX_SYMBOL_LENGTH 256 /* arbitrary */
36 #define MAX_LINE_LENGTH 1024 /* arbitrary */
38 #define SEARCH_TOKEN_TEST_LENGTH sizeof( SEARCH_TOKEN_TEST )-1
39 #define SEARCH_TOKEN_BRINGUP_LENGTH sizeof( SEARCH_TOKEN_BRINGUP )-1
40 #define SEARCH_TOKEN_TAKEDOWN_LENGTH sizeof( SEARCH_TOKEN_TAKEDOWN )-1
42 typedef enum TestType {
43 TYPE_TEST = 0,
44 TYPE_BRINGUP = 1,
45 TYPE_TAKEDOWN = 2
46 } TestType;
48 typedef struct TestItem {
49 char name[MAX_SYMBOL_LENGTH];
50 enum TestType type;
51 struct TestItem *next;
52 } TestItem;
54 /* globals */
56 TestItem *testList = 0;
57 FILE *outfile;
58 extern char *libCUT[]; /* defined at the end of this file */
60 static int g_count, g_ready, g_index; /* Used by filename globbing support for windows */
61 static char **g_wildcards, g_fileName[MAX_LINE_LENGTH];
63 TestItem *FindFirstMatch( TestItem *current, char *basis_name, int basis_type )
65 while ( current )
67 if ( !strcmp(current->name,basis_name) && current->type == basis_type )
68 return current;
69 current = current->next;
71 return 0;
74 int NameAndTypeInTestList( char *name, TestType type )
76 return 0 != FindFirstMatch(testList,name,type);
79 void AppendToTestList( char *name, TestType type )
81 struct TestItem *current = testList;
82 if ( !current )
83 current = testList = malloc( sizeof( *testList) );
84 else
86 while ( current->next ) current = current->next;
87 current->next = malloc(sizeof( *testList));
88 current = current->next;
91 current->next = 0;
92 strcpy(current->name, name);
93 current->type = type;
96 void InsertNameAndTypeIntoTestList( char *name, TestType type )
98 if ( !NameAndTypeInTestList( name, type ) )
99 AppendToTestList( name, type );
102 int CharacterIsDigit(char ch)
104 return ( ( ch >= '0') && ( ch <= '9' ) );
107 int CharacterIsUppercase(char ch)
109 return ( ( ch >= 'A' ) && ( ch <= 'Z' ) );
112 int CharacterIsLowercase(char ch)
114 return ( ( ch >= 'a' ) && ( ch <= 'z' ) );
117 int CharacterIsAlphabetic(char ch)
119 return CharacterIsUppercase(ch) || CharacterIsLowercase(ch) || ( ch == '_' );
122 int CharacterIsAlphanumeric( char ch )
124 return CharacterIsDigit(ch) || CharacterIsAlphabetic(ch);
127 void ProcessGenericFunction( char *line, int position,
128 TestType type, int tokenDisplacement )
130 char name[MAX_SYMBOL_LENGTH] = "";
131 int maxLength = strlen( line ) - 1, offset=0;
132 position = position + tokenDisplacement;
134 while ( CharacterIsAlphanumeric(line[position])
135 && (position<maxLength) && (offset<MAX_SYMBOL_LENGTH) )
137 name[offset++] = line[position++];
138 name[offset] = 0;
141 InsertNameAndTypeIntoTestList( name, type );
144 void ProcessBringupFunction( char *line, int position )
146 ProcessGenericFunction( line, position, TYPE_BRINGUP, SEARCH_TOKEN_BRINGUP_LENGTH );
149 void ProcessTestFunction( char *line, int position )
151 ProcessGenericFunction( line, position, TYPE_TEST, SEARCH_TOKEN_TEST_LENGTH );
154 void ProcessTakedownFunction( char *line, int position )
156 ProcessGenericFunction( line, position, TYPE_TAKEDOWN, SEARCH_TOKEN_TAKEDOWN_LENGTH );
160 int OffsetOfSubstring( char *line, char *token )
162 char *inset = strstr(line,token);
164 if ( !inset ) return -1;
165 else return inset - line;
168 void CallIfSubstringFound( char *line, char *token, void (*function)(char*,int) )
170 int index = OffsetOfSubstring( line, token );
171 if ( index != -1 )
172 function( line, index );
175 void ProcessSourceFile( char *filename )
177 FILE *source;
178 char line[MAX_LINE_LENGTH];
180 if( strcmp( filename, DO_NOT_PROCESS ) != 0 )
183 source = fopen(filename,"r");
185 while ( fgets(line,MAX_LINE_LENGTH,source) )
187 CallIfSubstringFound( line, SEARCH_TOKEN_BRINGUP, ProcessBringupFunction );
188 CallIfSubstringFound( line, SEARCH_TOKEN_TEST, ProcessTestFunction );
189 CallIfSubstringFound( line, SEARCH_TOKEN_TAKEDOWN, ProcessTakedownFunction );
192 fclose(source);
196 void EmitExternDeclarationFor( char *name, char *prefix )
198 fprintf( outfile, "extern void %s%s( void );\n", prefix, name );
201 void Emit(char *text)
203 fprintf(outfile, "%s\n", text);
206 void BlankLine()
208 Emit( "" );
211 void ListExternalFunctions()
213 TestItem *current = testList;
214 while ( current )
216 if (current->type == TYPE_TEST)
217 EmitExternDeclarationFor( current->name, SEARCH_TOKEN_TEST );
218 else if (current->type == TYPE_BRINGUP)
219 EmitExternDeclarationFor( current->name, SEARCH_TOKEN_BRINGUP );
220 else if (current->type == TYPE_TAKEDOWN)
221 EmitExternDeclarationFor( current->name, SEARCH_TOKEN_TAKEDOWN );
222 current = current->next;
225 BlankLine();
228 void EmitLibrary(void)
230 int i=0;
231 while ( libCUT[i] )
232 Emit(libCUT[i++]);
235 void ListHeaderFiles(void)
237 EmitLibrary();
238 BlankLine();
239 BlankLine();
242 void EmitIndented(int indent,char *format, ...)
244 va_list v;
245 /* Print two spaces per level of indentation. */
246 fprintf( outfile, "%*s", indent*2, "" );
248 va_start(v,format);
249 vfprintf( outfile, format, v );
250 va_end(v);
252 fprintf( outfile, "\n" );
255 void EmitBringup(int indent,char *name)
257 BlankLine();
258 EmitIndented(indent, "cut_start( \"group-%s\", __CUT_TAKEDOWN__%s );",
259 name, name );
260 EmitIndented(indent, "__CUT_BRINGUP__%s();", name );
261 EmitIndented(indent, "cut_check_errors();");
264 void EmitTest(int indent,char *name)
266 EmitIndented(indent, "cut_start( \"%s\", 0 );", name );
267 EmitIndented(indent, "__CUT__%s();", name );
268 EmitIndented(indent, "cut_end( \"%s\" );", name );
271 void EmitTakedown(int indent,char *name)
273 EmitIndented(indent, "cut_end( \"group-%s\" );", name );
274 EmitIndented(indent, "__CUT_TAKEDOWN__%s();", name );
275 BlankLine();
278 void EmitUnitTesterBody()
280 int indent=0;
281 TestItem *test;
282 Emit( "int main( int argc, char *argv[] )\n{" );
283 Emit( " if ( argc == 1 )" );
284 Emit( " cut_init( -1 );" );
285 Emit( " else cut_init( atoi( argv[1] ) );" );
286 BlankLine();
288 indent = 1;
289 test = testList;
290 while ( test )
292 if (test->type == TYPE_BRINGUP)
294 EmitBringup(indent,test->name);
295 indent ++;
298 if (test->type == TYPE_TEST)
299 EmitTest(indent,test->name);
301 if (test->type == TYPE_TAKEDOWN)
303 indent --;
304 EmitTakedown(indent,test->name);
306 test = test->next;
309 BlankLine();
310 Emit( " cut_break_formatting();" );
311 Emit( " printf(\"Done.\");" );
312 Emit( " return 0;\n}\n" );
315 void EmitCutCheck()
317 Emit( "/* Automatically generated: DO NOT MODIFY. */" );
318 ListHeaderFiles();
319 BlankLine();
320 ListExternalFunctions();
321 BlankLine();
322 EmitUnitTesterBody();
325 #if defined(_MSC_VER) // version tested for MS Visual C++ 6.0
326 #include <io.h>
328 void FileName(char *base)
330 char *pos = strrchr(g_wildcards[g_index],'/');
331 char *pos2 = strrchr(g_wildcards[g_index],'\\');
332 if ( !pos || pos2 > pos )
333 pos = pos2;
335 if ( pos )
337 size_t length = 1 + pos - g_wildcards[g_index];
338 strncpy(g_fileName,g_wildcards[g_index],length);
339 g_fileName[length] = 0;
341 else g_fileName[0] = 0;
342 strcat(g_fileName,base);
345 int LoadArgument(void)
347 static struct _finddata_t fd;
348 static long handle = -1;
350 if ( g_index>=g_count )
351 return 0;
353 if ( -1 == handle )
355 handle = _findfirst(g_wildcards[g_index], &fd);
356 if ( -1 == handle ) // there MUST be at least a first match for each wildcard.
357 return 0;
358 FileName(fd.name);
359 return 1;
361 else if ( 0 == _findnext(handle,&fd) )
362 { // there's a 'next' filename
363 FileName(fd.name);
364 return 1;
366 else
367 { // this wasn't the first filename, and there isn't a next.
368 _findclose(handle);
369 handle = -1;
370 g_index++;
371 return LoadArgument();
375 #endif
376 #if defined(__LINUX__)
378 void FileName( char *base )
380 strncpy( g_fileName, base, MAX_LINE_LENGTH );
381 g_fileName[ MAX_LINE_LENGTH - 1 ] = 0;
384 int LoadArgument( void )
386 if ( g_index >= g_count )
387 return 0;
389 FileName( g_wildcards[g_index] );
390 g_index++; /* MUST come after FileName() call; bad code smell */
391 return 1;
394 #endif
396 void StartArguments( int starting, int count, char *array[] )
398 g_index = starting;
399 g_count = count;
400 g_wildcards = array;
402 g_ready = LoadArgument();
405 int NextArgument( void )
407 if( g_ready )
408 g_ready = LoadArgument();
410 return g_ready;
413 char *GetArgument( void )
415 if( g_ready )
416 return g_fileName;
418 return NULL;
421 void EstablishOutputFile( int argc, char *argv[] )
423 int i;
425 i = 0;
426 while( i < argc )
428 if( ( argv[i+1] != NULL ) && ( strcmp( argv[i], "-o" ) == 0 ) )
430 outfile = fopen( argv[i+1], "wb+" );
431 if( outfile == NULL )
432 fprintf( stderr, "ERROR: Can't open %s for writing.\n", argv[i+1] );
434 argv[i] = argv[i+1] = DO_NOT_PROCESS;
436 return;
439 i++;
442 outfile = stdout;
445 int main( int argc,char *argv[] )
447 char *filename;
449 if ( argc < 2 )
451 fprintf(
452 stderr,
453 "USAGE:\n"
454 " %s [options] <input file> [<input file> [...]]\n"
455 "\n"
456 "OPTIONS:\n"
457 " -o filename Specifies output file.\n"
458 "\n"
459 "NOTES:\n"
460 " If -o is left unspecified, output defaults to stdout.\n",
461 argv[0]
463 return 3;
466 EstablishOutputFile( argc, argv );
468 /* Skip the executable's name and the output filename. */
469 StartArguments(0,argc,argv);
471 /* Consume the rest of the arguments, one at a time. */
472 while ( NextArgument() )
474 filename = GetArgument();
476 if( strcmp( filename, DO_NOT_PROCESS ) )
478 fprintf( stderr, " - parsing '%s'... ", filename);
479 ProcessSourceFile( filename );
480 fprintf( stderr, "done.\n");
484 EmitCutCheck();
485 fflush(outfile);
486 fclose(outfile);
487 return 0;
490 char * libCUT[] =
492 "/*",
493 " * libcut.inc",
494 " * CUT 2.1",
495 " *",
496 " * Copyright (c) 2001-2002 Samuel A. Falvo II, William D. Tanksley",
497 " * See LICENSE.TXT for details.",
498 " *",
499 " * Based on WDT's 'TestAssert' package.",
500 " *",
501 " * $log$",
502 " */",
504 "#include <string.h>",
505 "#include <stdlib.h>",
506 "#include <stdio.h>",
507 "#include <stdarg.h>",
508 "#include \"cut.h\"",
510 "#ifndef BOOL /* Just in case -- helps in portability */",
511 "#define BOOL int",
512 "#endif",
514 "#ifndef FALSE",
515 "#define FALSE (0)",
516 "#endif",
518 "#ifndef TRUE",
519 "#define TRUE 1",
520 "#endif",
522 "typedef struct NameStackItem NameStackItem;",
523 "typedef struct NameStackItem *NameStack;",
525 "struct NameStackItem",
526 "{",
527 " NameStackItem * next;",
528 " char * name;",
529 " CUTTakedownFunction *takedown;",
530 "};",
532 "static int breakpoint = 0;",
533 "static int count = 0;",
534 "static BOOL test_hit_error = FALSE;",
535 "static NameStack nameStack;",
537 "static void traceback( void );",
538 "static void cut_exit( void );",
540 "/* I/O Functions */",
542 "static void print_string( char *string )",
543 "{",
544 " printf( \"%s\", string );",
545 " fflush( stdout );",
546 "}",
548 "static void print_string_as_error( char *filename, int lineNumber, char *string )",
549 "{",
550 " printf( \"%s(%d): %s\", filename, lineNumber, string );",
551 " fflush( stdout );",
552 "}",
554 "static void print_integer_as_expected( int i )",
555 "{",
556 " printf( \"(signed) %d (unsigned) %u (hex) 0x%08X\", i, i, i );",
557 "}",
559 "static void print_integer( int i )",
560 "{",
561 " printf( \"%d\", i );",
562 " fflush( stdout );",
563 "}",
565 "static void print_integer_in_field( int i, int width )",
566 "{",
567 " printf( \"%*d\", width, i );",
568 " fflush( stdout );",
569 "}",
571 "static void new_line( void )",
572 "{",
573 " printf( \"\\n\" );",
574 " fflush( stdout );",
575 "}",
577 "static void print_character( char ch )",
578 "{",
579 " printf( \"%c\", ch );",
580 " fflush( stdout );",
581 "}",
583 "static void dot( void )",
584 "{",
585 " print_character( '.' );",
586 "}",
588 "static void space( void )",
589 "{",
590 " print_character( ' ' );",
591 "}",
593 "/* Name Stack Functions */",
595 "static NameStackItem *stack_topOf( NameStack *stack )",
596 "{",
597 " return *stack;",
598 "}",
600 "static BOOL stack_isEmpty( NameStack *stack )",
601 "{",
602 " return stack_topOf( stack ) == NULL;",
603 "}",
605 "static BOOL stack_isNotEmpty( NameStack *stack )",
606 "{",
607 " return !( stack_isEmpty( stack ) );",
608 "}",
610 "static void stack_push( NameStack *stack, char *name, CUTTakedownFunction *tdfunc )",
611 "{",
612 " NameStackItem *item;",
614 " item = (NameStackItem *)( malloc( sizeof( NameStackItem ) ) );",
615 " if( item != NULL )",
616 " {",
617 " item -> next = stack_topOf( stack );",
618 " item -> name = name;",
619 " item -> takedown = tdfunc;",
621 " *stack = item;",
622 " }",
623 "}",
625 "static void stack_drop( NameStack *stack )",
626 "{",
627 " NameStackItem *oldItem;",
629 " if( stack_isNotEmpty( stack ) )",
630 " {",
631 " oldItem = stack_topOf( stack );",
632 " *stack = oldItem -> next;",
634 " free( oldItem );",
635 " }",
636 "}",
638 "/* CUT Initialization and Takedown Functions */",
640 "void cut_init( int brkpoint )",
641 "{",
642 " breakpoint = brkpoint;",
643 " count = 0;",
644 " test_hit_error = FALSE;",
645 " nameStack = NULL;",
647 " if( brkpoint >= 0 )",
648 " {",
649 " print_string( \"Breakpoint at test \" );",
650 " print_integer( brkpoint );",
651 " new_line();",
652 " }",
653 "}",
655 "void cut_exit( void )",
656 "{",
657 " exit( test_hit_error != FALSE );",
658 "}",
660 "/* User Interface functions */",
662 "static void print_group( int position, int base, int leftover )",
663 "{",
664 " if( !leftover )",
665 " return;",
667 " print_integer_in_field( base, position );",
668 " while( --leftover )",
669 " dot();",
670 "}",
672 "static void print_recap( int count )",
673 "{",
674 " int countsOnLastLine = count % 50;",
675 " int groupsOnLastLine = countsOnLastLine / 10;",
676 " int dotsLeftOver = countsOnLastLine % 10;",
677 " int lastGroupLocation =",
678 " countsOnLastLine - dotsLeftOver + ( 4 * groupsOnLastLine ) + 5;",
680 " if( dotsLeftOver == 0 )",
681 " {",
682 " if( countsOnLastLine == 0 )",
683 " lastGroupLocation = 61;",
684 " else",
685 " lastGroupLocation -= 14;",
687 " print_group( lastGroupLocation, countsOnLastLine-10, 10);",
688 " }",
689 " else",
690 " {",
691 " print_group(",
692 " lastGroupLocation,",
693 " countsOnLastLine - dotsLeftOver,",
694 " dotsLeftOver",
695 " );",
696 " }",
697 "}",
699 "void cut_break_formatting( void ) // DEPRECATED: Do not use in future software",
700 "{",
701 " new_line();",
702 "}",
704 "void cut_resume_formatting( void )",
705 "{",
706 " new_line();",
707 " print_recap( count );",
708 "}",
710 "void cut_interject( const char *comment, ... )",
711 "{",
712 " va_list marker;",
713 " va_start(marker,comment);",
714 " ",
715 " cut_break_formatting();",
716 " vprintf(comment,marker);",
717 " cut_resume_formatting();",
718 " ",
719 " va_end(marker);",
720 "}",
722 "/* Test Progress Accounting functions */",
724 "void __cut_mark_point( char *filename, int lineNumber )",
725 "{",
726 " if( ( count % 10 ) == 0 )",
727 " {",
728 " if( ( count % 50 ) == 0 )",
729 " new_line();",
731 " print_integer_in_field( count, 5 );",
732 " }",
733 " else",
734 " dot();",
736 " count++;",
737 " if( count == breakpoint )",
738 " {",
739 " print_string_as_error( filename, lineNumber, \"Breakpoint hit\" );",
740 " new_line();",
741 " traceback();",
742 " cut_exit();",
743 " }",
744 "}",
746 "void __cut_assert_equals( // DEPRECATED: Do not use in future software",
747 " char *filename,",
748 " int lineNumber,",
749 " char *message,",
750 " char *expression,",
751 " BOOL success,",
752 " int expected",
753 " )",
754 "{",
755 " __cut_mark_point( filename, lineNumber );",
756 " ",
757 " if( success != FALSE )",
758 " return;",
759 " ",
760 " cut_break_formatting();",
761 " print_string_as_error( filename, lineNumber, message );",
762 " new_line();",
763 " print_string_as_error( filename, lineNumber, \"Failed expression: \" );",
764 " print_string( expression );",
765 " new_line();",
766 " print_string_as_error( filename, lineNumber, \"Actual value: \" );",
767 " print_integer_as_expected( expected );",
768 " new_line();",
770 " test_hit_error = TRUE;",
771 " cut_resume_formatting();",
772 "}",
775 "void __cut_assert(",
776 " char *filename,",
777 " int lineNumber,",
778 " char *message,",
779 " char *expression,",
780 " BOOL success",
781 " )",
782 "{",
783 " __cut_mark_point( filename, lineNumber );",
784 " ",
785 " if( success != FALSE )",
786 " return;",
787 " ",
788 " cut_break_formatting();",
789 " print_string_as_error( filename, lineNumber, message );",
790 " new_line();",
791 " print_string_as_error( filename, lineNumber, \"Failed expression: \" );",
792 " print_string( expression );",
793 " new_line();",
795 " test_hit_error = TRUE;",
796 " cut_resume_formatting();",
797 "}",
800 "/* Test Delineation and Teardown Support Functions */",
802 "static void traceback()",
803 "{",
804 " if( stack_isNotEmpty( &nameStack ) )",
805 " print_string( \"Traceback\" );",
806 " else",
807 " print_string( \"(No traceback available.)\" );",
809 " while( stack_isNotEmpty( &nameStack ) )",
810 " {",
811 " print_string( \": \" );",
812 " print_string( stack_topOf( &nameStack ) -> name );",
814 " if( stack_topOf( &nameStack ) -> takedown != NULL )",
815 " {",
816 " print_string( \"(taking down)\" );",
817 " stack_topOf( &nameStack ) -> takedown();",
818 " }",
820 " stack_drop( &nameStack );",
822 " if( stack_isNotEmpty( &nameStack ) )",
823 " space();",
824 " }",
826 " new_line();",
827 "}",
829 "void cut_start( char *name, CUTTakedownFunction *takedownFunction )",
830 "{",
831 " stack_push( &nameStack, name, takedownFunction );",
832 "}",
834 "int __cut_check_errors( char *filename, int lineNumber )",
835 "{",
836 " if( test_hit_error || stack_isEmpty( &nameStack ) )",
837 " {",
838 " cut_break_formatting();",
839 " if( stack_isEmpty( &nameStack ) )",
840 " print_string_as_error( filename, lineNumber, \"Missing cut_start(); no traceback possible.\" );",
841 " else",
842 " traceback();",
844 " cut_exit();",
845 " return 0;",
846 " } else return 1;",
847 "}",
849 "void __cut_end( char *filename, int lineNumber, char *closingFrame )",
850 "{",
851 " if( test_hit_error || stack_isEmpty( &nameStack ) )",
852 " {",
853 " cut_break_formatting();",
854 " if( stack_isEmpty( &nameStack ) )",
855 " print_string_as_error( filename, lineNumber, \"Missing cut_start(); no traceback possible.\" );",
856 " else",
857 " traceback();",
859 " cut_exit();",
860 " }",
861 " else",
862 " {",
863 " if( strcmp( stack_topOf( &nameStack ) -> name, closingFrame ) == 0 )",
864 " stack_drop( &nameStack );",
865 " else",
866 " {",
867 " print_string_as_error( filename, lineNumber, \"Mismatched cut_end().\" );",
868 " traceback();",
869 " cut_exit();",
870 " }",
871 " }",
872 "}",
877 * vim: tabstop=3 shiftwidth=3 expandtab