File Coverage

blib/lib/VSGDR/TestScriptGen.pm
Criterion Covered Total %
statement 71 248 28.6
branch 0 48 0.0
condition 0 14 0.0
subroutine 24 40 60.0
pod 0 10 0.0
total 95 360 26.3


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