Icon for Dimension.orgIcon for TalentIcon for ToolsIcon for Doorways.org
Icon for ToolsIcon for Perl Tools

DoxyFilt: Test/Test.pm Source File

  • Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List

Test/Test.pm

Go to the documentation of this file.
00001 package Doxygen::Test;
00002 
00003 =head1 NAME
00004 
00005 Doxygen::Test - Test module, used only in Doxygen:: test files (.t files).
00006 
00007 =head1 SYNOPSIS
00008 
00009     use Doxygen::Test   path  => 'Filter.pm',
00010                         tests => 2;
00011     
00012     hasComment('\file Test.pm');
00013     hasMacro;
00014     
00015 =head1 DESCRIPTION
00016 
00017 This module exists only to support automated testing of the other
00018 C<Doxygen> modules.
00019 It is used inside of C<t.#> files instead of C<Test::Simple> or
00020 C<Test::More>.
00021 
00022 In addition to specifying the number of tests,
00023 instantiation specifies a source file to be processed.
00024 This is done using the normal C<Doxygen::Source> mechanism.
00025 The output is captured and stored in the object,
00026 so that subsequent C<like()> calls can match against it.
00027 
00028 =head1 METHODS
00029 
00030 =over
00031 
00032 =cut
00033 
00034 use 5.005;  # just to pick something, but not really tested
00035 use strict;
00036 use warnings;
00037 use UNIVERSAL   qw(isa);
00038 
00039 use File::Basename;
00040 use Test::Builder;
00041 
00042 use Doxygen::Source;
00043 
00044 use constant TEST_FLAGS => qw(
00045     path
00046 );
00047 
00048 require Exporter;
00049 
00050 our @ISA     = qw(Exporter);
00051 our @EXPORT  = qw(
00052     ok like contains
00053     hasComment hasMacro includes nameSpace
00054     dumpGeneratedText);
00055 our $VERSION = '0.01';
00056 
00057 our $global;
00058 
00059 ###########################################################################
00060 ###########################################################################
00061 
00062 =item   C<new($class, %flags)>
00063 
00064 Create new C<Doxygen::Text> item.  Not called directly, a global object
00065 is instantiated via the C<import()> function.
00066 
00067 =cut
00068 
00069 sub new
00070 {
00071     my ($class, %flags) = @_;
00072     my  $self = bless {
00073         builder =>  Test::Builder->new,
00074         ok      =>  0,
00075         failed  =>  0,
00076         map { $flags{$_} ? ( $_ => $flags{$_} ) : ( ) } TEST_FLAGS
00077     }, $class;
00078     
00079     return $self
00080         unless $self->{ok} = isa $self->{builder}, 'Test::Builder';
00081     
00082     delete $flags{$_}
00083         for TEST_FLAGS;
00084     
00085     $flags{tests} += 3
00086         if defined $flags{tests};
00087     
00088     $self->{builder}->plan(%flags);
00089     
00090     $self->{path} = '<noPath>'
00091         unless $self->{path};
00092     
00093     # test #1
00094     $self->{builder}->ok($self->{ok} = -f $self->{path}, $self->{path});
00095     
00096     return $self
00097         unless $self->{ok};
00098     
00099     $self->{source} = new Doxygen::Source(path => $self->{path}, info => 0, stats => 0);
00100     $self->{builder}->ok( # test #2
00101         $self->{ok} = isa($self->{source}, 'Doxygen::Source'),
00102         "create Doxygen::Source object");
00103     
00104     return $self
00105         unless $self->{ok};
00106     
00107     my  $string;
00108     
00109     open  TMPOUT, ">&STDOUT";
00110     close STDOUT;
00111     open  STDOUT, '>', \$string or die "*** Boo hoo!\n";
00112     
00113     eval {
00114         $self->{source}->massage;
00115         $self->{source}->generate;
00116     };
00117     
00118     my  $err = $@;
00119     
00120     close STDOUT;
00121     open  STDOUT, ">&TMPOUT";
00122     close TMPOUT;
00123     
00124     # test #3
00125     $self->{builder}->ok($self->{text} = $string, 'generated text');
00126     
00127     warn "!!! Error generating text!\n!!!   $err\n"
00128         if $err;
00129     
00130     $self
00131 }
00132 
00133 ###########################################################################
00134 
00135 =item   C<DESTROY($self)>
00136 
00137 Destructor for object.
00138 
00139 Checks to see if all tests appear to have passed.
00140 If not, dumps generated text to C<STDERR>.
00141 
00142 =cut
00143 
00144 sub DESTROY
00145 {
00146     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00147     
00148     warn "<<<<<<<<<<<<<<<\n$self->{text}\n>>>>>>>>>>>>>>>\n"
00149         if $self->{ok}
00150         && $self->{failed};
00151 }
00152 
00153 ###########################################################################
00154 
00155 =item   C<import(%self, %flags)>
00156 
00157 The import protocol for the package is used to setup conditions
00158 for testing.
00159 
00160 =cut
00161 
00162 sub import ($%)
00163 {
00164     my  $class = shift;
00165     
00166     $global = $class->new(@_)
00167         unless isa $global, __PACKAGE__;
00168     
00169     die "*** Unable to create @{[ __PACKAGE__ ]}\n"
00170         unless isa $global, __PACKAGE__;
00171     
00172     $global->{builder}->exported_to(scalar(caller));
00173     $class->export_to_level(1, $class, @EXPORT);
00174 }
00175 
00176 ###########################################################################
00177 ###########################################################################
00178 
00179 =item   C<check()>
00180 
00181 Checks to see if the underlying Test::Builder has registered a failed test.
00182 
00183 Seems clumsy that we have call this after changing the test object.
00184 Sadly, the test object disappears before we get called in C<DESTROY>.
00185 Thus we have to keep this updated the best we can.
00186 
00187 =cut
00188 
00189 sub check ()
00190 {
00191     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00192     
00193     return
00194         unless isa $self->{builder}, 'Test::Builder';
00195     
00196     unless (isa $self->{builder}, 'Test::Builder') {
00197     } elsif ($self->{builder}->can('is_passing')) {
00198         $self->{failed} = 1
00199             unless $self->{builder}->is_passing;
00200     } elsif (isa $self->{builder}->{Test_Results}, 'ARRAY') {
00201         for my $t (@{$self->{builder}->{Test_Results}}) {
00202             $self->{failed} = 1
00203                 unless $t->{ok}
00204         }
00205     }
00206 }
00207 
00208 ###########################################################################
00209 
00210 =item   C<comment($self, $match)>
00211 
00212 Get generated comment starting with the specified match string.
00213 This allows pulling a specific comment from the generated text.
00214 
00215 Comments are assumed to be in C++ multi-line format with a doubled asterisk
00216 on the opening of the comment:
00217 
00222 Returns any subsequent matching text from the comment.
00223 The C<$match> is a string, not a regular expression.
00224 
00225 =cut
00226 
00227 sub comment
00228 {
00229     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00230     
00231     return
00232         unless $self->{ok};
00233     
00234     unless (isa $self->{comment}, 'HASH') {
00235         $self->{comment}->{$1} = $2
00236             while $self->{text}
00237                 =~ m{/\*\*\s*\n[\*\s]+(.*?)\s*?\n[\*\s]*?(.*?)[\*\s]*\*/}gs;
00238     }
00239     
00240     $self->{comment}->{$_[0]}
00241 }
00242 
00243 ###########################################################################
00244 ###########################################################################
00245 
00246 =item   C<contains($text [, $name])>
00247 
00248 Checks to see if the the generated text contains the specified text.
00249 If the C<$name> is not provided it is constructed.
00250 
00251 =cut
00252 
00253 sub contains ($;$)
00254 {
00255     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00256     
00257     return
00258         unless $self->{ok};
00259     
00260     my  $text = shift;
00261     my  $name = shift || qq{contains "$text"};
00262     
00263     $self->{builder}->ok(index($self->{text}, $text) >= 0, $name);
00264     check;
00265 }
00266 
00267 ###########################################################################
00268 
00269 =item   C<like($pattern, $name)>
00270 
00271 Marks test based on the generated text matching the specified C<$pattern>.
00272 Much like C<Test::Simple::like()>, with the text target pre-specified.
00273 
00274 =cut
00275 
00276 sub like ($$)
00277 {
00278     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00279     
00280     return
00281         unless $self->{ok};
00282 
00283     $self->{builder}->like($self->{text}, @_);
00284     check
00285 }
00286 
00287 ###########################################################################
00288 
00289 =item   C<ok($test, $name)>
00290 
00291 Marks test based on boolean test result C<$test>.
00292 Just like C<Test::Simple::ok()>.
00293 
00294 =cut
00295 
00296 sub ok ($$)
00297 {
00298     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00299 
00300     return
00301         unless $self->{ok};
00302 
00303     $self->{builder}->ok(@_);
00304     check
00305 }
00306 
00307 ###########################################################################
00308 ###########################################################################
00309 
00310 =item   C<hasComment($tag [, @matches ])>
00311 
00312 Checks if generated text contains comment beginning with specified C<$tag>.
00313 This would be something like C<"\file Filter.pm">.
00314 
00315 If this is true and subsequent C<@matches> are provided these will be
00316 tested against the body of the comment and the test will pass only if
00317 all of them match.
00318 
00319 Note that for purposes of counting tests, this is one test for the
00320 C<$tag> and one test for each of the C<@matches> (may be zero).
00321 
00322 =cut
00323 
00324 sub hasComment ($;@)
00325 {
00326     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00327     
00328     return
00329         unless $self->{ok};
00330     
00331     my  $tag  = shift;
00332     my  $cmnt = $self->comment($tag);
00333     my  $name = "/** $tag */";
00334     my  $ok   = defined $cmnt;
00335     
00336     $self->{builder}->ok($ok, $name);
00337     
00338     for (@_) {
00339         my  $fullname = qq{$name "$_"};
00340         
00341         if ($ok) {
00342             $self->{builder}->ok(index($cmnt, $_) >= 0, $fullname);
00343         } else {
00344             $self->{builder}->skip($fullname);
00345         }
00346     }
00347     
00348     check
00349 }
00350 
00351 ###########################################################################
00352 
00353 =item   C<hasMacro()>
00354 
00355 Checks if the redefinition-prevention macro is present in the file.
00356 This will look something like:
00357 
00358     #ifndef DoxyFilt
00359     #define DoxyFilt
00360     
00361     ...[snip]...
00362     
00363     #endif  DoxyFilt
00364 
00365 =cut
00366 
00367 sub hasMacro ()
00368 {
00369     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00370     
00371     return
00372         unless $self->{ok};
00373     
00374     my  $macro = basename $self->{path};
00375     
00376     # this matches Doxygen::Item::File:
00377     $macro =~ s|^.*:[/\\]||;
00378     $macro =~ s|\W|_|g;
00379     
00380     $self->{builder}->like(
00381         $self->{text},
00382         qr((?:^|\n)
00383            \s*   #ifndef \s+ $macro   \s* \n
00384            \s*   #define \s+ $macro   \s* \n
00385            .*?                        \s* \n
00386            \s*   #endif  \s+ $macro   \s*
00387            (?:\n|$))sx,
00388         "$macro re-definition macro");
00389     check
00390 }
00391 
00392 ###########################################################################
00393 
00394 =item   C<includes($path)>
00395 
00396 Checks for a C<#include> for the specified path.
00397 
00398 =cut
00399 
00400 sub includes ($)
00401 {
00402     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00403     my  $path = shift;
00404     
00405     return
00406         unless $self->{ok};
00407     
00408     $self->{builder}->like(
00409         $self->{text},
00410         qr{\n#include\s+<$path>\s*\n},
00411         "#include <$path>");
00412     
00413     check
00414 }
00415 
00416 ###########################################################################
00417 
00418 =item   C<nameSpace($path)>
00419 
00420 Check to see if specified namespace is defined.
00421 
00422 =cut
00423 
00424 sub nameSpace ($)
00425 {
00426     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00427     
00428     return
00429         unless $self->{ok};
00430     
00431     my  $pattern = join '', map {
00432         '\n\s*namespace\s+' . $_ . '\s*\{\s*'
00433     } split /::/, $_[0];
00434     
00435     $self->{builder}->like(
00436         $self->{text},
00437         qr($pattern)x,
00438         "namespace $_[0]");
00439     check
00440 }
00441 
00442 ###########################################################################
00443 ###########################################################################
00444 
00445 =item   C<dumpGeneratedText()>
00446 
00447 Bail out of test, incidentally causing generated text to be dumped.
00448 
00449 =cut
00450 
00451 sub dumpGeneratedText () {
00452     my  $self = isa($_[0], __PACKAGE__) ? shift : $global;
00453 
00454     $self->{failed} = 1;
00455 }
00456 
00457 ###########################################################################
00458 ###########################################################################
00459 
00460 1
00461 
00462 __END__
00463 
00464 =back
00465 
00466 =head1 SEE ALSO
00467 
00468 DoxyFilt.pl
00469 
00470 =head1 AUTHOR
00471 
00472 Marc M. Adkins, L<mailto:Perl@Doorways.org>
00473 
00474 =head1 COPYRIGHT AND LICENSE
00475 
00476 Copyright 2004-2010 by Marc M. Adkins
00477 
00478 This library is free software; you can redistribute it and/or modify
00479 it under the same terms as Perl itself.
00480 
00481 =cut

Generated on Mon Dec 27 2010 15:15:39 for DoxyFilt by  doxygen 1.7.1

www.dimension.org logo

(C)opyright 1998 - 2012 Dimension.org

WebMaster