]> git.donarmstrong.com Git - debbugs.git/blob - t/lib/DebbugsTest.pm
add submit_but and run_processall utility routines
[debbugs.git] / t / lib / DebbugsTest.pm
1
2 package DebbugsTest;
3
4 =head1 NAME
5
6 DebbugsTest
7
8 =head1 SYNOPSIS
9
10 use DebbugsTest
11
12
13 =head1 DESCRIPTION
14
15 This module contains various testing routines used to test debbugs in
16 a "pseudo install"
17
18 =head1 FUNCTIONS
19
20 =cut
21
22 use warnings;
23 use strict;
24 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
25 use base qw(Exporter);
26
27 use v5.10;
28
29 use IO::File;
30 use File::Temp qw(tempdir);
31 use Cwd qw(getcwd);
32 use Debbugs::MIME qw(create_mime_message);
33 use File::Basename qw(dirname basename);
34 use IPC::Open3;
35 use IO::Handle;
36 use Test::More;
37 use Test::PostgreSQL;
38
39 use Params::Validate qw(validate_with :types);
40
41 BEGIN{
42      $VERSION = 1.00;
43      $DEBUG = 0 unless defined $DEBUG;
44
45      @EXPORT = ();
46      %EXPORT_TAGS = (configuration => [qw(dirsize create_debbugs_configuration send_message),
47                                        qw(submit_bug run_processall)],
48                      mail          => [qw(num_messages_sent)],
49                      control       => [qw(test_control_commands)],
50                      database => [qw(create_postgresql_database update_postgresql_database)]
51                     );
52      @EXPORT_OK = ();
53      Exporter::export_ok_tags(keys %EXPORT_TAGS);
54      $EXPORT_TAGS{all} = [@EXPORT_OK];
55 }
56
57 # First, we're going to send mesages to receive.
58 # To do so, we'll first send a message to submit,
59 # then send messages to the newly created bugnumber.
60
61
62
63 sub create_debbugs_configuration {
64      my %param = validate_with(params => \@_,
65                                spec   => {debug => {type => BOOLEAN,
66                                                     default => exists $ENV{DEBUG}?
67                                                     $ENV{DEBUG}:0,
68                                                    },
69                                           cleanup => {type => BOOLEAN,
70                                                       optional => 1,
71                                                      },
72                                          },
73                               );
74      $param{cleanup} = $param{debug}?0:1 if not exists $param{cleanup};
75      my $sendmail_dir = tempdir(CLEANUP => $param{cleanup});
76      my $spool_dir = tempdir(CLEANUP => $param{cleanup});
77      my $config_dir = tempdir(CLEANUP => $param{cleanup});
78
79
80      $ENV{DEBBUGS_CONFIG_FILE}  ="$config_dir/debbugs_config";
81      $ENV{PERL5LIB} = getcwd();
82      $ENV{SENDMAIL_TESTDIR} = $sendmail_dir;
83      eval {
84      my $sendmail_tester = getcwd().'/t/sendmail_tester';
85      unless (-x $sendmail_tester) {
86           die q(t/sendmail_tester doesn't exist or isn't executable. You may be in the wrong directory.);
87      }
88      my %files_to_create = ("$config_dir/debbugs_config" => <<END,
89 \$gSendmail='$sendmail_tester';
90 \$gSpoolDir='$spool_dir';
91 \$gLibPath='@{[getcwd()]}/scripts';
92 \$gTemplateDir='@{[getcwd()]}/templates';
93 \$gWebDir='@{[getcwd()]}/html';
94 \$gWebHost='localhost';
95 1;
96 END
97                             "$spool_dir/nextnumber" => qq(1\n),
98                             "$config_dir/Maintainers" => qq(foo Blah Bleargh <foo\@baz.com>\nbar Bar Bleargh <bar\@baz.com>\n),
99                             "$config_dir/Maintainers.override" => qq(),
100                             "$config_dir/Source_maintainers" => qq(foo Blah Bleargh <foo\@baz.com>\nbar Bar Bleargh <bar\@baz.com>\n),
101                             "$config_dir/indices/sources" => <<END,
102 foo main foo
103 END
104                             "$config_dir/pseudo-packages.description" => '',
105                             "$config_dir/pseudo-packages.maintainers" => '',
106                            );
107      while (my ($file,$contents) = each %files_to_create) {
108           system('mkdir','-p',dirname($file));
109           my $fh = IO::File->new($file,'w') or
110                die "Unable to create $file: $!";
111           print {$fh} $contents or die "Unable to write $contents to $file: $!";
112           close $fh or die "Unable to close $file: $!";
113      }
114
115      system('touch',"$spool_dir/index.db.realtime");
116      system('ln','-s','index.db.realtime',
117             "$spool_dir/index.db");
118      system('touch',"$spool_dir/index.archive.realtime");
119      system('ln','-s','index.archive.realtime',
120             "$spool_dir/index.archive");
121
122      # create the spool files and sub directories
123      for my $dir (0..99) {
124          for my $archive (qw(db-h archive)) {
125              system('mkdir','-p',"$spool_dir/$archive/".sprintf('%02d',$dir));
126          }
127      }
128      system('mkdir','-p',"$spool_dir/incoming");
129      system('mkdir','-p',"$spool_dir/lock");
130      # generate the maintainers index files
131      system('scripts/maintainer-indices') == 0
132          or die "Unable to generate maintainer index files";
133      eval '
134 END{
135      if ($ENV{DEBUG}) {
136           diag("spool_dir:   $spool_dir\n");
137           diag("config_dir:   $config_dir\n",);
138           diag("sendmail_dir: $sendmail_dir\n");
139      }
140 }';
141
142      };
143      BAIL_OUT ($@) if ($@);
144      return (spool_dir => $spool_dir,
145              sendmail_dir => $sendmail_dir,
146              config_dir => $config_dir,
147             );
148 }
149
150 sub dirsize{
151      my ($dir) = @_;
152      opendir(DIR,$dir);
153      my @content = grep {!/^\.\.?$/} readdir(DIR);
154      closedir(DIR);
155      return scalar @content;
156 }
157
158
159 # We're going to use create mime message to create these messages, and
160 # then just send them to receive.
161 # First, check that submit@ works
162
163 sub send_message{
164      my %param = validate_with(params => \@_,
165                                spec   => {to => {type => SCALAR,
166                                                  default => 'submit@bugs.something',
167                                                 },
168                                           headers => {type => ARRAYREF,
169                                                      },
170                                           body    => {type => SCALAR,
171                                                      },
172                                           attachments => {type => ARRAYREF,
173                                                           default => [],
174                                                          },
175                                           run_processall =>{type => BOOLEAN,
176                                                             default => 1,
177                                                            },
178                                          }
179                               );
180      $ENV{LOCAL_PART} = $param{to};
181      my ($rfd,$wfd);
182      my $output='';
183      my $pipe_handler = $SIG{PIPE};
184      $SIG{PIPE} = 'IGNORE';
185      $SIG{CHLD} = 'DEFAULT';
186      my $pid = open3($wfd,$rfd,$rfd,'scripts/receive')
187           or die "Unable to start receive: $!";
188      print {$wfd} create_mime_message($param{headers},
189                                       $param{body},
190                                       $param{attachments}) or
191                                           die "Unable to to print to receive";
192      close($wfd) or die "Unable to close receive";
193      $SIG{PIPE} = $pipe_handler;
194      my $err = $? >> 8;
195      my $childpid = waitpid($pid,0);
196      if ($childpid != -1) {
197           $err = $? >> 8;
198           print STDERR "receive pid: $pid doesn't match childpid: $childpid\n" if $childpid != $pid;
199      }
200      if ($err != 0 ) {
201           my $rfh =  IO::Handle->new_from_fd($rfd,'r') or die "Unable to create filehandle: $!";
202           $rfh->blocking(0);
203           my $rv;
204           while ($rv = $rfh->sysread($output,1000,length($output))) {}
205           if (not defined $rv) {
206                print STDERR "Reading from STDOUT/STDERR would have blocked.";
207           }
208           print STDERR $output,qq(\n);
209           die "receive failed with exit status $err";
210      }
211      # now we should run processall to see if the message gets processed
212      if ($param{run_processall}) {
213          run_processall();
214       }
215      return 1;
216 }
217
218 sub run_processall {
219     system('scripts/processall') == 0 or die "processall failed";
220 }
221
222 =item test_control_commands
223
224  test_control_commands(\%config,
225                        forcemerge => {command => 'forcemerge',
226                                       value   => '1 2',
227                                       status_key => 'mergedwith',
228                                       status_value => '2',
229                                       expect_error => 0,
230                                      });
231
232 Test a set of control commands to see if they will fail or not. Takes
233 SCALAR/HASHREF pairs, where the scalar should be unique, and the HASHREF
234 contains the following keys:
235
236 =over
237
238 =item command -- control command to issue
239
240 =item value -- value to pass to control command
241
242 =item status_key -- bug status key to check
243
244 =item status_value -- value of status key
245
246 =item expect_error -- whether to expect the control command to error or not
247
248 =back
249
250 =cut
251
252 sub test_control_commands {
253     my ($config,@commands) = @_;
254
255     # now we need to check to make sure that the control message actually did anything
256     # This is an eval because $ENV{DEBBUGS_CONFIG_FILE} isn't set at BEGIN{} time
257     eval "use Debbugs::Status qw(read_bug writebug);";
258     while (my ($command,$control_command) = splice(@commands,0,2)) {
259         # just check to see that control doesn't explode
260         $control_command->{value} = " $control_command->{value}" if length $control_command->{value}
261             and $control_command->{value} !~ /^\s/;
262         send_message(to => 'control@bugs.something',
263                      headers => [To   => 'control@bugs.something',
264                                  From => 'foo@bugs.something',
265                                  Subject => "Munging a bug with $command",
266                                 ],
267                      body => <<EOF) or fail 'message to control@bugs.something failed';
268 debug 10
269 $control_command->{command} $control_command->{value}
270 thanks
271 EOF
272         ;
273         # now we need to check to make sure the control message was processed without errors
274         if (not ($control_command->{expect_error} // 0)) {
275             ok(system('sh','-c','find '.$config->{sendmail_dir}.
276                       q( -type f | xargs grep -q "Subject: Processed: Munging a bug with $command")
277                      ) == 0,
278                'control@bugs.something'. "$command message was parsed without errors");
279         }
280         # now we need to check to make sure that the control message actually did anything
281         my $status;
282         $status = read_bug(exists $control_command->{bug}?(bug => $control_command->{bug}):(bug=>1),
283                            exists $control_command->{location}?(location => $control_command->{location}):(),
284                           );
285         is_deeply($status->{$control_command->{status_key}},
286                   $control_command->{status_value},
287                   "bug " .
288                   (exists $control_command->{bug}?$control_command->{bug}:1).
289                   " $command"
290                  )
291             or fail(Data::Dumper->Dump([$status],[qw(status)]));
292     }
293 }
294
295 sub submit_bug {
296     state $spec =
297        {subject => {type => SCALAR,
298                     default => 'Submitting a bug',
299                    },
300         body => {type => SCALAR,
301                  default => 'This is a silly bug',
302                 },
303         submitter => {type => SCALAR,
304                       default => 'foo@bugs.something',
305                      },
306         pseudoheaders => {type => HASHREF,
307                           default => sub {{}},
308                          },
309         package => {type => SCALAR,
310                     default => 'foo',
311                    },
312         run_processall => {type => SCALAR,
313                            default => 0,
314                           },
315        };
316     my %param =
317         validate_with(params => \@_,
318                       spec => $spec);
319     my $body = 'Package: '.$param{package}."\n";
320     foreach my $key (keys %{$param{pseudoheaders}}) {
321         for my $val (ref($param{pseudoheaders}{$key}) ?
322                      @{$param{pseudoheaders}{$key}} :
323                      $param{pseudoheaders}{$key}) {
324             $body .= $key. ': '.$val."\n";
325         }
326     }
327     $body .="\n".$param{body};
328     send_message(to => 'submit@bugs.something',
329                  headers => [To => 'submit@bugs.something',
330                              From => $param{submitter},
331                              Subject => $param{subject},
332                             ],
333                  run_processall => $param{run_processall},
334                  body => $body
335                 );
336 }
337
338
339 {
340      package DebbugsTest::HTTPServer;
341      use base qw(HTTP::Server::Simple::CGI HTTP::Server::Simple::CGI::Environment);
342
343      our $child_pid = undef;
344      our $webserver = undef;
345      our $server_handler = undef;
346
347      END {
348           if (defined $child_pid) {
349                # stop the child
350                my $temp_exit = $?;
351                kill(15,$child_pid);
352                waitpid(-1,0);
353                $? = $temp_exit;
354           }
355      }
356
357      sub fork_and_create_webserver {
358           my ($handler,$port) = @_;
359           $port ||= 8080;
360           if (defined $child_pid) {
361                die "We appear to have already forked once";
362           }
363           $server_handler = $handler;
364           my $pid = fork;
365           return 0 if not defined $pid;
366           if ($pid) {
367                $child_pid = $pid;
368                # Wait here for a second to let the child start up
369                sleep 1;
370                return $pid;
371           }
372           else {
373                $webserver = DebbugsTest::HTTPServer->new($port);
374                $webserver->run;
375           }
376
377      }
378
379      sub handle_request {
380           if (defined $server_handler) {
381                $server_handler->(@_);
382           }
383           else {
384                warn "No handler defined\n";
385                print "No handler defined\n";
386           }
387      }
388 }
389
390 =head2 num_messages_sent
391
392      $SD_SIZE = num_messages_sent($SD_SIZE,2,$sendmail_dir,'2 messages have been sent properly');
393
394 Tests to make sure that at least a certain number of messages have
395 been sent since the last time this command was run. Usefull to test to
396 make sure that mail has been sent.
397
398 =cut
399
400 sub num_messages_sent {
401     my ($prev_size,$num_messages,$sendmail_dir,$test_name) = @_;
402     my $cur_size = dirsize($sendmail_dir);
403     ## print STDERR "sendmail: $sendmail_dir, want: $num_messages,
404     ## size: $cur_size, prev_size: $prev_size\n";
405     ok($cur_size-$prev_size >= $num_messages, $test_name);
406     return $cur_size;
407 }
408
409 =head2 create_postgresql_database
410
411 C<my $pgsql = create_postgresql_database();>
412
413 Create a postgresql database for testing; when the L<Test::PostgreSQL> object it
414 returns is destroyed (or goes out of scope) the database will be removed.
415
416 =cut
417
418 sub create_postgresql_database {
419     my $pgsql = Test::PostgreSQL->new(use_socket => 1) or
420         return undef;
421     my $installsql =
422         File::Spec->rel2abs(dirname(__FILE__).'/../..').
423             '/bin/debbugs-installsql';
424     # create the debversion extension
425     my $dbh = DBI->connect($pgsql->dsn);
426     $dbh->do(<<END) or die "Unable to create extension";
427 CREATE EXTENSION IF NOT EXISTS debversion;
428 END
429     # create the schema for the bug tracking system
430     my $dep_dir = File::Temp::tempdir(CLEANUP=>1);
431     system($installsql,
432            '--dsn',$pgsql->dsn,
433            '--install',
434            '--deployment-dir',$dep_dir);
435
436     initialize_postgresql_database($pgsql,@_);
437     return $pgsql;
438 }
439
440 =item iniitalize_postgresql_database
441
442 C<initialize_postgresql_database();>
443
444 Initialize postgresql database by calling debbugs-loadsql appropriately.
445
446 =cut
447
448 sub initialize_postgresql_database {
449     my ($pgsql,@options) = @_;
450     my $loadsql =
451         File::Spec->rel2abs(dirname(__FILE__).'/../..').
452             '/bin/debbugs-loadsql';
453
454     my $ftpdists =
455         File::Spec->rel2abs(dirname(__FILE__).'/../debian/dist');
456     my $debinfo_dir =
457         File::Spec->rel2abs(dirname(__FILE__).'/../debian/debinfo');
458     my %loadsql_commands =
459         (configuration => [],
460          suites => ['--ftpdists',$ftpdists],
461          debinfo => ['--debinfo-dir',$debinfo_dir],
462          packages => ['--ftpdists',$ftpdists],
463          maintainers => [],
464         );
465     for my $command (keys %loadsql_commands) {
466         system($loadsql,$command,
467                '--dsn',$pgsql->dsn,
468                @options,
469                @{$loadsql_commands{$command}}) == 0 or
470                    die "Unable to load $command";
471     }
472 }
473
474
475 =item update_postgresql_database
476
477 C<update_postgresql_database();>
478
479 Update the postgresql database by calling debbugs-loadsql appropriately.
480
481 =cut
482 sub update_postgresql_database {
483     my ($pgsql,@options) = @_;
484     my $loadsql =
485         File::Spec->rel2abs(dirname(__FILE__).'/../..').
486             '/bin/debbugs-loadsql';
487
488     my %loadsql_commands =
489         (bugs_and_logs => [],
490         );
491     for my $command (keys %loadsql_commands) {
492         system($loadsql,$command,
493                '--dsn',$pgsql->dsn,
494                @options,
495                @{$loadsql_commands{$command}}) == 0 or
496                    die "Unable to load $command";
497     }
498 }
499
500
501
502 1;
503
504 __END__
505
506
507