File Coverage

blib/lib/VSGDR/TestScriptGen.pm
Criterion Covered Total %
statement 59 151 39.0
branch 0 36 0.0
condition 0 9 0.0
subroutine 20 27 74.0
pod n/a
total 79 223 35.4


line stmt bran cond sub pod time code
1             package VSGDR::TestScriptGen;
2            
3 1     1   45365 use strict;
  1         3  
  1         22  
4 1     1   4 use warnings;
  1         2  
  1         18  
5            
6 1     1   20 use 5.010;
  1         3  
7            
8 1     1   4 use List::Util qw(max);
  1         1  
  1         78  
9             #use List::MoreUtils;
10 1     1   351 use List::MoreUtils qw{firstidx} ;
  1         8968  
  1         4  
11 1     1   1078 use POSIX qw(strftime);
  1         4729  
  1         4  
12 1     1   1070 use Carp;
  1         2  
  1         45  
13 1     1   1084 use DBI;
  1         12556  
  1         50  
14 1     1   383 use Data::Dumper;
  1         4997  
  1         50  
15 1     1   310 use English;
  1         2416  
  1         5  
16 1     1   621 use IO::File ;
  1         5907  
  1         89  
17 1     1   7 use File::Basename;
  1         1  
  1         48  
18 1     1   313 use Try::Tiny;
  1         1398  
  1         59  
19            
20 1     1   270 use VSGDR::UnitTest::TestSet::Test;
  1         1580  
  1         26  
21 1     1   269 use VSGDR::UnitTest::TestSet::Test::TestCondition;
  1         731  
  1         26  
22 1     1   254 use VSGDR::UnitTest::TestSet::Representation;
  1         3177  
  1         40  
23 1     1   281 use VSGDR::UnitTest::TestSet::Resx;
  1         8829  
  1         44  
24 1     1   6 use File::Basename;
  1         2  
  1         588  
25            
26            
27             =head1 NAME
28            
29             VSGDR::TestScriptGen - Unit test script support package for SSDT unit tests, Ded MedVed.
30            
31             =head1 VERSION
32            
33             Version 0.17
34            
35             =cut
36            
37             our $VERSION = '0.17';
38            
39            
40             sub databaseName {
41            
42 0     0     local $_ = undef ;
43            
44 0           my $dbh = shift ;
45            
46 0           my $sth2 = $dbh->prepare(databaseNameSQL());
47 0           my $rs = $sth2->execute();
48 0           my $res = $sth2->fetchall_arrayref() ;
49            
50 0           return $$res[0][0] ;
51            
52             }
53            
54             sub databaseNameSQL {
55            
56 0     0     return <<"EOF" ;
57            
58             select db_name()
59            
60             EOF
61            
62             }
63            
64             sub ExecSp {
65            
66 0     0     local $_ = undef ;
67            
68 0           my $dbh = shift ;
69            
70 0           my $sth2 = $dbh->prepare( ExecSpSQL());
71 0           my $rs = $sth2->execute();
72 0           my $res = $sth2->fetchall_arrayref() ;
73            
74 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
75 0           return [] ;
76             }
77            
78            
79            
80             sub ExecSpSQL {
81            
82 0     0     return <<"EOF" ;
83            
84             ; with BASE as (
85             SELECT case when ROUTINE_TYPE = 'PROCEDURE' then cast([PARAMETER_NAME] + ' = ' + [PARAMETER_NAME] + case when PARAMETER_MODE = 'IN' then '' else ' OUTPUT' + CHAR(10) end as VARCHAR(MAX))
86             when ROUTINE_TYPE = 'FUNCTION' then cast([PARAMETER_NAME] as VARCHAR(MAX))
87             end as PARAMTER
88             -- cast([PARAMETER_NAME] + ' = ' + [PARAMETER_NAME] + case when PARAMETER_MODE = 'IN' then '' else ' OUTPUT' + CHAR(10) end as VARCHAR(MAX)) as PARAMTER
89             , cast([PARAMETER_NAME] + ' ' + case when P.DATA_TYPE in ('table type') then user_defined_type_schema +'.'+ user_defined_type_name when P.DATA_TYPE in ('ntext','text') then 'varchar' when P.DATA_TYPE in ('image') then 'varbinary' else P.DATA_TYPE end +
90             case when P.DATA_TYPE not in ('xml') then coalesce('('+case when P.CHARACTER_MAXIMUM_LENGTH = -1 or P.CHARACTER_MAXIMUM_LENGTH > 8000 then 'max' else cast(P.CHARACTER_MAXIMUM_LENGTH as varchar) end +')','') ELSE '' END + CHAR(10) as VARCHAR(MAX)) as DECLARATION
91             , R.[SPECIFIC_CATALOG]
92             , R.[SPECIFIC_SCHEMA]
93             , R.[SPECIFIC_NAME]
94             , [ORDINAL_POSITION]
95             , [PARAMETER_MODE]
96             FROM [INFORMATION_SCHEMA].[PARAMETERS] P
97             JOIN INFORMATION_SCHEMA.ROUTINES R
98             on R.[SPECIFIC_NAME] = P.[SPECIFIC_NAME]
99             and R.[SPECIFIC_SCHEMA] = P.[SPECIFIC_SCHEMA]
100             and R.[SPECIFIC_CATALOG] = P.[SPECIFIC_CATALOG]
101             where 1=1
102             and ORDINAL_POSITION = 1
103             union all
104             select cast(PARAMTER + +char(10)+CHAR(9)+CHAR(9)+CHAR(9)+char(9)+CHAR(9)+CHAR(9)+CHAR(9)+CHAR(9)+',' + CHAR(9)+ CHAR(9) + case when ROUTINE_TYPE = 'PROCEDURE' then cast(N.[PARAMETER_NAME] + ' = ' + N.[PARAMETER_NAME] + case when N.PARAMETER_MODE = 'IN' then '' else ' OUTPUT' + CHAR(10) end as VARCHAR(MAX))
105             when ROUTINE_TYPE = 'FUNCTION' then cast(N.[PARAMETER_NAME] as VARCHAR(MAX))
106             end as VARCHAR(MAX)) as PARAMTER
107             --N.[PARAMETER_NAME] + ' = ' + N.[PARAMETER_NAME] + case when N.PARAMETER_MODE = 'IN' then '' else ' OUTPUT' + CHAR(10) end as varchar(max))
108             , cast(DECLARATION + CHAR(9)+',' + CHAR(9)+CHAR(9) + [PARAMETER_NAME] + ' ' + case when n.DATA_TYPE in ('table type') then user_defined_type_schema +'.'+ user_defined_type_name when N.DATA_TYPE in ('ntext','text') then 'varchar' when N.DATA_TYPE in ('image') then 'varbinary' else N.DATA_TYPE end +
109             case when N.DATA_TYPE not in ('xml') then coalesce('('+case when N.CHARACTER_MAXIMUM_LENGTH = -1 or N.CHARACTER_MAXIMUM_LENGTH > 8000 then 'max' else cast(N.CHARACTER_MAXIMUM_LENGTH as varchar) end +')','') ELSE '' END + CHAR(10) as VARCHAR(MAX))
110             , N.[SPECIFIC_CATALOG]
111             , N.[SPECIFIC_SCHEMA]
112             , N.[SPECIFIC_NAME]
113             , N.[ORDINAL_POSITION]
114             , N.[PARAMETER_MODE]
115             from [INFORMATION_SCHEMA].[PARAMETERS] N
116             JOIN INFORMATION_SCHEMA.ROUTINES R
117             on R.[SPECIFIC_NAME] = N.[SPECIFIC_NAME]
118             and R.[SPECIFIC_SCHEMA] = N.[SPECIFIC_SCHEMA]
119             and R.[SPECIFIC_CATALOG] = N.[SPECIFIC_CATALOG]
120             join BASE B
121             on N.[SPECIFIC_NAME] = B.[SPECIFIC_NAME]
122             and N.[SPECIFIC_SCHEMA] = B.[SPECIFIC_SCHEMA]
123             and N.[SPECIFIC_CATALOG] = B.[SPECIFIC_CATALOG]
124             and N.ORDINAL_POSITION = B.ORDINAL_POSITION+1
125             )
126             , ALLL as (
127             select *
128             , ROW_NUMBER() over (partition by [SPECIFIC_CATALOG],[SPECIFIC_SCHEMA],[SPECIFIC_NAME] order by ORDINAL_POSITION DESC ) as RN
129             from BASE
130             )
131             , PARAMS as (
132             select * from ALLL where RN = 1
133             )
134             select '[' + R.SPECIFIC_SCHEMA + '].[' + R.SPECIFIC_NAME +']' as sp
135             , case when ROUTINE_TYPE = 'FUNCTION' and DATA_TYPE != 'TABLE'
136             then 'declare ' + coalesce(DECLARATION+char(9)+','+char(9)+char(9),'') + '\@RC ' + DATA_TYPE+coalesce('('+cast(CHARACTER_MAXIMUM_LENGTH as varchar)+')','')
137             else coalesce('declare ' + DECLARATION,'')
138             end as DECLARATION
139             , case when ROUTINE_TYPE = 'PROCEDURE' then 'execute [' + R.SPECIFIC_SCHEMA + '].[' + R.SPECIFIC_NAME + '] ' + coalesce(B.PARAMTER,'')
140             when ROUTINE_TYPE = 'FUNCTION' and DATA_TYPE = 'TABLE' then 'select * from [' + R.SPECIFIC_SCHEMA + '].[' + R.SPECIFIC_NAME + '](' + coalesce(B.PARAMTER,'') + ')'
141             when ROUTINE_TYPE = 'FUNCTION' and DATA_TYPE != 'TABLE' then 'select \@RC = [' + R.SPECIFIC_SCHEMA + '].[' + R.SPECIFIC_NAME + '](' + coalesce(B.PARAMTER,'') + ')'
142             else '-- unknown routine type'
143             end as sql
144             from INFORMATION_SCHEMA.ROUTINES R
145             LEFT JOIN PARAMS B
146             on R.[SPECIFIC_NAME] = B.[SPECIFIC_NAME]
147             and R.[SPECIFIC_SCHEMA] = B.[SPECIFIC_SCHEMA]
148             and R.[SPECIFIC_CATALOG] = B.[SPECIFIC_CATALOG]
149             where R.ROUTINE_TYPE in( 'PROCEDURE','FUNCTION')
150            
151            
152             EOF
153            
154             }
155            
156            
157             sub generateScripts {
158            
159 0     0     local $_ = undef;
160            
161 0           my $dbh = shift ;
162 0           my $dbh_typeinfo = shift ;
163 0           my $dirs = shift ;
164 0           my $file = shift ;
165 0           my $runChecks = shift ;
166            
167 0 0         croak "bad arg dbh" unless defined $dbh;
168 0 0         croak "bad arg dbh_typeinfo" unless defined $dbh_typeinfo;
169 0 0         croak "bad arg dirs" unless defined $dirs;
170             #croak "bad arg file" unless defined $file;
171 0 0         croak "bad arg runChecks" unless defined $runChecks;
172            
173 0           my $testSet = undef;
174 0 0         if ( defined $file ) {
175            
176 0           my %ValidParserMakeArgs = ( vb => "NET::VB"
177             , cs => "NET::CS"
178             , xls => "XLS"
179             , xml => "XML"
180             ) ;
181 0           my %ValidParserMakeArgs2 = ( vb => "NET2::VB"
182             , cs => "NET2::CS"
183             ) ;
184            
185             #my @validSuffixes = keys %ValidParserMakeArgs ;
186 0           my @validSuffixes = map { '.'.$_ } keys %ValidParserMakeArgs ;
  0            
187            
188 0           my $infile = $file;
189            
190 0           my($infname, $directories, $insfx) = fileparse($infile , @validSuffixes);
191 0 0         croak 'Invalid input file' unless defined $insfx ;
192 0           $insfx = lc $insfx ;
193 0           $insfx = substr $insfx,1;
194            
195             ### Validate parameters
196 0 0         die 'Invalid input file' unless exists $ValidParserMakeArgs{$insfx} ;
197            
198             ### Build parsers
199            
200 0           my %Parsers = () ;
201 0           $Parsers{${insfx}} = VSGDR::UnitTest::TestSet::Representation->make( { TYPE => $ValidParserMakeArgs{${insfx}} } );
202             # if input is in a .net language, add in a .net2 parser to the list
203 0 0   0     if ( firstidx { $_ eq ${insfx} } ['cs','vb'] != -1 ) {
  0            
204 0           $Parsers{"${insfx}2"} = VSGDR::UnitTest::TestSet::Representation->make( { TYPE => $ValidParserMakeArgs2{${insfx}} } );
205             }
206            
207             ### Deserialise tests
208 0           eval {
209 0           $testSet = $Parsers{$insfx}->deserialise($infile);
210             } ;
211 0 0         if ( not defined $testSet ) {
212 0 0         if ( exists $Parsers{"${insfx}2"}) {
213 0           eval {
214 0           $testSet = $Parsers{"${insfx}2"}->deserialise($infile);
215             }
216             }
217             else {
218 0           croak 'Parsing failed.';
219             }
220             }
221            
222             }
223 0           my @existingTests = () ;
224 0 0         if (defined $testSet) {
225 0           @existingTests = map {$_->testName()} @{$testSet->tests()};
  0            
  0            
226             }
227            
228            
229 0           my $database = databaseName($dbh);
230            
231 1     1   7 no warnings;
  1         1  
  1         70  
232 0 0         my $userName = $OSNAME eq 'MSWin32' ? Win32::LoginName : ${[getpwuid( $< )]}->[6]; $userName =~ s/,.*//;
  0            
  0            
233 1     1   5 use warnings;
  1         2  
  1         653  
234 0           my $date = strftime "%d/%m/%Y", localtime;
235             #warn Dumper $userName ;
236             #warn Dumper $ra_columns ;
237             #exit ;
238            
239 0           my $execs = ExecSp($dbh) ;
240            
241             #warn Dumper $widest_column_name_padding;
242            
243 0           foreach my $exec (@$execs) {
244            
245 0           my $ofile = $$exec[0];
246            
247 0           (my $fileName = "${ofile}" ) =~ s{[.]}{_} ;
248 0           $fileName =~ s{[\]\[]}{}g ;
249 0           $fileName =~ s{\s}{}g ;
250 0           my $testName = $fileName;
251            
252             # if not already defined in the test file (if given)
253 0 0   0     if ( (firstidx { $_ eq $testName } @existingTests ) == -1 ) {
  0            
254            
255            
256 0           my $checkText = "";
257 0           my $receivingTable = "" ;
258            
259 0 0         if ( $runChecks ) {
260            
261 0           $checkText = CheckForExceptions($dbh, $dbh_typeinfo, $$exec[0], $userName, $date, $$exec[1],$$exec[2] ) ;
262            
263 0           my $resultsTable = undef ;
264 0 0 0       if ( ! defined $checkText || $checkText eq q() ) {
265 0           $resultsTable = CheckForResults($dbh, $dbh_typeinfo, $$exec[0], $userName, $date, $$exec[1],$$exec[2] ) ;
266             }
267             #warn Dumper "--------------------------";
268             #warn Dumper $resultsTable;
269             #warn Dumper scalar @$resultsTable ;
270             #warn Dumper @{$resultsTable->[0]};
271 0 0 0       if (defined $resultsTable && scalar @$resultsTable eq 1 && scalar @{$resultsTable->[0]} gt 0 ) {
  0   0        
272 0           $receivingTable = do { local $"= "\n\t,\t\t" ; "\tdeclare \@ResultSet table\n\t(\t\t@{$resultsTable->[0]} \n\t)" } ;
  0            
  0            
  0            
273             # $receivingTable = do { local $"= "\n\t,\t\t" ; "@{$resultsTable->[0]}" } ;
274             }
275             #elsif (scalar @$resultsTable gt 1 ) {
276             # $receivingTable = "More than one set of results - can't capture them" } ;
277             #}
278             #warn Dumper $receivingTable ;
279             #warn Dumper $$exec[2];
280             } ;
281            
282 0           my $text = Template($dbh, $dbh_typeinfo, $$exec[0], $userName, $date, $$exec[1],$$exec[2],$checkText,$receivingTable ) ;
283 0           $fileName .= ".sql";
284            
285 0           my $fh = IO::File->new("> ${dirs}/${fileName}") ;
286            
287 0 0         if (defined ${fh} ) {
288 0           print {${fh}} $text ;
  0            
289 0           $fh->close;
290             }
291             else {
292 0           croak "Unable to write to ${ofile}.sql.";
293             }
294             }
295             }
296            
297 0           exit;
298             }
299            
300             sub Template {
301            
302             local $_ = undef;
303            
304             my $dbh = shift ;
305             my $dbh_typeinfo = shift ;
306            
307             my $sut = shift ;
308            
309             my $userName = shift ;
310             my $date = shift ;
311            
312             my $declaration = shift ;
313             my $code = shift ;
314            
315             my $checkText = shift ;
316             my $receivingTable = shift ;
317            
318             if (defined $checkText) {
319             $checkText = "\t--\t Raises this error:- " . $checkText ;
320             }
321             else {
322             $checkText = q();
323             }
324             if ($receivingTable ne '') {
325             $code = "insert into \@ResultSet\n\t" . $code ;
326             }
327            
328            
329             return <<"EOF";
330            
331            
332             /* AUTHOR
333             * ${userName}
334             *
335             * DESCRIPTION
336             * Tests the minimal case for ${sut}
337             * Runs a basic smoke-test.
338             *
339             * SUT
340             * ${sut}
341             *
342             * OTHER
343             * Other notes.
344             *
345             * CHANGE HISTORY
346             * ${date} ${userName}
347             * Created.
348             */
349            
350            
351             set nocount on
352            
353             begin try
354            
355             declare \@testStatus varchar(100)
356             set \@testStatus = 'Passed'
357            
358             begin transaction
359            
360             ${checkText}
361            
362             ${declaration}
363            
364             ${receivingTable}
365            
366             ${code}
367            
368             select \@testStatus
369            
370            
371             end try
372             begin catch
373            
374             set \@testStatus = 'Failed'
375            
376             select \@testStatus
377             select error_state()
378             select error_message()
379             select error_number()
380            
381             end catch
382            
383            
384             if \@\@trancount > 0 or xact_state() = -1
385             rollback
386            
387            
388             EOF
389            
390             }
391            
392            
393             sub CheckForExceptions {
394            
395             local $_ = undef;
396            
397             my $dbh = shift ;
398             my $dbh_typeinfo = shift ;
399            
400             my $sut = shift ;
401            
402             my $userName = shift ;
403             my $date = shift ;
404            
405             my $declaration = shift ;
406             my $code = shift ;
407            
408             my $sql = CheckForExceptionsSQL($declaration,$code) ;
409            
410             my @run1_res ;
411             my @res_col ;
412             my @res_type ;
413             my $sth = $dbh->prepare($sql,{odbc_exec_direct => 1});
414            
415             try {
416             $sth->execute;
417            
418             do {
419             push @res_type, $sth->{TYPE} ;
420             push @res_col, $sth->{NAME} ;
421            
422             no warnings;
423             push @run1_res, $sth->fetchall_arrayref() ;
424             use warnings;
425             } while ($sth->{odbc_more_results}) ;
426             } catch {
427             warn "SUT :- $sut\n";
428             };
429             #warn Dumper @run1_res ;
430             my $err = undef;
431             if ( scalar @run1_res && scalar @{$run1_res[0]} && $run1_res[0][0][0] eq 'VSGDR::TestScriptGen - raised exception') {
432             $err = $run1_res[0][0][1];
433             }
434             #warn Dumper $err ;
435             return $err;
436             }
437            
438             sub CheckForExceptionsSQL {
439            
440             local $_ = undef;
441            
442             my $declaration = shift ;
443             my $code = shift ;
444            
445             return <<"EOF";
446            
447            
448             set nocount on
449            
450             begin try
451            
452             begin transaction
453            
454             ${declaration}
455             ${code}
456            
457             end try
458             begin catch
459            
460             select 'VSGDR::TestScriptGen - raised exception', error_message()
461            
462             end catch
463            
464            
465             if \@\@trancount > 0 or xact_state() = -1
466             rollback
467            
468            
469             EOF
470            
471             }
472            
473             sub CheckForResults {
474            
475             local $_ = undef;
476            
477             my $dbh = shift ;
478             my $dbh_typeinfo = shift ;
479            
480             my $sut = shift ;
481            
482             my $userName = shift ;
483             my $date = shift ;
484            
485             my $declaration = shift ;
486             my $code = shift ;
487            
488             my $sql = CheckForResultsSQL($declaration,$code) ;
489            
490             my @run1_res ;
491             my @res_col ;
492             my @res_type ;
493             my $sth = $dbh->prepare($sql,{odbc_exec_direct => 1});
494            
495             try {
496             $sth->execute;
497            
498             do {
499             push @res_type, $sth->{TYPE} ;
500             push @res_col, $sth->{NAME} ;
501            
502             my @names = map { scalar $dbh_typeinfo->type_info($_)->{TYPE_NAME} } @{ $sth->{TYPE} } ;
503             my @colSize = map { scalar $dbh_typeinfo->type_info($_)->{COLUMN_SIZE} } @{ $sth->{TYPE} } ;
504            
505             my @types = () ;
506             my @spec = () ;
507             #warn Dumper $sth->{TYPE} ;
508             #warn Dumper $sth->{NUM_OF_FIELDS} ;
509             if (scalar @names) {
510             my $col=1;
511             @types = List::MoreUtils::pairwise { $a =~ m{char|binary}ism ? "$a($b)" : "$a" } @names, @colSize ;
512             @spec = List::MoreUtils::pairwise { ( ($a eq "" ) ? "[Column_" . ${col}++ . "]" : "[$a]" ) . "\t\t\t$b" } @{$sth->{NAME}}, @types ;
513             }
514            
515             #warn Dumper @spec;
516            
517             #do { local $"= "\n,\t" ;
518             # say {*STDERR} "ResultSet(\n\t@{spec}\n)";
519             # };
520            
521             no warnings;
522             push @run1_res, \@spec ;
523             use warnings;
524            
525            
526             } while ($sth->{odbc_more_results}) ;
527             } catch {
528             warn "SUT :- $sut\n";
529             };
530            
531             return \@run1_res;
532             }
533            
534            
535             sub CheckForResultsSQL {
536            
537             local $_ = undef;
538            
539             my $declaration = shift ;
540             my $code = shift ;
541            
542             return <<"EOF";
543            
544            
545             set nocount on
546            
547             begin try
548            
549             begin transaction
550            
551             ${declaration}
552             ${code}
553            
554             end try
555             begin catch
556            
557             select 'VSGDR::TestScriptGen - raised exception', error_message()
558            
559             end catch
560            
561            
562             if \@\@trancount > 0 or xact_state() = -1
563             rollback
564            
565            
566             EOF
567            
568             }
569            
570            
571            
572             1;
573            
574             __DATA__