ParallelRunner.pm 5.28 KB
Newer Older
1
#!/usr/bin/perl
Mike Hibler's avatar
Mike Hibler committed
2 3
#
# Copyright (c) 2009 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Mike Hibler's avatar
Mike Hibler committed
23
#
24
package TestBed::ParallelRunner;
25
use SemiModern::Perl;
26
use TestBed::ParallelRunner::Executor;
27
use TestBed::ForkFramework;
Kevin Tew's avatar
Kevin Tew committed
28
use TestBed::TestBuilderWrapper;
29
use Data::Dumper;
30
use Moose;
31
use TBConfig;
32

Kevin Tew's avatar
Kevin Tew committed
33 34 35 36 37 38 39 40
has executors => ( is => 'rw', default => sub { [] } );

our $GlobalRunner = TestBed::ParallelRunner->new;

sub executor { 
  my ($s, $itemId) = @_;
  $s->executors->[$itemId];
} 
41

42
sub add_executor { 
Kevin Tew's avatar
Kevin Tew committed
43 44
  my ($s, $executor) = @_;
  push @{$s->executors}, $executor;
45 46 47 48
  $executor;
} 

sub build_executor { 
Kevin Tew's avatar
Kevin Tew committed
49 50
  my $s = shift;
  $s->add_executor(TestBed::ParallelRunner::Executor::build(@_));
51
} 
52 53

sub runtests {
Kevin Tew's avatar
Kevin Tew committed
54
  my ($s, $concurrent_pre_runs, $concurrent_node_count_usage) = @_;
55
  $concurrent_pre_runs         ||= $TBConfig::concurrent_prerun_jobs;
56
  $concurrent_node_count_usage ||= $TBConfig::concurrent_node_usage;
57

58
  if ( $TBConfig::runonly) {
Kevin Tew's avatar
Kevin Tew committed
59
    $s->executors([ (grep { my $executor = $_; (grep { $_ eq $executor->e->eid } @{$TBConfig::runonly}) } @{$s->executors}) ]);
Kevin Tew's avatar
Kevin Tew committed
60 61
  }

62
  #prerun step
Kevin Tew's avatar
Kevin Tew committed
63
  my $result = TestBed::ForkFramework::ForEach::max_work($concurrent_pre_runs, sub { shift->prerun }, $s->executors);
64 65
  if ( $result->has_errors ) { 
    for (@{$result->errors}) {
66 67 68
      my $executor = $s->executor($_->itemid);
      $_->name($executor->e->eid);
      $executor->handleResult(undef, $_);
69
    }
70
    sayd($result->errors);
Kevin Tew's avatar
Kevin Tew committed
71
    warn 'TestBed::ParallelRunner::runtests died during test prep';
72 73
  }

74
  my $workscheduler =  TestBed::ForkFramework::WeightedScheduler->new( 
Kevin Tew's avatar
Kevin Tew committed
75
    items => $s->executors,
Kevin Tew's avatar
Kevin Tew committed
76
    proc => \&tap_wrapper,
77 78 79 80 81
    maxnodes => $concurrent_node_count_usage,
  );

  #add taskss to scheduler step
  my $total_test_count = 0;
82
  for (@{$result->successes}) {
Kevin Tew's avatar
Kevin Tew committed
83
    my $executor = $s->executor($_->itemid);
84
    my $maximum_nodes = $_->result->{'maximum_nodes'};
85
    my $eid = $executor->e->eid;
86

87 88
    if ($maximum_nodes > $concurrent_node_count_usage) {
      warn "$eid requires upto $maximum_nodes nodes, only $concurrent_node_count_usage concurrent nodes permitted\n$eid will not be run";
89
      $executor->e->end_wait;
90 91
    }
    else {
Kevin Tew's avatar
Kevin Tew committed
92
      $workscheduler->add_task($executor, $maximum_nodes);
93
      $total_test_count += $executor->test_count;
94
    }
95
  }
96

97
  USE_TESTBULDER_PREAMBLE: {
Kevin Tew's avatar
Kevin Tew committed
98
    TestBed::TestBuilderWrapper::reset_test_builder($total_test_count, no_numbers => 1);
99
  }
100

101
  #run tests
102 103 104 105 106
  $result = $workscheduler->run;

  USE_TESTBULDER_POSTAMBLE: {
    $total_test_count = 0;
    for (@{$result->successes}) {
Kevin Tew's avatar
Kevin Tew committed
107
      my $executor = $s->executor($_->itemid);
108 109
      $total_test_count += $executor->test_count;
    }
Kevin Tew's avatar
Kevin Tew committed
110
    TestBed::TestBuilderWrapper::set_test_builder_to_end_state($total_test_count);
111 112
  }

Kevin Tew's avatar
Kevin Tew committed
113
  if ($result->has_errors) {
Kevin Tew's avatar
Kevin Tew committed
114 115 116 117
    for (@{$result->errors}) {
      my $executor = $s->executor($_->itemid);
      warn $executor->failReason($_);   
    }
Kevin Tew's avatar
Kevin Tew committed
118 119 120
    sayd($result->errors);
    die 'TestBed::ParallelRunner::runtests died during test execution';
  }
121 122 123
  return;
}

124
our $ENABLE_SUBTESTS_FEATURE = 0;
125 126 127

sub tap_wrapper {
  my ($te) = @_;
128
  
129
  if ($ENABLE_SUBTESTS_FEATURE) {
130
    TestBed::ForkFramework::fork_redir( sub {
131 132 133 134 135 136 137
      my ($in, $out, $err, $pid) = @_;
      #while(<$out>) { print "K2" . $_; }
      use TAP::Parser;  
      my $tapp = TAP::Parser->new({'stream' => TAP::Parser::Iterator::StdOutErr->new($out, $err, $pid)});
      while ( defined( my $result = $tapp->next ) ) {
        #sayd($result);
      }
138
      ok(1, $te->desc) if $ENABLE_SUBTESTS_FEATURE && $tapp;
139 140
    },
    sub {
Kevin Tew's avatar
Kevin Tew committed
141 142
      TestBed::TestBuilderWrapper::reset_test_builder($te->test_count) if $ENABLE_SUBTESTS_FEATURE;
      TestBed::TestBuilderWrapper::setup_test_builder_ouputs(*STDOUT, *STDERR);
143
      $te->execute;
144 145 146
    });
  }
  else {
147
    $te->execute;
148
  }
149
  return 0;
150 151
}

152 153 154 155 156 157
=head1 NAME

TestBed::ParallelRunner

=over 4

Kevin Tew's avatar
Kevin Tew committed
158 159 160 161 162
=item C<< $pr->executor($itemid) >>

return the $itemid th executor

=item C<< $pr->build_executor >>
163 164

helper function called by rege.
165 166
creates a TestBed::ParallelRunner::Executor job

Kevin Tew's avatar
Kevin Tew committed
167 168 169
=item C<< $pr->add_executor($executor) >>

pushes $executor onto $s->executors list
170

Kevin Tew's avatar
Kevin Tew committed
171
=item C<< $pr->runtests($concurrent_pre_runs, $concurrent_node_count_usage) >>
172

Kevin Tew's avatar
Kevin Tew committed
173 174
allows a maximum of $concurrent_pre_runs during parallel execution
allows a maximum of $concurrent_nodes during parallel execution
175

Kevin Tew's avatar
Kevin Tew committed
176
start the execution of parallel tests
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

=item C<< set_test_builder_to_end_state >>
=item C<< reset_test_builder >>
=item C<< setup_test_builder_ouputs >>

B<INTERNAL> functions to get Test::Builder to behave correctly with parallel tests

=item C<< tap_wrapper >>

wraps two different ways of executing parallel tests and wrapping their TAP output stream

=back

=cut

192
1;
193