File Coverage

blib/lib/TAP/Parser/SourceHandler/pgTAP.pm
Criterion Covered Total %
statement 46 50 92.0
branch 23 30 76.6
condition 8 10 80.0
subroutine 6 6 100.0
pod 2 2 100.0
total 85 98 86.7


line stmt bran cond sub pod time code
1             package TAP::Parser::SourceHandler::pgTAP;
2              
3 1     1   784 use strict;
  1         2  
  1         29  
4 1     1   4 use vars qw($VERSION @ISA);
  1         2  
  1         47  
5              
6 1     1   418 use TAP::Parser::IteratorFactory ();
  1         1141  
  1         18  
7 1     1   422 use TAP::Parser::Iterator::Process ();
  1         7774  
  1         484  
8              
9             @ISA = qw(TAP::Parser::SourceHandler);
10             TAP::Parser::IteratorFactory->register_handler(__PACKAGE__);
11              
12             our $VERSION = '3.36';
13              
14             =head1 Name
15              
16             TAP::Parser::SourceHandler::pgTAP - Stream TAP from pgTAP test scripts
17              
18             =head1 Synopsis
19              
20             In F for your application with pgTAP tests in F:
21              
22             Module::Build->new(
23             module_name => 'MyApp',
24             test_file_exts => [qw(.t .pg)],
25             use_tap_harness => 1,
26             tap_harness_args => {
27             sources => {
28             Perl => undef,
29             pgTAP => {
30             dbname => 'try',
31             username => 'postgres',
32             suffix => '.pg',
33             },
34             }
35             },
36             build_requires => {
37             'Module::Build' => '0.30',
38             'TAP::Parser::SourceHandler::pgTAP' => '3.19',
39             },
40             )->create_build_script;
41              
42             If you're using L|prove>:
43              
44             prove --source Perl \
45             --ext .t --ext .pg \
46             --source pgTAP --pgtap-option dbname=try \
47             --pgtap-option username=postgres \
48             --pgtap-option suffix=.pg
49              
50             If you have only pgTAP tests, just use C:
51              
52             pg_prove --dbname try --username postgres
53              
54             Direct use:
55              
56             use TAP::Parser::Source;
57             use TAP::Parser::SourceHandler::pgTAP;
58              
59             my $source = TAP::Parser::Source->new->raw(\'mytest.pg');
60             $source->config({ pgTAP => {
61             dbname => 'testing',
62             username => 'postgres',
63             suffix => '.pg',
64             }});
65             $source->assemble_meta;
66              
67             my $class = 'TAP::Parser::SourceHandler::pgTAP';
68             my $vote = $class->can_handle( $source );
69             my $iter = $class->make_iterator( $source );
70              
71             =head1 Description
72              
73             This source handler executes pgTAP tests. It does two things:
74              
75             =over
76              
77             =item 1.
78              
79             Looks at the L passed to it to determine whether or not
80             the source in question is in fact a pgTAP test (L).
81              
82             =item 2.
83              
84             Creates an iterator that will call C to run the pgTAP tests
85             (L).
86              
87             =back
88              
89             Unless you're writing a plugin or subclassing L, you probably
90             won't need to use this module directly.
91              
92             =head2 Testing with pgTAP
93              
94             If you just want to write tests with L, here's how:
95              
96             =over
97              
98             =item *
99              
100             Build your test database, including pgTAP. It's best to install it in its own
101             schema. To build it and install it in the schema "tap", do this (assuming your
102             database is named "try"):
103              
104             make TAPSCHEMA=tap
105             make install
106             psql -U postgres -d try -f pgtap.sql
107              
108             =item *
109              
110             Write your tests in files ending in F<.pg> in the F directory, right
111             alongside your normal Perl F<.t> tests. Here's a simple pgTAP test to get you
112             started:
113              
114             BEGIN;
115              
116             SET search_path = public,tap,pg_catalog;
117              
118             SELECT plan(1);
119              
120             SELECT pass('This should pass!');
121              
122             SELECT * FROM finish();
123             ROLLBACK;
124              
125             Note how C has been set so that the pgTAP functions can be found
126             in the "tap" schema. Consult the extensive L
127             documentation|https://pgtap.org/documentation.html> for a comprehensive list of
128             test functions.
129              
130             =item *
131              
132             Run your tests with C like so:
133              
134             prove --source Perl \
135             --ext .t --ext .pg \
136             --source pgTAP --pgtap-option dbname=try \
137             --pgtap-option username=postgres \
138             --pgtap-option suffix=.pg
139              
140             This will run both your Perl F<.t> tests and your pgTAP F<.pg> tests all
141             together. You can also use L to run just the pgTAP tests like so:
142              
143             pg_prove -d try -U postgres t/
144              
145             =item *
146              
147             Once you're sure that you've got the pgTAP tests working, modify your
148             F script to allow F<./Build test> to run both the Perl and the pgTAP
149             tests, like so:
150              
151             Module::Build->new(
152             module_name => 'MyApp',
153             test_file_exts => [qw(.t .pg)],
154             use_tap_harness => 1,
155             configure_requires => { 'Module::Build' => '0.30', },
156             tap_harness_args => {
157             sources => {
158             Perl => undef,
159             pgTAP => {
160             dbname => 'try',
161             username => 'postgres',
162             suffix => '.pg',
163             },
164             }
165             },
166             build_requires => {
167             'Module::Build' => '0.30',
168             'TAP::Parser::SourceHandler::pgTAP' => '3.19',
169             },
170             )->create_build_script;
171              
172             The C parameter is optional, since it's implicitly set by the
173             use of the C parameter. All the other parameters are
174             required as you see here. See the documentation for C for a
175             complete list of options to the C key under C.
176              
177             And that's it. Now get testing!
178              
179             =back
180              
181             =head1 METHODS
182              
183             =head2 Class Methods
184              
185             =head3 C
186              
187             my $vote = $class->can_handle( $source );
188              
189             Looks at the source to determine whether or not it's a pgTAP test and returns
190             a score for how likely it is in fact a pgTAP test file. The scores are as
191             follows:
192              
193             1 if it's not a file and starts with "pgsql:".
194             1 if it has a suffix equal to that in a "suffix" config
195             0.9 if its suffix is ".pg"
196             0.8 if its suffix is ".sql"
197             0.75 if its suffix is ".s"
198              
199             The latter two scores are subject to change, so try to name your pgTAP tests
200             ending in ".pg" or specify a suffix in the configuration to be sure.
201              
202             =cut
203              
204             sub can_handle {
205 7     7 1 4195 my ( $class, $source ) = @_;
206 7         16 my $meta = $source->meta;
207              
208 7 100       36 unless ($meta->{is_file}) {
209 2 50       5 my $test = ref $source->raw ? ${ $source->raw } : $source->raw;
  2         11  
210 2 50       11 return 1 if $test =~ /^pgsql:/;
211 2         9 return 0;
212             }
213              
214 5         12 my $suf = $meta->{file}{lc_ext};
215              
216             # If the config specifies a suffix, it's required.
217 5 100       11 if ( my $config = $source->config_for('pgTAP') ) {
218 2 50       32 if ( my $suffix = $config->{suffix} ) {
219 2 100       6 if (ref $suffix) {
220 1 50       3 return (grep { $suf eq $_ } @{ $suffix }) ? 1 : 0;
  2         9  
  1         2  
221             }
222 1 50       7 return $suf eq $config->{suffix} ? 1 : 0;
223             }
224             }
225              
226             # Otherwise, return a score for our supported suffixes.
227 3         56 my %score_for = (
228             '.pg' => 0.9,
229             '.sql' => 0.8,
230             '.s' => 0.75,
231             );
232 3   50     15 return $score_for{$suf} || 0;
233             }
234              
235             =head3 C
236              
237             my $iterator = $class->make_iterator( $source );
238              
239             Returns a new L for the source.
240             C<< $source->raw >> must be either a file name or a scalar reference to the
241             file name -- or a string starting with "pgsql:", in which case the remainder
242             of the string is assumed to be SQL to be executed inside the database.
243              
244             The pgTAP tests are run by executing C, the PostgreSQL command-line
245             utility. A number of arguments are passed to it, many of which you can affect
246             by setting up the source source configuration. The configuration must be a
247             hash reference, and supports the following keys:
248              
249             =over
250              
251             =item C
252              
253             The path to the C command. Defaults to simply "psql", which should work
254             well enough if it's in your path.
255              
256             =item C
257              
258             The database to which to connect to run the tests. Defaults to the value of
259             the C<$PGDATABASE> environment variable or, if not set, to the system
260             username.
261              
262             =item C
263              
264             The PostgreSQL username to use to connect to PostgreSQL. If not specified, no
265             username will be used, in which case C will fall back on either the
266             C<$PGUSER> environment variable or, if not set, the system username.
267              
268             =item C
269              
270             Specifies the host name of the machine to which to connect to the PostgreSQL
271             server. If the value begins with a slash, it is used as the directory for the
272             Unix-domain socket. Defaults to the value of the C<$PGDATABASE> environment
273             variable or, if not set, the local host.
274              
275             =item C
276              
277             Specifies the TCP port or the local Unix-domain socket file extension on which
278             the server is listening for connections. Defaults to the value of the
279             C<$PGPORT> environment variable or, if not set, to the port specified at the
280             time C was compiled, usually 5432.
281              
282             =item C
283              
284             Specifies a hash of printing options in the style of C<\pset> in the C
285             program. See the L
286             documentation|https://www.postgresql.org/docs/current/static/app-psql.html> for
287             details on the supported options.
288              
289             =begin comment
290              
291             =item C
292              
293             The schema search path to use during the execution of the tests. Useful for
294             overriding the default search path and you have pgTAP installed in a schema
295             not included in that search path.
296              
297             =end comment
298              
299             =back
300              
301             =cut
302              
303             sub make_iterator {
304 4     4 1 31628 my ( $class, $source ) = @_;
305 4         13 my $config = $source->config_for('pgTAP');
306              
307 4   100     95 my @command = ( $config->{psql} || 'psql' );
308 4         13 push @command, qw(
309             --no-psqlrc
310             --no-align
311             --quiet
312             --pset pager=off
313             --pset tuples_only=true
314             --set ON_ERROR_STOP=1
315             );
316              
317 4         10 for (qw(username host port dbname)) {
318 16 100       37 push @command, "--$_" => $config->{$_} if defined $config->{$_};
319             }
320              
321 4 50       11 if (my $pset = $config->{pset}) {
322 0         0 while (my ($k, $v) = each %{ $pset }) {
  0         0  
323 0         0 push @command, '--pset', "$k=$v";
324             }
325             }
326              
327 4 100       9 if (my $set = $config->{set}) {
328 1         44 while (my ($k, $v) = each %{ $set }) {
  2         21  
329 1         5 push @command, '--set', "$k=$v";
330             }
331             }
332              
333 4 100       18 my $fn = ref $source->raw ? ${ $source->raw } : $source->raw;
  1         8  
334              
335 4 50 66     46 if ($fn && $fn =~ s/^pgsql:\s*//) {
336 0         0 push @command, '--command', $fn;
337             } else {
338 4 100 100     125 $class->_croak(
    100          
339             'No such file or directory: ' . ( defined $fn ? $fn : '' ) )
340             unless $fn && -e $fn;
341 2         7 push @command, '--file', $fn;
342             }
343              
344             # XXX I'd like a way to be able to specify environment variables to set when
345             # the iterator executes the command...
346             # local $ENV{PGOPTIONS} = "--search_path=$config->{search_path}"
347             # if $config->{search_path};
348              
349 2         24 return TAP::Parser::Iterator::Process->new({
350             command => \@command,
351             merge => $source->merge
352             });
353             }
354              
355             =head1 See Also
356              
357             =over
358              
359             =item * L
360              
361             =item * L
362              
363             =item * L
364              
365             =item * L
366              
367             =item * L
368              
369             =item * L
370              
371             =item * L
372              
373             =item * L
374              
375             =item * L
376              
377             =item * L
378              
379             =back
380              
381             =head1 Support
382              
383             This module is managed in an open
384             L.
385             Feel free to fork and contribute, or to clone
386             C and send
387             patches!
388              
389             Found a bug? Please
390             L or
391             L a report!
392              
393             =head1 Author
394              
395             David E. Wheeler
396              
397             =head1 Copyright and License
398              
399             Copyright (c) 2010-2022 David E. Wheeler. Some Rights Reserved.
400              
401             This module is free software; you can redistribute it and/or modify it under
402             the same terms as Perl itself.
403              
404             =cut