"Fossies" - the Fresh Open Source Software Archive

Member "RPerl-5.002000/t/13_generate.t" (30 Aug 2019, 41379 Bytes) of package /linux/misc/RPerl-5.002000.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "13_generate.t": 4.001000_vs_4.002000.

    1 #!/usr/bin/env perl  ## no critic qw(ProhibitExcessMainComplexity)  # SYSTEM SPECIAL 4: allow complex code outside subroutines, must be on line 1
    2 
    3 # [[[ PRE-HEADER ]]]
    4 # suppress 'WEXRP00: Found multiple rperl executables' due to blib/ & pre-existing installation(s),
    5 # also 'WARNING WCOCODE00, COMPILER, FIND DEPENDENCIES: Failed to eval-use package' due to RPerl/Test/*/*Bad*.pm & RPerl/Test/*/*bad*.pl,
    6 # also other warnings generated in this file due to partially-incomplete generator code
    7 BEGIN { $ENV{RPERL_WARNINGS} = 0; }
    8 
    9 # [[[ HEADER ]]]
   10 use strict;
   11 use warnings;
   12 use RPerl::AfterSubclass;
   13 our $VERSION = 0.052_000;
   14 
   15 # [[[ CRITICS ]]]
   16 ## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls)  # USER DEFAULT 1: allow numeric values & print operator
   17 ## no critic qw(RequireBriefOpen)  # USER DEFAULT 5: allow open() in perltidy-expanded code
   18 ## no critic qw(RequireInterpolationOfMetachars)  # USER DEFAULT 2: allow single-quoted control characters & sigils
   19 ## no critic qw(ProhibitPostfixControls)  # SYSTEM SPECIAL 6: PERL CRITIC FILED ISSUE #639, not postfix foreach or if
   20 ## no critic qw(ProhibitDeepNests)  # SYSTEM SPECIAL 7: allow deeply-nested code
   21 
   22 # [[[ INCLUDES ]]]
   23 use RPerl::Test;
   24 use RPerl::Parser;
   25 use RPerl::Generator;
   26 use RPerl::Compiler;
   27 use Test::More;
   28 use Test::Exception;
   29 use File::Find qw(find);
   30 use Perl::Tidy;
   31 use Cwd;
   32 use File::Spec;
   33 
   34 # [[[ CONSTANTS ]]]
   35 use constant PATH_TESTS => my string $TYPED_PATH_TESTS = $RPerl::INCLUDE_PATH . '/RPerl/Test';
   36 use constant PATH_TESTS_MYCLASS => my string $TYPED_PATH_TESTS_MYCLASS = $RPerl::INCLUDE_PATH . '/_MyClass_Bad.pm';
   37 use constant PATH_PRECOMPILED => my string $TYPED_PATH_PRECOMPILED = $RPerl::INCLUDE_PATH . '/RPerl';
   38 use constant PATH_PRECOMPILED_MYCLASS1 => my string $TYPED_PATH_PRECOMPILED_MYCLASS1 = $RPerl::INCLUDE_PATH . '/MyClass_Good.pm';
   39 use constant PATH_PRECOMPILED_MYCLASS2 => my string $TYPED_PATH_PRECOMPILED_MYCLASS2 = $RPerl::INCLUDE_PATH . '/MyClass_Good.pm.CPPOPS_DUALTYPES';
   40 use constant PATH_PRECOMPILED_MYCLASS3 => my string $TYPED_PATH_PRECOMPILED_MYCLASS3 = $RPerl::INCLUDE_PATH . '/MyClass_Good.cpp.CPPOPS_CPPTYPES';
   41 use constant PATH_PRECOMPILED_MYCLASS4 => my string $TYPED_PATH_PRECOMPILED_MYCLASS4 = $RPerl::INCLUDE_PATH . '/MyClass_Good.h.CPPOPS_CPPTYPES';
   42 
   43 # [[[ OPERATIONS ]]]
   44 our $verbose_newline = q{};
   45 if ( $ENV{RPERL_VERBOSE} or $RPerl::VERBOSE ) {
   46     $verbose_newline = "\n";
   47 }
   48 
   49 # DEV NOTE: if set to true, will speed up tests where no pre-compiled reference files are found
   50 my boolean $cpp_skip_parse_gen_if_no_ref_files = 1;
   51 
   52 BEGIN {
   53     if ( $ENV{RPERL_VERBOSE} ) {
   54         Test::More::diag('[[[ Beginning Generator Pre-Test Loading, RPerl Compilation System ]]]');
   55     }
   56     # DEV NOTE: can't do use_ok() or require_ok() because it will place them before all other BEGIN blocks,
   57     # which means we wil have 4 tests passing before we call 'plan tests',
   58     # which means we will fail to have 'plan tests' first OR done_testing() last, which causes a TAP failure;
   59     # must be included w/ regular 'use' operators above
   60 #    lives_and( sub { use_ok('RPerl::AfterSubclass'); },            q{use_ok('RPerl::AfterSubclass') lives} );
   61 #    lives_and( sub { use_ok('RPerl::Parser'); }, q{use_ok('RPerl::Parser') lives} );
   62 #    lives_and( sub { use_ok('RPerl::Generator'); }, q{use_ok('RPerl::Generator') lives} );
   63 #    lives_and( sub { use_ok('RPerl::Compiler'); }, q{use_ok('RPerl::Compiler') lives} );
   64 }
   65 
   66 # TEMP DEBUGGING, ONLY LOAD SPECIFIC FILES
   67 #my $test_files = { './lib/RPerl/FOO/BAR.pm' => undef };
   68 
   69 # NEED UPDATE: add string_arrayref_hashref_hashref type
   70 #my string_arrayref_hashref_hashref $test_files = {};
   71 my $test_files = {};
   72 
   73 sub find_tests {
   74     ( my string $file_full_path_arg ) = @ARG;
   75     
   76     # accept optional argument with pre-defined file path if provided, else fall back to File::Find
   77     my string $file_full_path;
   78     if (defined $file_full_path_arg) {
   79         $file_full_path = $file_full_path_arg;
   80     }
   81     else {
   82         $file_full_path = $File::Find::name;
   83     }
   84 
   85 #    RPerl::diag('in 13_generate.t, find_tests, have $file_full_path = ' . $file_full_path . "\n");
   86 #    RPerl::diag('in 13_generate.t, find_tests, have $File::Find::dir = ' . $File::Find::dir . "\n");
   87 #    RPerl::diag('in 13_generate.t, find_tests, have $File::Find::name = ' . $File::Find::name . "\n");
   88 #    RPerl::diag('in 13_generate.t, find_tests, have getcwd = ' . getcwd . "\n");
   89 
   90 #    if ( $file_full_path !~ m/[.]pm$/xms ) { # TEMP DEBUGGING, ONLY FIND *.pm, NOT *.pl
   91 #    if ( $file_full_path !~ m/.*Module\/.*$/xms ) { # TEMP DEBUGGING, ONLY FIND CERTAIN FILES
   92     if ( $file_full_path !~ m/[.]p[ml]$/xms ) {    # find all *.pm & *.pl files
   93         return;
   94     }
   95 
   96     if ( ( $file_full_path =~ m/Good/ms ) or ( $file_full_path =~ m/good/ms ) ) {
   97 #        RPerl::diag('in 13_generate.t, find_tests, have good $file_full_path = ' . $file_full_path . "\n");
   98         $test_files->{$file_full_path} = undef;
   99 
  100         # check for existence of GENERATE preprocessor directive, skip file if generating is explicitly disabled, <<< GENERATE: OFF >>>
  101         open my filehandleref $FILE_HANDLE, '<', $file_full_path
  102             or croak 'ERROR ETE13GE00: Cannot open file ' . $file_full_path . ' for reading,' . $OS_ERROR . ', croaking';
  103         while (<$FILE_HANDLE>) {
  104             if (m/^\#\s*\<\<\<\s*GENERATE\s*\:\s*OFF\s*\>\>\>/xms) {
  105                 delete $test_files->{$file_full_path};
  106                 last;
  107             }
  108         }
  109         close $FILE_HANDLE
  110             or croak 'ERROR ETE13GE01: Cannot close file ' . $file_full_path . ' after reading,' . $OS_ERROR . ', croaking';
  111     }
  112     elsif ( ( $file_full_path =~ m/Bad/ms ) or ( $file_full_path =~ m/bad/ms ) ) {
  113 #        RPerl::diag('in 13_generate.t, find_tests, have bad  $file_full_path = ' . $file_full_path . "\n");
  114 
  115         # check for existence of GENERATE & GENERATE_ERROR preprocessor directives, compile list of expected parse errors, <<< GENERATE_ERROR: 'FOO' >>>
  116         open my filehandleref $FILE_HANDLE, '<', $file_full_path
  117             or croak 'ERROR ETE13GE02: Cannot open file ' . $file_full_path . ' for reading,' . $OS_ERROR . ', croaking';
  118         while (<$FILE_HANDLE>) {
  119             if (m/^\#\s*\<\<\<\s*GENERATE\s*\:\s*OFF\s*\>\>\>/xms) {
  120                 delete $test_files->{$file_full_path};
  121                 last;
  122             }
  123             if (m/^\#\s*\<\<\<\s*GENERATE_ERROR\s*\:\s*['"](.*)['"]\s*\>\>\>/xms) {
  124                 push @{ $test_files->{$file_full_path}->{errors} }, $1;
  125             }
  126         }
  127         close $FILE_HANDLE
  128             or croak 'ERROR ETE13GE03: Cannot close file ' . $file_full_path . ' after reading,' . $OS_ERROR . ', croaking';
  129     }
  130     else {  # file named neither Good nor Bad
  131         # check for existence of GENERATE preprocessor directive, do NOT skip file if generating is explicitly enabled, <<< GENERATE: ON >>>
  132         open my filehandleref $FILE_HANDLE, '<', $file_full_path
  133             or croak 'ERROR ETE13GE04: Cannot open file ' . $file_full_path . ' for reading,' . $OS_ERROR . ', croaking';
  134         while (<$FILE_HANDLE>) {
  135             if (m/^\#\s*\<\<\<\s*GENERATE\s*\:\s*ON\s*\>\>\>/xms) {
  136                 $test_files->{$file_full_path} = undef;
  137                 last;
  138             }
  139         }
  140         close $FILE_HANDLE
  141             or croak 'ERROR ETE13GE05: Cannot close file ' . $file_full_path . ' after reading,' . $OS_ERROR . ', croaking';
  142     }
  143 }
  144 
  145 # locate all *Good* *good* *Bad* *bad* test files in PATH_TESTS directory or ARGV command-line argument directory
  146 find(
  147     {
  148         no_chdir => 1,  # if not set, causes incorrect paths when $ARGV[0] is defined
  149         wanted => \&find_tests
  150     },
  151     (defined $ARGV[0]) ? $ARGV[0] : PATH_TESTS()  # accept optional command-line argument
  152 );
  153 
  154 if (not defined $ARGV[0]) {
  155     # locate _MyClass.pm w/out unnecessary additional searching
  156     find_tests(PATH_TESTS_MYCLASS());
  157 }
  158 
  159 sub find_precompiled {
  160     ( my string $file_full_path_arg ) = @ARG;
  161     
  162     # accept optional argument with pre-defined file path if provided, else fall back to File::Find
  163     my string $file_full_path;
  164     if (defined $file_full_path_arg) {
  165         $file_full_path = $file_full_path_arg;
  166     }
  167     else {
  168         $file_full_path = $File::Find::name;
  169     }
  170 
  171 #    RPerl::diag('in 13_generate.t, find_precompiled, have $file_full_path = ' . $file_full_path . "\n");
  172     (my $volume_found, my $directories_found, my $file_found) = File::Spec->splitpath( $file_full_path, my $no_file = 0 );
  173 #    RPerl::diag('in 13_generate.t, find_precompiled, have $file_found     = ' . $file_found . "\n");
  174 
  175 #    if ( $file_found !~ m/[.]pm$/xms ) { # TEMP DEBUGGING, ONLY FIND *.pm, NOT *.pl
  176 #    if ( $file_found !~ m/.*Module\/.*$/xms ) { # TEMP DEBUGGING, ONLY FIND FILES IN A CERTAIN DIRECTORY
  177 #    if ( $file_found =~ m/^(.*foo_bar_arith.*)[.].*OPS.*$/xms ) { # TEMP DEBUGGING, ONLY FIND CERTAIN FILES
  178     if ( $file_found =~ m/^(.+)[.]\w+OPS_\w+TYPES$/gxms ) {    # find all pre-compiled files
  179         my string $file_base = $1;
  180 #        RPerl::diag('in 13_generate.t, have pre-compiled $file_found  = ' . $file_found . "\n");
  181 #        RPerl::diag('in 13_generate.t, have pre-compiled $file_base   = ' . $file_base . "\n");
  182         if (($file_base =~ m/^(.*)[.]cpp$/gxms) or ($file_base =~ m/^(.*)[.]h$/gxms) or ($file_base =~ m/^(.*)[.]pmc$/gxms)) {
  183             my string $file_prefix = $1;
  184 #            RPerl::diag('in 13_generate.t, find_precompiled, have pre-compiled $file_prefix = ' . $file_prefix . "\n");
  185 
  186             # restore saved path, because File::Find changes directories while searching for files
  187             my string $file_program = $file_prefix . '.pl';
  188             my string $file_module = $file_prefix . '.pm';
  189 #            RPerl::diag('in 13_generate.t, find_precompiled, have $file_program = ' . $file_program . "\n");
  190 #            RPerl::diag('in 13_generate.t, find_precompiled, have $file_module = ' . $file_module . "\n");
  191 
  192             my $file_program_full_path = File::Spec->catpath( $volume_found, $directories_found, $file_program );
  193             my $file_module_full_path = File::Spec->catpath( $volume_found, $directories_found, $file_module );
  194 #            RPerl::diag('in 13_generate.t, find_precompiled, have $file_program_full_path = ' . $file_program_full_path . "\n");
  195 #            RPerl::diag('in 13_generate.t, find_precompiled, have $file_module_full_path = ' . $file_module_full_path . "\n");
  196 
  197             if ((-e $file_program_full_path) and (-f $file_program_full_path) and (-T $file_program_full_path)) {
  198 #                RPerl::diag('in 13_generate.t, find_precompiled, have all reference code for *.pl file, pre-compiled $file_prefix = ' . $file_prefix . "\n");
  199 
  200                 # check for existence of GENERATE preprocessor directive, skip file if generating is explicitly disabled, <<< GENERATE: OFF >>>
  201                 open my filehandleref $FILE_HANDLE, '<', $file_program_full_path
  202                     or croak 'ERROR ETE13GE06: Cannot open file ' . $file_program_full_path . ' for reading,' . $OS_ERROR . ', croaking';
  203                 while (<$FILE_HANDLE>) {
  204                     if (m/^\#\s*\<\<\<\s*GENERATE\s*\:\s*OFF\s*\>\>\>/xms) {
  205 #                        RPerl::diag('in 13_generate.t, have explicitly-disabled pre-compiled $file_program_full_path = ' . $file_program_full_path . "\n");
  206                         return;
  207                     }
  208                 }
  209                 close $FILE_HANDLE
  210                     or croak 'ERROR ETE13GE07: Cannot close file ' . $file_program_full_path . ' after reading,' . $OS_ERROR . ', croaking';
  211 
  212                 $test_files->{$file_program_full_path} = undef;
  213             }
  214                 elsif ((-e $file_module_full_path) and (-f $file_module_full_path) and (-T $file_module_full_path)) {
  215 #                RPerl::diag('in 13_generate.t, find_precompiled, have all reference code for *.pm file, pre-compiled $file_prefix = ' . $file_prefix . "\n");
  216 
  217                 # check for existence of GENERATE preprocessor directive, skip file if generating is explicitly disabled, <<< GENERATE: OFF >>>
  218                 open my filehandleref $FILE_HANDLE, '<', $file_module_full_path
  219                     or croak 'ERROR ETE13GE08: Cannot open file ' . $file_module_full_path . ' for reading,' . $OS_ERROR . ', croaking';
  220                 while (<$FILE_HANDLE>) {
  221                     if (m/^\#\s*\<\<\<\s*GENERATE\s*\:\s*OFF\s*\>\>\>/xms) {
  222 #                        RPerl::diag('in 13_generate.t, have explicitly-disabled pre-compiled $file_module_full_path = ' . $file_module_full_path . "\n");
  223                         return;
  224                     }
  225                 }
  226                 close $FILE_HANDLE
  227                     or croak 'ERROR ETE13GE09: Cannot close file ' . $file_module_full_path . ' after reading,' . $OS_ERROR . ', croaking';
  228 
  229                 $test_files->{$file_module_full_path} = undef;
  230             }
  231             else {
  232                 RPerl::warning( 'WARNING WTE13GE01, TEST GROUP 13, CODE GENERATOR: Missing non-compiled source code reference file ' . q{'} . $file_prefix . '.pl' . q{'} . ' or '  . q{'} . $file_prefix . '.pm' . q{'} . ', not performing difference check' . "\n" );
  233             }
  234         }
  235         else {
  236             RPerl::warning( 'WARNING WTE13GE02, TEST GROUP 13, CODE GENERATOR: Unrecognized pre-compiled source code reference file base ' . q{'} . $file_base . q{'} . ', not performing difference check' . "\n" );
  237         }
  238     }
  239     else {  # not a pre-compiled file
  240 #        RPerl::diag('in 13_generate.t, have NOT pre-compiled $file_found = ' . $file_found . "\n");
  241         return;
  242     }
  243 }
  244 
  245 # locate all *.*OPS_*TYPES pre-compiled files in PATH_PRECOMPILED directory or ARGV command-line argument directory
  246 find(
  247     {
  248         no_chdir => 1,  # if not set, causes incorrect paths when $ARGV[0] is defined
  249         wanted => \&find_precompiled
  250     },
  251     (defined $ARGV[0]) ? $ARGV[0] : PATH_PRECOMPILED()  # accept optional command-line argument
  252 );
  253 
  254 if (not defined $ARGV[0]) {
  255     # locate MyClass.pm & associated pre-compiled files w/out unnecessary additional searching
  256     find_precompiled(PATH_PRECOMPILED_MYCLASS1());
  257     find_precompiled(PATH_PRECOMPILED_MYCLASS2());
  258     find_precompiled(PATH_PRECOMPILED_MYCLASS3());
  259     find_precompiled(PATH_PRECOMPILED_MYCLASS4());
  260 }
  261 
  262 # trim unnecessary (and possibly problematic) @INC & absolute paths from input file names
  263 # must be done outside find() to properly utilize getcwd()
  264 foreach my string $test_file_key (sort keys %{$test_files}) {
  265     my string $test_file_key_trimmed = $test_file_key;
  266     $test_file_key_trimmed = RPerl::Compiler::post_processor__INC_paths_delete($test_file_key_trimmed, 1, 0);  # $leading_slash_delete = 1, $leading_lib_delete = 0
  267     $test_file_key_trimmed = RPerl::Compiler::post_processor__absolute_path_delete($test_file_key_trimmed);
  268     if ($test_file_key_trimmed ne $test_file_key) {
  269         $test_files->{$test_file_key_trimmed} = $test_files->{$test_file_key};
  270         delete $test_files->{$test_file_key};
  271     }
  272 }
  273 
  274 my integer $number_of_test_files = scalar keys %{$test_files};
  275 
  276 #RPerl::diag( 'in 13_generate.t, have $test_files = ' . "\n" . Dumper($test_files) . "\n" );
  277 #RPerl::diag( 'in 13_generate.t, have sort keys %{$test_files} = ' . "\n" . Dumper(sort keys %{$test_files}) . "\n" );
  278 #RPerl::diag( 'in 13_generate.t, before primary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  279 
  280 #plan tests => $number_of_test_files + 1;         # (PERLOPS_PERLTYPES or  CPPOPS_CPPTYPES) + lives_ok(rperltypes::types_enable())
  281 plan tests => (($number_of_test_files + 1) * 2);  # (PERLOPS_PERLTYPES and CPPOPS_CPPTYPES tests) + lives_ok(rperltypes::types_enable())
  282 
  283 my string_hashref $modes;
  284 my hashref_arrayref $output_file_name_groups_tmp;
  285 my string_hashref $output_file_name_group;
  286 my string_hashref $reference_file_name_group;
  287 my string $test_file_reference;
  288 my boolean $perform_diff_check;
  289 my unknown $eval_return_value;
  290 my hashref $diff_check_retval;
  291 #my integer $number_of_tests_run = 0;
  292 
  293 # [[[ PRIMARY RUNLOOP ]]]
  294 # [[[ PRIMARY RUNLOOP ]]]
  295 # [[[ PRIMARY RUNLOOP ]]]
  296 
  297 # loop up to 3 times, once for each mode: PERLOPS_PERLTYPES, PERLOPS_CPPTYPES, CPPOPS_CPPTYPES
  298 #foreach my integer $mode_id ( sort keys %{$RPerl::MODES} ) {
  299 #for my $mode_id ( 0 ) {    # PERLOPS_PERLTYPES ONLY
  300 #for my $mode_id ( 2 ) {    # CPPOPS_CPPTYPES ONLY
  301 #for my $mode_id ( 0 , 2 ) {    # PERLOPS_PERLTYPES, CPPOPS_CPPTYPES
  302 for my $mode_id ( 2 , 0 ) {    # CPPOPS_CPPTYPES, PERLOPS_PERLTYPES; DEV NOTE: reverse order because many C++ tests are currently skipped, more accurate time-to-finish
  303 #    RPerl::diag("in 13_generate.t, top of for() loop, have \$mode_id = $mode_id\n");
  304 #    $number_of_test_files = scalar keys %{$test_files};
  305 #    RPerl::diag( 'in 13_generate.t, top of primary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  306 
  307     # [[[ MODE SETUP ]]]
  308     my scalartype_hashref $mode = $RPerl::MODES->{$mode_id};
  309     my $ops                 = $mode->{ops};
  310     my $types               = $mode->{types};
  311     my string $mode_tagline = $ops . 'OPS_' . $types . 'TYPES';
  312     if ( $ENV{RPERL_VERBOSE} ) {
  313         Test::More::diag( '[[[ Beginning Generator Tests, RPerl Compilation System, ' . $ops . ' Operations & ' . $types . ' Data Types' . ' ]]]' );
  314     }
  315 
  316     lives_ok( sub { rperltypes::types_enable($types) }, q{mode '} . $ops . ' Operations & ' . $types . ' Data Types' . q{' enabled} );
  317 #    $number_of_tests_run++;
  318 
  319     # [[[ SECONDARY RUNLOOP ]]]
  320     # [[[ SECONDARY RUNLOOP ]]]
  321     # [[[ SECONDARY RUNLOOP ]]]
  322 
  323     TEST_FILE_LOOP: foreach my $test_file ( sort keys %{$test_files} ) {
  324         $reference_file_name_group = {};
  325 #        RPerl::diag( 'in 13_generate.t, top of secondary runloop, have $test_file = ' . $test_file . "\n" );
  326 #        $number_of_test_files = scalar keys %{$test_files};
  327 #        RPerl::diag( 'in 13_generate.t, top of secondary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  328 
  329         $modes = {
  330             dependencies => 'ON',
  331             ops     => $ops,
  332             types   => $types,
  333 #            check        => 'TRACE',  # unnecessary
  334 #            uncompile    => 'OFF',  # unnecessary
  335             compile => 'GENERATE',    # don't save source code to disk, will diff check from memory
  336             subcompile   => 'DYNAMIC',  # must be DYNAMIC for Subroutine::ast_to_cpp__generate_shims__CPPOPS_CPPTYPES() to generate PMC output
  337 #            CXX          => 'g++',  # unnecessary
  338             parallel => 'OFF',
  339             execute => 'OFF',
  340             label   => 'OFF'          # don't label source code, will strip comments before diff check
  341         };
  342 
  343         # DEV NOTE: must recursively find & parse ((great)*grand)?parents to receive any inherited class $properties
  344         my string_arrayref $parent_files = RPerl::Compiler::find_parents( $test_file, 1, $modes );  # second argument set to 1 for true value of $find_grandparents_recurse
  345 
  346         # generate starting with furthest ancestor & ending with parent,
  347         # to avoid errors due to one parent not properly receiving their own inheritance from an older grandparent, etc.
  348         $parent_files = [reverse @{$parent_files}];
  349 
  350 #        RPerl::diag( 'in 13_generate.t, have $parent_files = ' . Dumper($parent_files) );
  351 
  352         foreach my string $parent_file (@{$parent_files}) {
  353             # trim unnecessary (and possibly problematic) @INC & absolute paths from parent file names
  354             $parent_file = RPerl::Compiler::post_processor__INC_paths_delete($parent_file, 1, 0);  # $leading_slash_delete = 1, $leading_lib_delete = 0
  355             $parent_file = RPerl::Compiler::post_processor__absolute_path_delete( $parent_file );
  356     
  357             # [[[ PARSE PARENTS ]]]
  358             $eval_return_value = eval { RPerl::Parser::rperl_to_ast__parse($parent_file); };
  359             if ( not( ( defined $eval_return_value ) and $eval_return_value ) ) {
  360                 ok( 0, q{Program or module's parent parses with errors, code generation not reached, test aborted:} . (q{ } x 2) . $parent_file );
  361                 diag('Error output captured:' . "\n" . $EVAL_ERROR);
  362                 next;
  363             }
  364             my object $rperl_ast_parent = $eval_return_value;
  365 
  366             $modes->{_input_file_name} = $parent_file;
  367 
  368             # [[[ GENERATE PARENTS ]]]
  369             # reset symbol table namespace & subroutine for each generate phase of each file,
  370             # do not delete entire symbol table because we need parsed parent files for inherited OO properties
  371             $modes->{_symbol_table}->{_namespace} = q{};
  372             $modes->{_symbol_table}->{_subroutine} = q{};
  373 
  374             if ( $ops eq 'PERL' ) {
  375                 $eval_return_value = eval { RPerl::Generator::ast_to_rperl__generate( $rperl_ast_parent, $modes ); };
  376 #                RPerl::diag( 'in 13_generate.t, have $ops = ' . $ops . ', $parent_file = ' . $parent_file . ', $eval_return_value = ' . Dumper($eval_return_value) );
  377             }
  378             else {    # $ops eq 'CPP'
  379                 $eval_return_value = eval { RPerl::Generator::ast_to_cpp__generate( $rperl_ast_parent, $modes ); };
  380 #                RPerl::diag( 'in 13_generate.t, have $ops = ' . $ops . ', $parent_file = ' . $parent_file . ', $eval_return_value = ' . Dumper($eval_return_value) );
  381             }
  382 #            RPerl::diag( 'in 13_generate.t, have $ops = ' . $ops . ', $modes = ' . Dumper($modes) );
  383 #            RPerl::diag( 'in 13_generate.t, have $ops = ' . $ops . ', $modes->{_symbol_table} = ' . Dumper($modes->{_symbol_table}) );
  384         }
  385 
  386         # DEV NOTE: do not actually follow or compile dependencies;
  387         # find RPerl system deps such as 'use rperlsse;', 'use rperlgmp;', 'use rperlgsl;', etc.;
  388         # ignore return value, here we only care about $modes->{_enable_sse}, $modes->{_enable_gmp}, $modes->{_enable_gsl}, $modes->{_enable_mongodb}, etc.;
  389         RPerl::Compiler::find_dependencies( $test_file, 0, $modes );  # second argument set to 0 for false value of $find_subdependencies_recurse
  390 
  391         $output_file_name_groups_tmp = RPerl::Compiler::generate_output_file_names( [$test_file], [], 1, $modes );
  392         $output_file_name_group = $output_file_name_groups_tmp->[0];
  393 
  394         foreach my string $suffix_key (keys %{$output_file_name_group}) {
  395             if (defined $output_file_name_group->{$suffix_key}) {
  396                 $output_file_name_group->{$suffix_key} = RPerl::Compiler::post_processor__INC_paths_delete( $output_file_name_group->{$suffix_key}, 1, 0 );  # $leading_slash_delete = 1, $leading_lib_delete = 0
  397                 $output_file_name_group->{$suffix_key} = RPerl::Compiler::post_processor__absolute_path_delete( $output_file_name_group->{$suffix_key} );
  398             }
  399         }
  400 
  401 #        RPerl::diag( 'in 13_generate.t, have sort keys %{ $reference_file_name_group } = ' . Dumper( [ sort keys %{ $reference_file_name_group } ] ) );
  402 #        RPerl::diag( 'in 13_generate.t, have sort keys %{ $output_file_name_group } = ' . Dumper( [ sort keys %{ $output_file_name_group } ] ) );
  403 
  404         if ( $ops eq 'PERL' ) {
  405             $perform_diff_check = 1;    # Perl source code is it's own reference file, always perform diff check for PERLOPS_PERLTYPES
  406             foreach my string $suffix_key (keys %{$output_file_name_group}) {
  407                 if (defined $output_file_name_group->{$suffix_key}) {
  408                     $reference_file_name_group->{$suffix_key} = $output_file_name_group->{$suffix_key};
  409                 }
  410             }
  411         }
  412         else {                          # $ops eq 'CPP'
  413             $perform_diff_check = 1;    # begin by assuming diff check, all reference file(s) found
  414             # check if all reference files exist, update reference file names
  415             foreach my string $suffix_key ( sort keys %{ $output_file_name_group } ) {
  416 #                RPerl::diag( 'in 13_generate.t, have $suffix_key = ' . $suffix_key . "\n" );
  417 #                RPerl::diag( 'in 13_generate.t, have $output_file_name_group->{$suffix_key} = ' . $output_file_name_group->{$suffix_key} . "\n" );
  418                 if ((substr $suffix_key, 0, 1) eq '_') {
  419                     next;
  420                 }
  421                 elsif ($suffix_key eq 'PMC') {
  422                     $test_file_reference = $output_file_name_group->{$suffix_key} . q{.} . $ops . 'OPS_DUALTYPES';
  423                 }
  424                 elsif ($suffix_key eq 'H') {
  425                     if ((substr $output_file_name_group->{$suffix_key}, -12, 12) eq ' (if needed)') {
  426                         $test_file_reference = (substr $output_file_name_group->{$suffix_key}, 0, ((length $output_file_name_group->{$suffix_key}) - 12)) . q{.} . $ops . 'OPS_' . $types . 'TYPES';
  427                     }
  428                     else {
  429                         $test_file_reference = $output_file_name_group->{$suffix_key} . q{.} . $ops . 'OPS_' . $types . 'TYPES';
  430                     }
  431                 }
  432                 elsif ($suffix_key eq 'CPP') {
  433                     $test_file_reference = $output_file_name_group->{$suffix_key} . q{.} . $ops . 'OPS_' . $types . 'TYPES';
  434 
  435                     if (( not -e $test_file_reference ) or ( not -f $test_file_reference ) or ( not -T $test_file_reference )) {
  436                         # CPP ops mode will always require some CPP output file, regardless of H or PMC output file(s), skip before reaching parse or generate below
  437                         if ($cpp_skip_parse_gen_if_no_ref_files) { 
  438                             print $verbose_newline;
  439                             ok( 1, 'Program or module is missing a pre-compiled reference file, no diff check, skipped early:' . (q{ } x 2) . $test_file );
  440 #                            $number_of_tests_run++;
  441                             next TEST_FILE_LOOP;
  442                         }
  443                         # CPP reference file is missing, but go forward with parse & generate anyway, just don't try to perform a diff check
  444                         $perform_diff_check = 0;
  445                     }
  446                 }
  447                 elsif ($suffix_key eq 'EXE') {
  448 #                    $test_file_reference = $output_file_name_group->{$suffix_key} . q{.cpp.} . $ops . 'OPS_' . $types . 'TYPES';
  449                     # in CPPOPS mode, EXE file should be an actual binary, don't try to compare
  450                     next;
  451                 }
  452                 else {
  453                     croak 'ERROR ETE13GE02: Unrecognized suffix key ' . $suffix_key . ', croaking';
  454                 }
  455 #                RPerl::diag( 'in 13_generate.t, have $test_file_reference = ' . $test_file_reference . "\n" );
  456 
  457                 $reference_file_name_group->{$suffix_key} = $test_file_reference;  # update reference file name to include *OPS_*TYPES suffix
  458             }
  459         }
  460 
  461         # trim unnecessary (and possibly problematic) absolute paths from reference file names
  462         foreach my string $suffix_key (keys %{$reference_file_name_group}) {
  463             if (defined $reference_file_name_group->{$suffix_key}) {
  464                 $reference_file_name_group->{$suffix_key} = RPerl::Compiler::post_processor__INC_paths_delete($reference_file_name_group->{$suffix_key}, 1, 0);  # $leading_slash_delete = 1, $leading_lib_delete = 0
  465                 $reference_file_name_group->{$suffix_key} = RPerl::Compiler::post_processor__absolute_path_delete( $reference_file_name_group->{$suffix_key} );
  466             }
  467         }
  468 
  469         # [[[ PARSE ]]]
  470         $eval_return_value = eval { RPerl::Parser::rperl_to_ast__parse($test_file); };
  471         if ( not( ( defined $eval_return_value ) and $eval_return_value ) ) {
  472             ok( 0, 'Program or module parses with errors, code generation not reached, test aborted:' . (q{ } x 2) . $test_file );
  473             diag('Error output captured:' . "\n" . $EVAL_ERROR);
  474 #            $number_of_tests_run++;
  475             next;
  476         }
  477         my object $rperl_ast = $eval_return_value;
  478 
  479 #        RPerl::diag( 'in 13_generate.t, have $ops = ' . $ops . "\n" );
  480 #        RPerl::diag( 'in 13_generate.t, have $types = ' . $types . "\n" );
  481 
  482         $modes->{_input_file_name} = $test_file;
  483         $modes->{_input_file_name_current} = $test_file;
  484  
  485         # [[[ GENERATE ]]]
  486         # reset symbol table namespace & subroutine for each generate phase of each file,
  487         # do not delete entire symbol table because we need parsed parent files for inherited OO properties
  488         $modes->{_symbol_table}->{_namespace} = q{};
  489         $modes->{_symbol_table}->{_subroutine} = q{};
  490 
  491         if ( $ops eq 'PERL' ) {
  492             $eval_return_value = eval { RPerl::Generator::ast_to_rperl__generate( $rperl_ast, $modes ); };
  493         }
  494         else {    # $ops eq 'CPP'
  495             $eval_return_value = eval { RPerl::Generator::ast_to_cpp__generate( $rperl_ast, $modes ); };
  496         }
  497 
  498 #        RPerl::diag( 'in 13_generate.t, have $eval_return_value = ' . Dumper($eval_return_value) . "\n" );    # warning if undef retval
  499 
  500         if ( ( defined $eval_return_value ) and $eval_return_value ) {    # Perl eval return code defined & true, success
  501 #            RPerl::diag( 'in 13_generate.t, have defined and true $eval_return_value' . "\n" );
  502             my string_hashref $source_group = $eval_return_value;
  503 
  504 #            RPerl::diag( 'in 13_generate.t, have $source_group = ' . Dumper($source_group ) . "\n\n" );
  505 #            RPerl::diag( 'in 13_generate.t, have $source_group->{H} = ' . "\n" . $source_group->{H} . "\n\n" );
  506 #            RPerl::diag( 'in 13_generate.t, have sort keys %{ $source_group } = ' . Dumper( [ sort keys %{$source_group} ] ) );
  507 
  508             if ( $ops eq 'CPP' ) {
  509                 # CPPOPS POST-PROCESSING: set H paths in CPP files & finally create PMC file, as needed
  510                 $source_group->{CPP} = RPerl::Compiler::post_processor_cpp__header_unneeded( $source_group );
  511 
  512                 # DEV NOTE, CORRELATION #rp039: programs never have header files
  513                 if (defined $output_file_name_group->{H}) {
  514 #                    RPerl::diag( 'in 13_generate.t, about to call post_processor_cpp__header_or_cpp_path() w/ $output_file_name_group->{H} = ' . $output_file_name_group->{H} . "\n" );
  515                     $source_group->{CPP} = RPerl::Compiler::post_processor_cpp__header_or_cpp_path( $source_group->{CPP}, $output_file_name_group->{H} );
  516                 }
  517             }
  518 
  519             if (( $ops eq 'CPP' ) and (exists $output_file_name_group->{PMC}) and (defined $output_file_name_group->{PMC})) {
  520                 # generate PMC source code
  521 #                RPerl::diag( 'in 13_generate.t, about to call post_processor_cpp__pmc_generate() w/ $output_file_name_group->{H} = ' . $output_file_name_group->{H} . "\n" );
  522                 RPerl::Compiler::post_processor_cpp__pmc_generate( $source_group, $output_file_name_group, $modes );
  523             }
  524 
  525             if ( ( $test_file =~ m/Bad/xms ) or ( $test_file =~ m/bad/xms ) ) {    # file named *Bad* or *bad*
  526 #                RPerl::diag( 'in 13_generate.t, have $test_file NOT named *Good* or *good*' . "\n" );
  527 
  528                 # skip test if dummy source code found
  529                 if ( not RPerl::Generator::dummy_source_code_find($source_group) ) {
  530                     ok( 0, 'Program or module generates with errors:' . (q{ } x 21) . $test_file );
  531 #                    $number_of_tests_run++;
  532                 }
  533                 else { 
  534                     ok( 1, 'Program or module generates with errors, unfinished C++ generator called, test skipped:' . (q{ } x 3) . $test_file );
  535 #                    RPerl::diag( 'in 13_generate.t, DUMMY SOURCE CODE found, from $source_group:' . "\n" . Dumper($source_group) . "\n" );
  536 #                    $number_of_tests_run++;
  537                 }
  538             }
  539 
  540             #            elsif ( ( $test_file =~ m/Good/xms ) or ( $test_file =~ m/good/xms ) )
  541             else    # default assuming files are good
  542             {
  543 #                RPerl::diag( 'in 13_generate.t, have $test_file named *Good* or *good*' . "\n" );
  544                 if ($perform_diff_check) {
  545 #                    RPerl::diag( 'in 13_generate.t, need to perform diff check(s)' . "\n" );
  546 #                    RPerl::diag( 'in 13_generate.t, have $reference_file_name_group = ' . Dumper( $reference_file_name_group ) . "\n" );
  547                     my string $suffix_key;
  548                     my string $suffix_key_saved;
  549 
  550                     # [[[ TERTIARY RUNLOOP ]]]
  551                     # [[[ TERTIARY RUNLOOP ]]]
  552                     # [[[ TERTIARY RUNLOOP ]]]
  553 
  554                     foreach $suffix_key ( sort keys %{ $reference_file_name_group } ) {
  555 #                        RPerl::diag( 'in 13_generate.t, top of suffix loop, have $suffix_key = ' . $suffix_key . "\n" );
  556 #                        $number_of_test_files = scalar keys %{$test_files};
  557 #                        RPerl::diag( 'in 13_generate.t, top of tertiary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  558 
  559                         $suffix_key_saved = $suffix_key;
  560 
  561                         if ( $ops eq 'PERL' ) {    # single reference file; use original Perl source file as reference for diff check
  562                             $test_file_reference = $test_file;
  563                         }
  564                         else {                     # $ops eq 'CPP'
  565                                                    # multiple reference files; use pre-compiled CPP source files as references for diff check
  566                             $test_file_reference = $reference_file_name_group->{$suffix_key};
  567                         }
  568 
  569 #                        RPerl::diag( 'in 13_generate.t, have $test_file_reference = ' . $test_file_reference . "\n" );
  570 
  571                         # reference file does not exist (okay for some cases, such as H suffix for some .pl EXE input files)
  572                         if (( not -e $test_file_reference ) or ( not -f $test_file_reference ) or ( not -T $test_file_reference )) {
  573                             # source code generated
  574                             if ((exists $source_group->{$suffix_key}) and (defined $source_group->{$suffix_key}) and ($source_group->{$suffix_key} ne q{})) {
  575                                 ok( 1, 'Program or module is missing a pre-compiled reference file, no diff check, skipped:' . (q{ } x 2) . $test_file );
  576                                 next TEST_FILE_LOOP;
  577                             }
  578                             
  579                             # source code not generated, ref file does not exist, okay to skip
  580                             next;
  581                         }
  582 
  583                         $diff_check_retval = RPerl::Generator::diff_check_file_vs_string( $test_file_reference, $source_group, $suffix_key, $output_file_name_group, $modes );
  584 
  585                         #                        RPerl::diag( 'in 13_generate.t, have $diff_check_retval->{diff_line} = ' . $diff_check_retval->{diff_line} . "\n" );
  586                         if ( $diff_check_retval->{diff_line} != 0 ) {
  587 #                            RPerl::diag( 'in 13_generate.t, near bottom of suffix loop, have $diff_check_retval->{diff_line} = ' . $diff_check_retval->{diff_line} . ', $suffix_key = ' . $suffix_key . "\n" );
  588                             last;
  589                         }
  590 #                        RPerl::diag( 'in 13_generate.t, bottom of tertiary runloop, have $suffix_key = ' . $suffix_key . "\n" );
  591 #                        $number_of_test_files = scalar keys %{$test_files};
  592 #                        RPerl::diag( 'in 13_generate.t, bottom of tertiary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  593                     }
  594                     # NEED ANSWER: why is $suffix_key cleared when the loop is completed?
  595                     $suffix_key = $suffix_key_saved;
  596                     $suffix_key_saved = undef;
  597 #                    RPerl::diag( 'in 13_generate.t, after suffix loop, have $suffix_key = ' . $suffix_key . "\n" );
  598 
  599                     if ( $diff_check_retval->{diff_line} == 0 ) {
  600                         ok( 1, 'Program or module generates without errors, yes diff check:' . (q{ } x 2) . $test_file );
  601 #                        $number_of_tests_run++;
  602                     }
  603                     elsif ( $diff_check_retval->{diff_line} > 0 ) {
  604 #                        ok( 0, 'Program or module generates without errors, yes diff check, output file ' . $output_file_name_group->{$suffix_key} . ' and reference file ' . $test_file_reference . ' differ at line ' . $diff_check_retval->{diff_line} );
  605                         ok( 0, 'Program or module generates without errors, yes diff check, output file ' . $output_file_name_group->{$suffix_key} . ' and reference file ' . $test_file_reference . ' differ at line ' . $diff_check_retval->{diff_line} . ', have $diff_check_retval->{line_reference} = ' . "\n" . $diff_check_retval->{line_reference} . "\n" . 'have $diff_check_retval->{line_generated} = ' . "\n" . $diff_check_retval->{line_generated} . "\n");
  606 #                        $number_of_tests_run++;
  607                     }
  608                     else {    # $diff_check_retval->{diff_line} < 0
  609                               # skip __DUMMY_SOURCE_CODE results, indicated by $diff_check_retval->{diff_line} < 0
  610                         ok( 1, 'Program or module generates without errors, yes diff check, unfinished C++ generator called, test skipped:' . (q{ } x 3) . $test_file );
  611 #                        $number_of_tests_run++;
  612 #                        RPerl::diag( 'in 13_generate.t, DUMMY SOURCE CODE found in diff check' . "\n" );
  613                     }
  614                 }
  615                 else {
  616 #                    RPerl::diag( 'in 13_generate.t, do NOT need to perform diff check(s)' . "\n" );
  617 
  618                     # skip test if dummy source code found
  619                     if ( not RPerl::Generator::dummy_source_code_find($source_group) ) {
  620                         ok( 1, 'Program or module generates without errors, no diff check:' . (q{ } x 3) . $test_file );
  621 #                        $number_of_tests_run++;
  622                     }
  623                     else { 
  624                         ok( 1, 'Program or module generates without errors, no diff check, unfinished C++ generator called, test skipped:' . (q{ } x 3) . $test_file );
  625 #                        $number_of_tests_run++;
  626 #                        RPerl::diag( 'in 13_generate.t, DUMMY SOURCE CODE found, from $source_group:' . "\n" . Dumper($source_group) . "\n" );
  627                     }
  628                 }
  629             }
  630         }
  631         else {    # Perl eval return code undefined or false, error
  632 #            RPerl::diag( 'in 13_generate.t, have undefined or false $eval_return_value' . "\n" );
  633 #            RPerl::diag( 'in 13_generate.t, have $EVAL_ERROR = ' . $EVAL_ERROR . "\n" );
  634             if (   ( $test_file =~ m/Bad/ms )
  635                 or ( $test_file =~ m/bad/ms ) )
  636             {
  637 #                RPerl::diag( 'in 13_generate.t, have $test_file named *Bad* or *bad*' . "\n" );
  638                 my $missing_errors = [];
  639                 if ((exists $test_files->{$test_file}) and (defined $test_files->{$test_file}) and 
  640                     (exists $test_files->{$test_file}->{errors}) and (defined $test_files->{$test_file}->{errors})) {
  641                     foreach my $error ( @{ $test_files->{$test_file}->{errors} } ) {
  642 #                    RPerl::diag( 'in 13_generate.t, have possible $error = ', $error, "\n" );
  643                         if ( $EVAL_ERROR !~ /\Q$error\E/xms ) {
  644                             push @{$missing_errors}, q{Error message '} . $error . q{' expected, but not found};
  645                         }
  646                     }
  647                 }
  648                 print $verbose_newline;
  649                 ok( ( ( scalar @{$missing_errors} ) == 0 ), 'Program or module generates with expected error(s):' . (q{ } x 10) . $test_file );
  650                 if (( scalar @{$missing_errors} ) != 0) {
  651                     diag((join "\n", @{$missing_errors}) . "\n");
  652                 }
  653 #                $number_of_tests_run++;
  654             }
  655             else {
  656 #                RPerl::diag( 'in 13_generate.t, have $test_file NOT named *Bad* or *bad*' . "\n" );
  657                 if ( $EVAL_ERROR =~ /Can't\slocate\sobject\smethod\s"ast_to_\w*__generate"\svia\spackage/xms ) {
  658                     ok( 0, 'Program or module generates without errors, missing code generator:' . (q{ } x 1) . $test_file );
  659                     diag('Error output captured:' . "\n" . $EVAL_ERROR);
  660 #                    $number_of_tests_run++;
  661                 }
  662                 else {
  663                     ok( 0, 'Program or module generates without errors:' . (q{ } x 18) . $test_file );
  664                     diag('Error output captured:' . "\n" . $EVAL_ERROR);
  665 #                    $number_of_tests_run++;
  666                 }
  667             }
  668         }
  669 #        RPerl::diag( ( '-' x 100 ) . "\n" );
  670 #        die 'TMP DEBUG';
  671         $number_of_test_files = scalar keys %{$test_files};
  672 #        RPerl::diag( 'in 13_generate.t, bottom of secondary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  673     }
  674 #    RPerl::diag( ( '=' x 100 ) . "\n" );
  675 
  676     $number_of_test_files = scalar keys %{$test_files};
  677 #    RPerl::diag( 'in 13_generate.t, bottom of primary runloop, have $number_of_test_files = ' . $number_of_test_files . "\n" );
  678 }
  679 
  680 done_testing();
  681 #done_testing($number_of_tests_run);