File Coverage

bin/tprove
Criterion Covered Total %
statement 54 154 35.0
branch 1 32 3.1
condition 1 22 4.5
subroutine 17 29 58.6
pod n/a
total 73 237 30.8


line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2             # PODNAME: tprove
3             # ABSTRACT: Tapper - alternative 'prove' which uploads results to a Tapper server
4              
5             # -----------------------------------------------------------
6             # Keep this a single file with no external libs and only core
7             # dependencies, so we can use that script in any restricted
8             # environment. Similar spirit as bash-test-utils.
9             # -----------------------------------------------------------
10              
11 1     1   31322 use strict;
  1         2  
  1         33  
12 1     1   4 use warnings;
  1         3  
  1         27  
13              
14 1     1   1156 use Archive::Tar;
  1         5141045  
  1         87  
15 1     1   48465 use IO::Socket::INET;
  1         22581  
  1         8  
16 1     1   1926 use Sys::Hostname "hostname";
  1         1455  
  1         63  
17 1     1   7 use File::Temp "tempfile", "tempdir";
  1         3  
  1         68  
18 1     1   5 use File::Basename "basename", "dirname";
  1         3  
  1         55  
19 1     1   1010 use File::Copy;
  1         2659  
  1         61  
20 1     1   7 use File::Find;
  1         1  
  1         57  
21 1     1   853 use YAML::XS "LoadFile", "DumpFile";
  1         3484  
  1         58  
22 1     1   8 use Cwd;
  1         2  
  1         59  
23              
24 1     1   237801 use Data::Dumper;
  1         8255  
  1         2413  
25              
26             my $tap_archive_support;
27             my $tmp_archive;
28             my $tmp_dir;
29              
30             sub slurp {
31 1     1   3 my ($file) = @_;
32              
33 1         2 my $FILE;
34 1 50       84 open $FILE, "<", $file and do {
35 1         5 local $/;
36 1         93 return <$FILE>;
37             };
38             }
39              
40             sub get_tmp_dir {
41 1   33 1   10 $tmp_dir ||= tempdir(CLEANUP => 1);
42 1         19 return $tmp_dir;
43             }
44              
45             sub get_prove {
46 1     1   8 my $prove = $^X;
47 1         124 $prove = dirname($^X)."/prove";
48 1         17 return $prove;
49             }
50              
51             sub patch_args {
52 1     1   8 my @args = @_;
53              
54             # skip potential archive options
55 1         5 @args = grep { ! ( /^-a$/ ... // ) } @args;
  9         32  
56 1         6 unshift @args, "-a", get_tmp_dir();
57             return @args
58 1         21 }
59              
60             sub run_prove {
61 0     0     system get_prove(), @_;
62             }
63              
64             # a Tapper::Archive is a .tgz containing: tests.(tap|tgz) and optional files.tgz
65             sub pack_tapper_archive {
66 0     0     my $name = "tapper-archive.tgz";
67 0           my $full_name = get_tmp_dir."/$name";
68 0           system ("tar", "-C", get_tmp_dir, "-czf", $full_name, "tests.tgz");
69 0           return $full_name;
70             }
71              
72             sub report_tapper_archive {
73 0     0     my ($archive) = @_;
74              
75 0           my $report_server = $ENV{TAPPER_REPORT_SERVER};
76 0   0       my $report_api_port = $ENV{TAPPER_REPORT_API_PORT} || '7358';
77 0   0       my $report_port = $ENV{TAPPER_REPORT_PORT} || '7357';
78              
79 0 0         if (not $report_server) {
80 0           print "Generated TAP-Archive: $archive";
81 0           return;
82             }
83              
84 0           my $gzipped_content = slurp($archive);
85              
86 0           my $sock = IO::Socket::INET->new(PeerAddr => $report_server,
87             PeerPort => $report_port,
88             Proto => 'tcp');
89 0   0       print STDERR "Report to Tapper at ".($report_server || "report_server=UNDEF").":".($report_port || "report_port=UNDEF");
      0        
90 0 0         unless ($sock) {
91 0           print STDERR "\nResult TAP in $archive can not be sent to Tapper server.\n";
92 0   0       die "\nCan't open connection to ", ($report_server || "report_server=UNDEF"), ":", ($report_port || "report_port=UNDEF"), ":$!"
      0        
93             }
94              
95 0           my $report_id = <$sock>;
96 0           ($report_id) = $report_id =~ /(\d+)$/;
97 0           $sock->print($gzipped_content);
98 0           $sock->close();
99 0 0         print STDERR ", id=$report_id, url=http://$report_server".($report_server =~ /^(localhost|0)$/ ? ":3000" : "")."/tapper/reports/id/$report_id\n";
100 0           return $report_id;
101             }
102              
103             # Return hostname for metainfo in typical Tapper notation, i.e., just
104             # the hostname (without FQDN) in host context or C (colon
105             # separated) in guest context.
106             sub get_machine_name
107             {
108 0     0     my $etc_tapper = "/etc/tapper";
109              
110 0           my $hostname = hostname();
111 0           $hostname =~ s/\..*$//; # no FQDN
112             # combined machine name in Tapper automation guest environment
113 0 0         if ($ENV{TAPPER_HOSTNAME}) {
    0          
114 0           $hostname = "$ENV{TAPPER_HOSTNAME}:$hostname"
115             } elsif ( -r $etc_tapper ) {
116 0           my @tapper_config = ();
117 0           my $TAPPERCFG;
118 0 0         open $TAPPERCFG, "<", $etc_tapper and do {
119 0           local $/;
120 0           @tapper_config = <$TAPPERCFG>;
121 0           close $TAPPERCFG;
122             };
123 0           my ($machinename) =
124             map {
125 0           my $m = $_ ; $m =~ s/^[^:]*:// ; $m
  0            
  0            
126             }
127             grep {
128 0           /hostname:/
129             } @tapper_config;
130 0           $hostname = "${machinename}:$hostname";
131             }
132 0           return $hostname;
133             }
134              
135             sub relative_file_list {
136 0     0     my ($dir) = @_;
137              
138 0           my @files;
139              
140 0           my $olddir = cwd;
141 0           chdir $dir;
142 0 0 0 0     find({ wanted => sub { $_ =~ s/^\.\///; push @files, $_ if -f $_ and $_ !~ /\b(tests\.tgz|tapper-archive.tgz)$/ }, no_chdir => 1 }, ".");
  0            
  0            
143 0           chdir $olddir;
144              
145 0           return sort @files;
146             }
147              
148             sub report_meta {
149 0     0     my $hostname = get_machine_name;
150 0   0       my $testrun_id = $ENV{TAPPER_TESTRUN} || '',
      0        
151             my $report_group = $ENV{TAPPER_REPORT_GROUP} || '',
152              
153             my $suite_name = basename(cwd);
154 0 0         $suite_name = "unknown" if $suite_name eq "/";
155 0           my $suite_version = "";
156 0 0         ($suite_name, $suite_version) = $suite_name =~ m/^(\D*)-([\d.]+)/ if $suite_name =~ /-\d/;
157              
158 0           my $report_meta = "Version 13\n1..1\n# Tapper-Suite-Name: $suite_name\n";
159 0           $report_meta .= "# Tapper-Machine-Name: $hostname\n";
160 0 0         $report_meta .= $suite_version ? "# Tapper-Suite-Version: $suite_version\n" : "";
161 0 0         $report_meta .= $testrun_id ? "# Tapper-Reportgroup-Testrun: $testrun_id\n" : "";
162 0 0         $report_meta .= $report_group ? "# Tapper-Reportgroup-Arbitrary: $report_group\n" : "";
163 0           $report_meta .= "ok 1 - Tapper metainfo\n";
164 0           return $report_meta;
165             }
166              
167             sub patch_archive_meta_file {
168 0     0     my ($meta_file) = @_;
169              
170 0           my $succ = 1;
171 0           my $meta = {};
172 0           eval { $meta = LoadFile($meta_file) };
  0            
173 0 0         $succ = 0 if $@;
174 0           push @{$meta->{file_order}}, 'tapper-meta';
  0            
175 0           DumpFile($meta_file, $meta);
176              
177 0           return $succ;
178             }
179              
180             sub create_archive_file {
181 0     0     my ($dir, $report_meta, $files) = @_;
182              
183 0           my $archive = "$dir/tests.tgz";
184              
185 0           my $olddir = cwd;
186 0           chdir $dir;
187              
188 0           my $tar = Archive::Tar->new;
189 0           $tar->add_data('tapper-meta', $report_meta);
190 0           $tar->add_files(@$files);
191 0           $tar->write($archive, COMPRESS_GZIP);
192              
193 0           chdir $olddir;
194              
195 0           return $archive;
196             }
197              
198             sub patch_archive
199             {
200 0     0     my $report;
201              
202 0           my $report_meta = report_meta;
203 0           my @files = relative_file_list(get_tmp_dir);
204              
205 0 0         unless (patch_archive_meta_file (get_tmp_dir."/meta.yml")) {
206 0           $report_meta .= "# Error loading meta.yml from archive: $@\n";
207 0           $report_meta .= "# Files in archive:\n";
208 0           $report_meta .= $_ foreach map { "# $_\n" } @files;
  0            
209             }
210              
211 0           return create_archive_file(get_tmp_dir, $report_meta, \@files);
212             }
213              
214             sub check_tap_archive_support {
215 0     0     eval { require TAP::Harness::Archive };
  0            
216 0 0         die "No TAP-Archive support. Install TAP::Harness::Archive.\n"
217             if $@;
218             }
219              
220             sub main {
221 0     0     check_tap_archive_support;
222 0           run_prove (patch_args(@ARGV));
223 0           my $tap_archive = patch_archive;
224 0           my $tapper_archive = pack_tapper_archive($tap_archive);
225 0           report_tapper_archive($tap_archive);
226             # TODO: send $tapper_archive, not $tap_archive (needs receiver support)
227             }
228              
229             # are we a lib or a program, e.g., require'd during testing?
230             {
231 1     1   17 no warnings 'uninitialized';
  1         3  
  1         111  
232             ((caller 0)[3] eq "(eval)") ? 1 : main;
233             }
234              
235             __END__