source: git/libpolys/tests/cxxtestgen.pl @ 405407

spielwiese
Last change on this file since 405407 was b27c052, checked in by Oleksandr Motsak <motsak@…>, 13 years ago
ADD: first poly-tests (Sum Up integers 0..N) ADD: GlobalFixup made common and is able to redirect clog FIX: related minor fixes
  • Property mode set to 100755
File size: 14.3 KB
Line 
1#!/usr/bin/perl -w
2use strict;
3use Getopt::Long;
4
5sub 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}
27
28main();
29
30sub main {
31  parseCommandline();
32  scanInputFiles();
33  writeOutput();
34}
35
36#
37# Handling the command line
38#
39
40my ($output, $runner, $gui, $template, $abortOnFail, $haveEh, $noEh, $haveStd, $noStd);
41my ($root, $part, $noStaticInit, $longlong, $factor);
42my @headers = ();
43
44sub 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();
65
66  if ( defined($noStaticInit) && (defined($root) || defined($part)) ) {
67    die "--no-static-init cannot be used with --root/--part\n";
68  }
69
70  if ( $gui && !$runner ) {
71    $runner = 'StdioPrinter';
72  }
73
74  if ( defined($longlong) && !$longlong ) {
75    $longlong = 'long long';
76  }
77
78  foreach my $header (@headers) {
79    if ( !($header =~ m/^["<].*[>"]$/) ) {
80      $header = "\"$header\"";
81    }
82  }
83}
84
85sub printVersion() {
86  print "This is CxxTest version 3.10.1.\n";
87  exit 0;
88}
89
90sub expandWildcards() {
91  my @result = ();
92  while( my $fn = shift @_ ) {
93    push @result, glob($fn);
94  }
95  return @result;
96}
97
98#
99# Reading the input files and scanning for test cases
100#
101
102my (@suites, $suite, $test, $inBlock);
103my $numTotalTests = 0;
104
105sub scanInputFiles() {
106  foreach my $file (@ARGV) {
107    scanInputFile( $file );
108  }
109  scalar @suites or $root or die("No tests defined\n");
110}
111
112sub scanInputFile($) {
113  my ($file) = @_;
114  open FILE, "<$file" or die("Cannot open input file \"$file\"\n");
115
116  my $line;
117  while (defined($line = <FILE>)) {
118    scanLineForExceptionHandling( $line );
119    scanLineForStandardLibrary( $line );
120
121    scanLineForSuiteStart( $file, $., $line );
122
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;
133}
134
135sub lineBelongsToSuite($$$) {
136  my ($suite, $lineNo, $line) = @_;
137  if ( !$suite->{'generated'} ) {
138    return 1;
139  }
140
141  if ( !$inBlock ) {
142    $inBlock = lineStartsBlock( $line );
143  }
144  if ( $inBlock ) {
145    addLineToBlock( $suite->{'file'}, $lineNo, $line );
146  }
147  return $inBlock;
148}
149
150sub scanLineForExceptionHandling($) {
151  my ($line) = @_;
152  if ( $line =~ m/\b(try|throw|catch|TSM?_ASSERT_THROWS[A-Z_]*)\b/ ) {
153    addExceptionHandling();
154  }
155}
156
157sub 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  }
162}
163
164sub 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  }
173}
174
175sub 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' => [] };
186}
187
188sub lineStartsBlock($) {
189  my ($line) = @_;
190  return $line =~ m/\bCXXTEST_CODE\s*\(/;
191}
192
193sub scanLineForTest($$) {
194  my ($lineNo, $line) = @_;
195  if ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*(void)?\s*\)/ ) {
196    addTest( $2, $lineNo );
197  }
198}
199
200sub addTest($$$) {
201  my ($name, $line) = @_;
202  $test = { 'name' => $name,
203            'line' => $line };
204  push @{suiteTests()}, $test;
205}
206
207sub 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  }
216}
217
218sub 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;
223}
224
225sub scanLineForCreate($$) {
226  my ($lineNo, $line) = @_;
227  if ( $line =~ m/\bstatic\s+\w+\s*\*\s*createSuite\s*\(\s*(void)?\s*\)/ ) {
228    addCreateSuite( $lineNo );
229  }
230}
231
232sub 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  }
237}
238
239sub closeSuite() {
240  if ( $suite && scalar @{suiteTests()} ) {
241    verifySuite();
242    rememberSuite();
243  }
244  undef $suite;
245}
246
247sub addCreateSuite($) {
248  $suite->{'createSuite'} = $_[0];
249}
250
251sub addDestroySuite($) {
252  $suite->{'destroySuite'} = $_[0];
253}
254
255sub addExceptionHandling() {
256  $haveEh = 1 unless defined($noEh);
257}
258
259sub addStandardLibrary() {
260  $haveStd = 1 unless defined($noStd);
261}
262
263sub verifySuite() {
264  if (suiteCreateLine() || suiteDestroyLine()) {
265    die("Suite ", suiteName(), "  must have both createSuite() and destroySuite()\n")
266      unless (suiteCreateLine() && suiteDestroyLine());
267  }
268}
269
270sub rememberSuite() {
271  push @suites, $suite;
272  $numTotalTests += scalar @{$suite->{'tests'}};
273}
274
275sub suiteName() { return $suite->{'name'}; }
276sub suiteTests() { return $suite->{'tests'}; }
277sub suiteCreateLine() { return $suite->{'createSuite'}; }
278sub suiteDestroyLine() { return $suite->{'destroySuite'}; }
279sub fileName() { return $suite->{'file'}; }
280sub fileString() { return cstr(fileName()); }
281sub testName() { return $test->{'name'}; }
282sub testLine() { return $test->{'line'}; }
283
284sub suiteObject() { return "suite_".suiteName(); }
285
286sub cstr($) {
287  my $file = $_[0];
288  $file =~ s/\\/\\\\/g;
289  return "\"".$file."\"";
290}
291
292#
293# Writing the test source file
294#
295
296sub writeOutput() {
297  $template ? writeTemplateOutput() : writeSimpleOutput();
298}
299
300sub 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";
306}
307
308sub standardOutput() {
309  return !$output;
310}
311
312sub writeSimpleOutput() {
313  startOutputFile();
314  writePreamble();
315  writeMain();
316  writeWorld();
317}
318
319my ($didPreamble, $didWorld);
320
321sub 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  }
337}
338
339sub openTemplateFile() {
340  open TEMPLATE_FILE, "<$template" or die("Cannot open template file \"$template\"\n");
341}
342
343sub 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;
375}
376
377sub writeWorld() {
378  return if $didWorld;
379  writePreamble();
380  writeSuites();
381  ($root or !$part) and writeRoot();
382  $noStaticInit and writeInitialize();
383  $didWorld = 1;
384}
385
386sub 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  }
396}
397
398sub dynamicSuite() {
399  return suiteCreateLine();
400}
401
402my $lastIncluded;
403
404sub writeInclude($) {
405  my $file = $_[0];
406  return if $lastIncluded && ($file eq $lastIncluded);
407  print "#include \"$file\"\n\n";
408  $lastIncluded = $file;
409}
410
411sub 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";
418}
419
420sub 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";
427}
428
429sub writeSuitePointer() {
430  if ( $noStaticInit ) {
431    print "static ", suiteName(), " *", suiteObject(), ";\n\n";
432  } else {
433    print "static ", suiteName(), " *", suiteObject(), " = 0;\n\n";
434  }
435}
436
437sub writeSuiteObject() {
438  print "static ", suiteName(), " ", suiteObject(), ";\n\n";
439}
440
441sub testList() {
442  return "Tests_" . suiteName();
443}
444
445sub 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  }
451}
452
453sub writeTestDescriptions() {
454  foreach (@{suiteTests()}) {
455    $test = $_;
456    writeTestDescription();
457  }
458}
459
460sub suiteDescription() {
461  return "suiteDescription_" . suiteName();
462}
463
464sub 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();
473}
474
475sub dynamicRun() {
476  return sprintf( "if ( %s ) %s->%s();", suiteObject(), suiteObject(), testName() );
477}
478
479sub staticRun() {
480  return sprintf( "%s.%s();", suiteObject(), testName() );
481}
482
483sub writeSuiteDescription() {
484  dynamicSuite() ? writeDynamicDescription() : writeStaticDescription();
485}
486
487sub 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";
495}
496
497sub 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";
503}
504
505sub writeRoot() {
506  print "#include <cxxtest/Root.cpp>\n";
507}
508
509sub 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    }
525
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";
534}
535
536sub writeMain() {
537  if ( $gui ) {
538    print "char* argv0 = NULL;\n";
539    print "int main( int argc, char *argv[] ) {\n";
540    print "argv0 = argv[0];\n";
541    $noStaticInit &&
542      print " CxxTest::initialize();\n";
543    print " return CxxTest::GuiTuiRunner<CxxTest::$gui, CxxTest::$runner>( argc, argv ).run();\n";
544    print "}\n";
545  }
546  elsif ( $runner ) {
547    print "char* argv0 = NULL;\n";
548    print "int main( int, char *argv[] ) {\n";
549    print "argv0 = argv[0];\n";
550    $noStaticInit &&
551      print " CxxTest::initialize();\n";
552    print " return CxxTest::$runner().run();\n";
553    print "}\n";
554  }
555}
Note: See TracBrowser for help on using the repository browser.