| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::TestOnTap::Scheduler; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 19 |  |  | 19 |  | 149 | use strict; | 
|  | 19 |  |  |  |  | 54 |  | 
|  | 19 |  |  |  |  | 589 |  | 
| 4 | 19 |  |  | 19 |  | 123 | use warnings; | 
|  | 19 |  |  |  |  | 49 |  | 
|  | 19 |  |  |  |  | 578 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 19 |  |  | 19 |  | 8748 | use TAP::Parser::Scheduler::Job; | 
|  | 19 |  |  |  |  | 7602 |  | 
|  | 19 |  |  |  |  | 581 |  | 
| 7 | 19 |  |  | 19 |  | 8584 | use TAP::Parser::Scheduler::Spinner; | 
|  | 19 |  |  |  |  | 3485 |  | 
|  | 19 |  |  |  |  | 7324 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | # This acts like a TAP Scheduler, but uses the test dispenser | 
| 10 |  |  |  |  |  |  | # to give out jobs. | 
| 11 |  |  |  |  |  |  | # This happens according to eligibility after continually | 
| 12 |  |  |  |  |  |  | # toposorting and removing completed jobs, making | 
| 13 |  |  |  |  |  |  | # it possible to parallelize tests efficiently | 
| 14 |  |  |  |  |  |  | # | 
| 15 |  |  |  |  |  |  | sub new | 
| 16 |  |  |  |  |  |  | { | 
| 17 | 26 |  |  | 26 | 0 | 88 | my $class = shift; | 
| 18 | 26 |  |  |  |  | 59 | my $dispenser = shift; | 
| 19 | 26 |  |  |  |  | 91 | my @testPairs = @_; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | # create | 
| 22 |  |  |  |  |  |  | # | 
| 23 | 26 |  |  |  |  | 322 | my $self = bless | 
| 24 |  |  |  |  |  |  | ( | 
| 25 |  |  |  |  |  |  | { | 
| 26 |  |  |  |  |  |  | pez => $dispenser, | 
| 27 |  |  |  |  |  |  | jobs => undef, | 
| 28 |  |  |  |  |  |  | queue => undef, | 
| 29 |  |  |  |  |  |  | finished => [], | 
| 30 |  |  |  |  |  |  | spinner => TAP::Parser::Scheduler::Spinner->new() | 
| 31 |  |  |  |  |  |  | }, | 
| 32 |  |  |  |  |  |  | $class | 
| 33 |  |  |  |  |  |  | ); | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | # create a list of job objects which is what | 
| 36 |  |  |  |  |  |  | # the harness wants | 
| 37 |  |  |  |  |  |  | # also hook them up to a closure to be called when they finish | 
| 38 |  |  |  |  |  |  | # | 
| 39 | 26 |  |  |  |  | 247 | my %jobs; | 
| 40 | 26 |  |  |  |  | 96 | foreach my $pair (@testPairs) | 
| 41 |  |  |  |  |  |  | { | 
| 42 | 48 |  |  |  |  | 357 | my $job = TAP::Parser::Scheduler::Job->new(@$pair); | 
| 43 | 48 |  |  | 48 |  | 693 | $job->on_finish( sub { $self->__finish(@_) } ); | 
|  | 48 |  |  |  |  | 33607 |  | 
| 44 | 48 |  |  |  |  | 407 | $jobs{$job->description()} = $job; | 
| 45 |  |  |  |  |  |  | } | 
| 46 | 26 |  |  |  |  | 204 | $self->{jobs} = \%jobs; | 
| 47 |  |  |  |  |  |  |  | 
| 48 | 26 |  |  |  |  | 100 | return $self; | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # list of all remaining tests (jobs) | 
| 52 |  |  |  |  |  |  | # | 
| 53 |  |  |  |  |  |  | sub get_all | 
| 54 |  |  |  |  |  |  | { | 
| 55 | 25 |  |  | 25 | 0 | 663 | my $self = shift; | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 25 |  |  |  |  | 56 | return values(%{$self->{jobs}}); | 
|  | 25 |  |  |  |  | 154 |  | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | # return the next eligible job | 
| 61 |  |  |  |  |  |  | #  - if all jobs completed, return undef | 
| 62 |  |  |  |  |  |  | #  - if no job is currently eligible (due to dependencies etc), return a 'spinner' job | 
| 63 |  |  |  |  |  |  | # | 
| 64 |  |  |  |  |  |  | sub get_job | 
| 65 |  |  |  |  |  |  | { | 
| 66 | 85 |  |  | 85 | 0 | 326355 | my $self = shift; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # maintain a queue of eligible tests here since we should deliver just one job | 
| 69 |  |  |  |  |  |  | # at a time but the dispenser may 'free up' multiple at a time | 
| 70 |  |  |  |  |  |  | # | 
| 71 | 85 | 100 | 66 |  |  | 1068 | if (!$self->{queue} || ($self->{queue} && !@{$self->{queue}})) | 
|  | 59 |  | 100 |  |  | 573 |  | 
| 72 |  |  |  |  |  |  | { | 
| 73 |  |  |  |  |  |  | # if there is no queue right now (first call after creation) | 
| 74 |  |  |  |  |  |  | # or the queue is empty, attempt to retrieve a new queue from the | 
| 75 |  |  |  |  |  |  | # dispenser. The dispenser is called with the current list of finished | 
| 76 |  |  |  |  |  |  | # jobs, so it may recompute and see what, if anything, is now free to run | 
| 77 |  |  |  |  |  |  | # | 
| 78 | 81 |  |  |  |  | 1003 | $self->{queue} = $self->{pez}->getEligibleTests($self->{finished}); | 
| 79 | 81 |  |  |  |  | 312 | $self->{finished} = []; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # the dispenser will return undef when there are no more tests, which means we | 
| 83 |  |  |  |  |  |  | # will return undef too | 
| 84 |  |  |  |  |  |  | # | 
| 85 | 85 |  |  |  |  | 272 | my $job; | 
| 86 | 85 | 100 |  |  |  | 348 | if ($self->{queue}) | 
| 87 |  |  |  |  |  |  | { | 
| 88 | 59 | 100 |  |  |  | 145 | if (@{$self->{queue}}) | 
|  | 59 |  |  |  |  | 250 |  | 
| 89 |  |  |  |  |  |  | { | 
| 90 |  |  |  |  |  |  | # if there are tests in the queue, pop the first | 
| 91 |  |  |  |  |  |  | # and translate it to a job object | 
| 92 |  |  |  |  |  |  | # | 
| 93 | 48 |  |  |  |  | 93 | my $t = shift(@{$self->{queue}}); | 
|  | 48 |  |  |  |  | 163 |  | 
| 94 | 48 |  |  |  |  | 156 | $job = $self->{jobs}->{$t}; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  | else | 
| 97 |  |  |  |  |  |  | { | 
| 98 |  |  |  |  |  |  | # the queue is empty but still active, so spin our wheels a bit... | 
| 99 |  |  |  |  |  |  | # | 
| 100 | 11 |  |  |  |  | 39 | $job = $self->{spinner}; | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 85 |  |  |  |  | 454 | return $job; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub __finish | 
| 108 |  |  |  |  |  |  | { | 
| 109 | 48 |  |  | 48 |  | 293 | my $self = shift; | 
| 110 | 48 |  |  |  |  | 141 | my $job = shift; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 48 |  |  |  |  | 260 | my $t = $job->description(); | 
| 113 | 48 |  |  |  |  | 281 | push(@{$self->{finished}}, $t); | 
|  | 48 |  |  |  |  | 289 |  | 
| 114 | 48 |  |  |  |  | 2244 | delete($self->{jobs}->{$t}); | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | 1; |