Code

Node tool: correctly save node skewing to undo history
[inkscape.git] / cxxtest / cxxtestgen.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use Getopt::Long;
5 sub usage() {
6   print STDERR "Usage: $0 [OPTIONS] <input file(s)>\n";
7   print STDERR "Generate test source file for CxxTest.\n";
8   print STDERR "\n";
9   print STDERR "  -v, --version        Write CxxTest version\n";
10   print STDERR "  -o, --output=NAME    Write output to file NAME\n";
11   print STDERR "  --runner=CLASS       Create a main() function that runs CxxTest::CLASS\n";
12   print STDERR "  --gui=CLASS          Like --runner, with GUI component\n";
13   print STDERR "  --error-printer      Same as --runner=ErrorPrinter\n";
14   print STDERR "  --abort-on-fail      Abort tests on failed asserts (like xUnit)\n";
15   print STDERR "  --have-std           Use standard library (even if not found in tests)\n";
16   print STDERR "  --no-std             Don't use standard library (even if found in tests)\n";
17   print STDERR "  --have-eh            Use exception handling (even if not found in tests)\n";
18   print STDERR "  --no-eh              Don't use exception handling (even if found in tests)\n";
19   print STDERR "  --longlong=[TYPE]    Use TYPE as `long long' (defaut = long long)\n";
20   print STDERR "  --template=TEMPLATE  Use TEMPLATE file to generate the test runner\n";
21   print STDERR "  --include=HEADER     Include \"HEADER\" in test runner before other headers\n";
22   print STDERR "  --root               Write CxxTest globals\n";
23   print STDERR "  --part               Don't write CxxTest globals\n";
24   print STDERR "  --no-static-init     Don't rely on static initialization\n";
25   exit -1;
26 }
28 main();
30 sub main {
31   parseCommandline();
32   scanInputFiles();
33   writeOutput();
34 }
36 #
37 # Handling the command line
38 #
40 my ($output, $runner, $gui, $template, $abortOnFail, $haveEh, $noEh, $haveStd, $noStd);
41 my ($root, $part, $noStaticInit, $longlong, $factor);
42 my @headers = ();
44 sub parseCommandline() {
45   @ARGV = expandWildcards(@ARGV);
46   GetOptions( 'version'        => \&printVersion,
47               'output=s'       => \$output,
48               'template=s'     => \$template,
49               'runner=s'       => \$runner,
50               'gui=s',         => \$gui,
51               'error-printer'  => sub { $runner = 'ErrorPrinter'; $haveStd = 1; },
52               'abort-on-fail'  => \$abortOnFail,
53               'have-eh'        => \$haveEh,
54               'no-eh'          => \$noEh,
55               'have-std'        => \$haveStd,
56               'no-std'          => \$noStd,
57               'include=s'      => \@headers,
58               'root'           => \$root,
59               'part'           => \$part,
60               'no-static-init' => \$noStaticInit,
61               'factor'         => \$factor,
62               'longlong:s'     => \$longlong
63             ) or usage();
64   scalar @ARGV or $root or usage();
66   if ( defined($noStaticInit) && (defined($root) || defined($part)) ) {
67     die "--no-static-init cannot be used with --root/--part\n";
68   }
70   if ( $gui && !$runner ) {
71     $runner = 'StdioPrinter';
72   }
74   if ( defined($longlong) && !$longlong ) {
75     $longlong = 'long long';
76   }
78   foreach my $header (@headers) {
79     if ( !($header =~ m/^["<].*[>"]$/) ) {
80       $header = "\"$header\"";
81     }
82   }
83 }
85 sub printVersion() {
86   print "This is CxxTest version 3.10.1.\n";
87   exit 0;
88 }
90 sub expandWildcards() {
91   my @result = ();
92   while( my $fn = shift @_ ) {
93     push @result, glob($fn);
94   }
95   return @result;
96 }
98 #
99 # Reading the input files and scanning for test cases
102 my (@suites, $suite, $test, $inBlock);
103 my $numTotalTests = 0;
105 sub scanInputFiles() {
106   foreach my $file (@ARGV) {
107     scanInputFile( $file );
108   }
109   scalar @suites or $root or die("No tests defined\n");
112 sub scanInputFile($) {
113   my ($file) = @_;
114   open FILE, "<$file" or die("Cannot open input file \"$file\"\n");
116   my $line;
117   while (defined($line = <FILE>)) {
118     scanLineForExceptionHandling( $line );
119     scanLineForStandardLibrary( $line );
121     scanLineForSuiteStart( $file, $., $line );
123     if ( $suite ) {
124       if ( lineBelongsToSuite( $suite, $., $line ) ) {
125         scanLineForTest( $., $line );
126         scanLineForCreate( $., $line );
127         scanLineForDestroy( $., $line );
128       }
129     }
130   }
131   closeSuite();
132   close FILE;
135 sub lineBelongsToSuite($$$) {
136   my ($suite, $lineNo, $line) = @_;
137   if ( !$suite->{'generated'} ) {
138     return 1;
139   }
141   if ( !$inBlock ) {
142     $inBlock = lineStartsBlock( $line );
143   }
144   if ( $inBlock ) {
145     addLineToBlock( $suite->{'file'}, $lineNo, $line );
146   }
147   return $inBlock;
150 sub scanLineForExceptionHandling($) {
151   my ($line) = @_;
152   if ( $line =~ m/\b(try|throw|catch|TSM?_ASSERT_THROWS[A-Z_]*)\b/ ) {
153     addExceptionHandling();
154   }
157 sub scanLineForStandardLibrary($) {
158   my ($line) = @_;
159   if ( $line =~ m/\b(std\s*::|CXXTEST_STD|using\s+namespace\s+std\b|^\s*\#\s*include\s+<[a-z0-9]+>)/ ) {
160     addStandardLibrary();
161   }
164 sub scanLineForSuiteStart($$$) {
165   my ($fileName, $lineNo, $line) = @_;
166   if ( $line =~ m/\bclass\s+(\w+)\s*:\s*public\s+((::)?\s*CxxTest\s*::\s*)?TestSuite\b/ ) {
167     startSuite( $1, $fileName, $lineNo, 0 );
168   }
169   if ( $line =~ m/\bCXXTEST_SUITE\s*\(\s*(\w*)\s*\)/ ) {
170     print "$fileName:$lineNo: Warning: Inline test suites are deprecated.\n";
171     startSuite( $1, $fileName, $lineNo, 1 );
172   }
175 sub startSuite($$$$) {
176   my ($name, $file, $line, $generated) = @_;
177   closeSuite();
178   $suite = { 'name' => $name,
179              'file' => $file,
180              'line' => $line,
181              'generated' => $generated,
182              'create' => 0,
183              'destroy' => 0,
184              'tests' => [],
185              'lines' => [] };
188 sub lineStartsBlock($) {
189   my ($line) = @_;
190   return $line =~ m/\bCXXTEST_CODE\s*\(/;
193 sub scanLineForTest($$) {
194   my ($lineNo, $line) = @_;
195   if ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*(void)?\s*\)/ ) {
196     addTest( $2, $lineNo );
197   }
200 sub addTest($$$) {
201   my ($name, $line) = @_;
202   $test = { 'name' => $name,
203             'line' => $line };
204   push @{suiteTests()}, $test;
207 sub addLineToBlock($$$) {
208   my ($fileName, $lineNo, $line) = @_;
209   $line = fixBlockLine( $fileName, $lineNo, $line );
210   $line =~ s/^.*\{\{//;
211   my $end = ($line =~ s/\}\}.*//s);
212   push @{$suite->{'lines'}}, $line;
213   if ( $end ) {
214     $inBlock = 0;
215   }
218 sub fixBlockLine($$$) {
219   my ($fileName, $lineNo, $line) = @_;
220   my $fileLine = cstr($fileName) . "," . $lineNo;
221   $line =~ s/\b(E?TSM?_(ASSERT[A-Z_]*|FAIL))\s*\(/_$1($fileLine,/g;
222   return $line;
225 sub scanLineForCreate($$) {
226   my ($lineNo, $line) = @_;
227   if ( $line =~ m/\bstatic\s+\w+\s*\*\s*createSuite\s*\(\s*(void)?\s*\)/ ) {
228     addCreateSuite( $lineNo );
229   }
232 sub scanLineForDestroy($$) {
233   my ($lineNo, $line) = @_;
234   if ( $line =~ m/\bstatic\s+void\s+destroySuite\s*\(\s*\w+\s*\*\s*\w*\s*\)/ ) {
235     addDestroySuite( $lineNo );
236   }
239 sub closeSuite() {
240   if ( $suite && scalar @{suiteTests()} ) {
241     verifySuite();
242     rememberSuite();
243   }
244   undef $suite;
247 sub addCreateSuite($) {
248   $suite->{'createSuite'} = $_[0];
251 sub addDestroySuite($) {
252   $suite->{'destroySuite'} = $_[0];
255 sub addExceptionHandling() {
256   $haveEh = 1 unless defined($noEh);
259 sub addStandardLibrary() {
260   $haveStd = 1 unless defined($noStd);
263 sub verifySuite() {
264   if (suiteCreateLine() || suiteDestroyLine()) {
265     die("Suite ", suiteName(), "  must have both createSuite() and destroySuite()\n")
266       unless (suiteCreateLine() && suiteDestroyLine());
267   }
270 sub rememberSuite() {
271   push @suites, $suite;
272   $numTotalTests += scalar @{$suite->{'tests'}};
275 sub suiteName() { return $suite->{'name'}; }
276 sub suiteTests() { return $suite->{'tests'}; }
277 sub suiteCreateLine() { return $suite->{'createSuite'}; }
278 sub suiteDestroyLine() { return $suite->{'destroySuite'}; }
279 sub fileName() { return $suite->{'file'}; }
280 sub fileString() { return cstr(fileName()); }
281 sub testName() { return $test->{'name'}; }
282 sub testLine() { return $test->{'line'}; }
284 sub suiteObject() { return "suite_".suiteName(); }
286 sub cstr($) {
287   my $file = $_[0];
288   $file =~ s/\\/\\\\/g;
289   return "\"".$file."\"";
293 # Writing the test source file
296 sub writeOutput() {
297   $template ? writeTemplateOutput() : writeSimpleOutput();
300 sub startOutputFile() {
301   if ( !standardOutput() ) {
302     open OUTPUT_FILE,">$output" or die("Cannot create output file \"$output\"\n");
303     select OUTPUT_FILE;
304   }
305   print "/* Generated file, do not edit */\n\n";
308 sub standardOutput() {
309   return !$output;
312 sub writeSimpleOutput() {
313   startOutputFile();
314   writePreamble();
315   writeMain();
316   writeWorld();
319 my ($didPreamble, $didWorld);
321 sub writeTemplateOutput() {
322   openTemplateFile();
323   startOutputFile();
324   my $line;
325   while (defined($line = <TEMPLATE_FILE>)) {
326     if ( $line =~ m/^\s*\#\s*include\s*<cxxtest\// ) {
327       writePreamble();
328       print $line;
329     } elsif ( $line =~ m/^\s*<CxxTest\s+preamble>\s*$/ ) {
330       writePreamble();
331     } elsif ( $line =~ m/^\s*<CxxTest\s+world>\s*$/ ) {
332       writeWorld();
333     } else {
334       print $line;
335     }
336   }
339 sub openTemplateFile() {
340   open TEMPLATE_FILE, "<$template" or die("Cannot open template file \"$template\"\n");
343 sub writePreamble() {
344   return if $didPreamble;
345   print "#ifndef CXXTEST_RUNNING\n";
346   print "#define CXXTEST_RUNNING\n";
347   print "#endif\n";
348   print "\n";
349   if ( $haveStd ) {
350     print "#define _CXXTEST_HAVE_STD\n";
351   }
352   if ( $haveEh ) {
353     print "#define _CXXTEST_HAVE_EH\n";
354   }
355   if ( $abortOnFail ) {
356     print "#define _CXXTEST_ABORT_TEST_ON_FAIL\n";
357   }
358   if ( $longlong ) {
359     print "#define _CXXTEST_LONGLONG $longlong\n";
360   }
361   if ( $factor ) {
362     print "#define _CXXTEST_FACTOR\n";
363   }
364   foreach my $header (@headers) {
365     print "#include $header\n";
366   }
367   print "#include <cxxtest/TestListener.h>\n";
368   print "#include <cxxtest/TestTracker.h>\n";
369   print "#include <cxxtest/TestRunner.h>\n";
370   print "#include <cxxtest/RealDescriptions.h>\n";
371   print "#include <cxxtest/$runner.h>\n" if $runner;
372   print "#include <cxxtest/$gui.h>\n" if $gui;
373   print "\n";
374   $didPreamble = 1;
377 sub writeWorld() {
378   return if $didWorld;
379   writePreamble();
380   writeSuites();
381   ($root or !$part) and writeRoot();
382   $noStaticInit and writeInitialize();
383   $didWorld = 1;
386 sub writeSuites() {
387   foreach (@suites) {
388     $suite = $_;
389     writeInclude(fileName());
390     if ( $suite->{'generated'} ) { generateSuite(); }
391     dynamicSuite() ? writeSuitePointer() : writeSuiteObject();
392     writeTestList();
393     writeSuiteDescription();
394     writeTestDescriptions();
395   }
398 sub dynamicSuite() {
399   return suiteCreateLine();
402 my $lastIncluded;
404 sub writeInclude($) {
405   my $file = $_[0];
406   return if $lastIncluded && ($file eq $lastIncluded);
407   print "#include \"$file\"\n\n";
408   $lastIncluded = $file;
411 sub generateSuite() {
412   print "class ", suiteName(), " : public CxxTest::TestSuite {\n";
413   print "public:\n";
414   foreach my $line (@{$suite->{'lines'}}) {
415     print $line;
416   }
417   print "};\n\n";
420 sub writeTestDescriptionsBase() {
421   my $class = "TestDescriptionBase_" . suiteName();
422   print "class $class : public CxxTest::TestDescription {\n";
423   print "public:\n";
424   print " const char *file() const { return ", fileString(), "; }\n";
425   print " const char *suiteName() const { return \"", suiteName(), "\"; }\n";
426   print "};\n\n";
429 sub writeSuitePointer() {
430   if ( $noStaticInit ) {
431     print "static ", suiteName(), " *", suiteObject(), ";\n\n";
432   } else {
433     print "static ", suiteName(), " *", suiteObject(), " = 0;\n\n";
434   }
437 sub writeSuiteObject() {
438   print "static ", suiteName(), " ", suiteObject(), ";\n\n";
441 sub testList() {
442   return "Tests_" . suiteName();
445 sub writeTestList() {
446   if ( $noStaticInit ) {
447     printf "static CxxTest::List %s;\n", testList();
448   } else {
449     printf "static CxxTest::List %s = { 0, 0 };\n", testList();
450   }
453 sub writeTestDescriptions() {
454   foreach (@{suiteTests()}) {
455     $test = $_;
456     writeTestDescription();
457   }
460 sub suiteDescription() {
461   return "suiteDescription_" . suiteName();
464 sub writeTestDescription() {
465   my $class = "TestDescription_" . suiteName() . "_" . testName();
466   printf "static class $class : public CxxTest::RealTestDescription {\n";
467   printf "public:\n";
468   $noStaticInit or
469     printf " $class() : CxxTest::RealTestDescription( %s, %s, %s, \"%s\" ) {}\n",
470       testList(), suiteDescription(), testLine(), testName();
471   printf " void runTest() { %s }\n", dynamicSuite() ? dynamicRun() : staticRun();
472   printf "} testDescription_%s_%s;\n\n", suiteName(), testName();
475 sub dynamicRun() {
476   return sprintf( "if ( %s ) %s->%s();", suiteObject(), suiteObject(), testName() );
479 sub staticRun() {
480   return sprintf( "%s.%s();", suiteObject(), testName() );
483 sub writeSuiteDescription() {
484   dynamicSuite() ? writeDynamicDescription() : writeStaticDescription();
487 sub writeDynamicDescription() {
488   printf "CxxTest::DynamicSuiteDescription<%s> %s", suiteName(), suiteDescription();
489   if ( !$noStaticInit ) {
490     printf "( %s, %s, \"%s\", %s, %s, %s, %s )",
491       fileString(), $suite->{'line'}, suiteName(), testList(),
492         suiteObject(), suiteCreateLine(), suiteDestroyLine();
493   }
494   print ";\n\n";
497 sub writeStaticDescription() {
498   printf "CxxTest::StaticSuiteDescription %s", suiteDescription();
499   if ( !$noStaticInit ) {
500     printf "( %s, %s, \"%s\", %s, %s )", fileString(), $suite->{'line'}, suiteName(), suiteObject(), testList();
501   }
502   print ";\n\n";
505 sub writeRoot() {
506   print "#include <cxxtest/Root.cpp>\n";
509 sub writeInitialize() {
510   print "namespace CxxTest {\n";
511   print " void initialize()\n";
512   print " {\n";
513   foreach (@suites) {
514     $suite = $_;
515     printf "  %s.initialize();\n", testList();
516     if ( dynamicSuite() ) {
517       printf "  %s = 0;\n", suiteObject();
518       printf "  %s.initialize( %s, %s, \"%s\", %s, %s, %s, %s );\n",
519         suiteDescription(), fileString(), $suite->{'line'}, suiteName(), testList(),
520           suiteObject(), suiteCreateLine(), suiteDestroyLine();
521     } else {
522       printf "  %s.initialize( %s, %s, \"%s\", %s, %s );\n",
523         suiteDescription(), fileString(), $suite->{'line'}, suiteName(), suiteObject(), testList();
524     }
526     foreach (@{suiteTests()}) {
527       $test = $_;
528       printf "  testDescription_%s_%s.initialize( %s, %s, %s, \"%s\" );\n",
529         suiteName(), testName(), testList(), suiteDescription(), testLine(), testName();
530     }
531   }
532   print " }\n";
533   print "}\n";
536 sub writeMain() {
537   if ( $gui ) {
538     print "int main( int argc, char *argv[] ) {\n";
539     $noStaticInit &&
540       print " CxxTest::initialize();\n";
541     print " return CxxTest::GuiTuiRunner<CxxTest::$gui, CxxTest::$runner>( argc, argv ).run();\n";
542     print "}\n";
543   }
544   elsif ( $runner ) {
545     print "int main() {\n";
546     $noStaticInit &&
547       print " CxxTest::initialize();\n";
548     print " return CxxTest::$runner().run();\n";
549     print "}\n";
550   }