File Coverage

lib/Test/Bot.pm
Criterion Covered Total %
statement 12 59 20.3
branch 0 8 0.0
condition n/a
subroutine 4 11 36.3
pod n/a
total 16 78 20.5


line stmt bran cond sub pod time code
1             package Test::Bot;
2              
3 1     1   30008 use Any::Moose 'Role';
  1         238022  
  1         7  
4 1     1   4516 use AnyEvent;
  1         5874  
  1         35  
5 1     1   1141 use Class::MOP;
  1         397652  
  1         70  
6 1     1   12 use Carp qw/croak/;
  1         1  
  1         909  
7              
8             our $VERSION = '0.09';
9              
10             =head1 NAME
11              
12             Test::Bot - Continuous integration bot for automatically running unit tests and notifying developers of failed tests
13              
14             =head1 SYNOPSIS
15              
16             See README
17              
18             =head1 AUTHOR
19              
20             Mischa Spiegelmock, C<< <revmischa at cpan.org> >>
21              
22             =cut
23              
24             #use Any::Moose 'X::Getopt'; # why the heck does this not work?
25             with 'MouseX::Getopt';
26              
27             # local source repo checkout
28             has 'source_dir' => (
29             is => 'rw',
30             isa => 'Str',
31             required => 1,
32             traits => [ 'Getopt' ],
33             cmd_flag => 'source-dir',
34             cmd_aliases => 's',
35             );
36              
37             # forcibly check out the current commit we are testing?
38             # warning: will overwrite local changes
39             has 'force' => ( is => 'rw', isa => 'Bool' );
40              
41             has 'tests_dir' => (
42             is => 'rw',
43             isa => 'Str',
44             required => 1,
45             traits => [ 'Getopt' ],
46             cmd_flag => 'tests-dir',
47             cmd_aliases => 't',
48             );
49              
50             has 'notification_modules' => (
51             is => 'rw',
52             isa => 'ArrayRef[Str]',
53             required => 0,
54             traits => [ 'Getopt' ],
55             cmd_flag => 'notifs',
56             cmd_aliases => 'n',
57             default => sub { ['Print'] },
58             );
59              
60             has 'preload_modules' => (
61             is => 'rw',
62             isa => 'ArrayRef[Str]',
63             required => 0,
64             traits => [ 'Getopt' ],
65             cmd_flag => 'preload',
66             cmd_aliases => 'p',
67             default => sub { ['Test::More'] },
68             );
69              
70             has 'test_harness_module' => (
71             is => 'rw',
72             isa => 'Str',
73             required => 1,
74             traits => [ 'Getopt' ],
75             cmd_flag => 'test_harness',
76             cmd_aliases => 't',
77             default => 'Aggregate',
78             );
79              
80             has '_notify_instances' => (
81             is => 'rw',
82             isa => 'ArrayRef',
83             traits => [ 'Array' ],
84             handles => {
85             add_notify_module_instance => 'push',
86             notify_instances => 'elements',
87             },
88             default => sub { [] },
89             );
90              
91             has '_configured_test_harness' => ( is => 'rw', isa => 'Bool' );
92             has '_configured_notifications' => ( is => 'rw', isa => 'Bool' );
93              
94             requires 'install';
95             requires 'watch';
96              
97             sub load_test_harness {
98 0     0     my ($self) = @_;
99              
100 0           my $harness = $self->test_harness_module;
101 0           my $harness_class = "Test::Bot::TestHarness::$harness";
102 0           Class::MOP::load_class($harness_class);
103 0           $harness_class->meta->apply($self);
104 0           print "+Loaded $harness test harness\n";
105            
106 0           requires 'run_tests_for_commit';
107             }
108              
109             sub load_notify_modules {
110 0     0     my ($self) = @_;
111              
112 0           foreach my $module (@{ $self->notification_modules }) {
  0            
113 0           my $notif_class = "Test::Bot::Notify::$module";
114 0           Class::MOP::load_class($notif_class);
115 0           my $i = $notif_class->new(
116             bot => $self,
117             );
118 0           $self->add_notify_module_instance($i);
119              
120 0           print "+Loaded $module notification module\n";
121             }
122             }
123              
124             sub notify {
125 0     0     my ($self, @commits) = @_;
126              
127 0           $_->notify(@commits) for $self->notify_instances;
128             }
129              
130             sub configure_test_harness {
131 0     0     my ($self, %config) = @_;
132              
133 0 0         return if $self->_configured_test_harness;
134            
135 0           $self->load_test_harness;
136              
137 0           while (my ($k, $v) = each %config) {
138 0 0         my $setter = $self->can($k) or croak "Unknown test harness setting $k";
139 0           $setter->($self, $v);
140             }
141              
142 0           $self->_configured_test_harness(1);
143             }
144              
145             sub configure_notifications {
146 0     0     my ($self, %config) = @_;
147              
148 0 0         return if $self->_configured_notifications;
149              
150 0           $self->load_notify_modules;
151              
152             # prepare notify instances
153 0           foreach my $ni ($self->notify_instances) {
154 0           while (my ($k, $v) = each %config) {
155 0 0         my $setter = $ni->can($k) or next;
156 0           $setter->($ni, $v);
157             }
158            
159 0           $ni->setup;
160             }
161              
162 0           $self->_configured_notifications(1);
163             }
164              
165             # preload libraries shared by tests
166             sub load_preload_modules {
167 0     0     my ($self) = @_;
168              
169 0           foreach my $module (@{ $self->preload_modules }) {
  0            
170 0           local $| = 1;
171 0           print "+Loading $module... ";
172 0           Class::MOP::load_class($module);
173 0           print "loaded.\n";
174             }
175             }
176              
177             sub run {
178 0     0     my ($self) = @_;
179              
180 0           $self->load_preload_modules;
181            
182 0           $self->configure_test_harness;
183 0           $self->configure_notifications;
184              
185             # listen...
186 0           $self->install;
187              
188             # ...and wait
189 0           $self->watch;
190              
191             # run forever.
192 0           AE::cv->recv;
193             }
194              
195             1;