File Coverage

blib/lib/Test/DBIx/Class/SchemaManager.pm
Criterion Covered Total %
statement 116 127 91.3
branch 40 58 68.9
condition 7 9 77.7
subroutine 24 26 92.3
pod 0 12 0.0
total 187 232 80.6


line stmt bran cond sub pod time code
1             package Test::DBIx::Class::SchemaManager;
2              
3 17     17   63 use Moose;
  17         21  
  17         117  
4 17     17   82243 use MooseX::Attribute::ENV;
  17         125347  
  17         445  
5 17     17   85 use Moose::Util;
  17         18  
  17         61  
6 17     17   2395 use Test::More ();
  17         3712  
  17         282  
7 17     17   7965 use List::MoreUtils qw(uniq);
  17         124171  
  17         1077  
8 17         107 use Test::DBIx::Class::Types qw(
9             TestBuilder SchemaManagerClass FixtureClass ConnectInfo
10 17     17   14453 );
  17         51  
11              
12             has 'force_drop_table' => (
13             traits=>['ENV'],
14             is=>'rw',
15             isa=>'Bool',
16             required=>1,
17             default=>0,
18             );
19              
20             has [qw/keep_db tdbic_debug/] => (
21             traits=>['ENV'],
22             is=>'ro',
23             isa=>'Bool',
24             required=>1,
25             default=>0,
26             );
27              
28             has 'deploy_db' => (
29             traits=>['ENV'],
30             is=>'ro',
31             isa=>'Bool',
32             required=>1,
33             default=>1,
34             );
35              
36             has 'builder' => (
37             is => 'ro',
38             isa => TestBuilder,
39             required => 1,
40             );
41              
42             has 'schema_class' => (
43             traits => ['ENV'],
44             is => 'ro',
45             isa => SchemaManagerClass,
46             required => 1,
47             coerce => 1,
48             );
49              
50             has 'schema' => (
51             is => 'ro',
52             lazy_build => 1,
53             );
54              
55             has 'connect_info' => (
56             is => 'ro',
57             isa => ConnectInfo,
58             coerce => 1,
59             lazy_build => 1,
60             );
61              
62             has 'connect_opts' => (
63             is => 'ro',
64             isa => 'HashRef',
65             );
66              
67             has 'deploy_opts' => (
68             is => 'ro',
69             isa => 'HashRef',
70             default => sub { {} },
71             );
72              
73             has 'connect_info_with_opts' => (
74             is => 'ro',
75             isa => 'HashRef',
76             lazy_build => 1,
77             );
78              
79             has 'fixture_class' => (
80             traits => ['ENV'],
81             is => 'ro',
82             isa => FixtureClass,
83             required => 1,
84             coerce => 1,
85             default => '::Populate',
86             );
87              
88             has 'fixture_command' => (
89             is => 'ro',
90             init_arg => undef,
91             lazy_build => 1,
92             );
93              
94             has 'fixture_sets' => (
95             is => 'ro',
96             isa => 'HashRef',
97             );
98              
99             has 'last_statement' => (
100             is=>'rw',
101             isa=>'Str',
102             );
103              
104             sub get_fixture_sets {
105 5     5 0 12 my ($self, @sets) = @_;
106 5         7 my @return;
107 5         13 foreach my $set (@sets) {
108 5 50       217 if(my $fixture = $self->fixture_sets->{$set}) {
109 5         12 push @return, $fixture;
110             }
111             }
112 5         17 return @return;
113             }
114              
115             sub _build_schema {
116 20     20   41 my $self = shift @_;
117 20         634 my $schema_class = $self->schema_class;
118 20         593 my $connect_info = $self->connect_info_with_opts;
119              
120 20 50       277 return unless $schema_class;
121              
122 20         103 $schema_class = $self->prepare_schema_class($schema_class);
123              
124 20         182 return $schema_class->connect($connect_info);
125             }
126              
127             sub _build_connect_info {
128 12     12   18 my ($self) = @_;
129 12 50       61 if(my $default = $self->can('get_default_connect_info') ) {
130 12         58 return $self->$default;
131             } else {
132 0         0 Test::More::fail("Can't build a default connect info");
133             }
134             }
135              
136             sub _build_connect_info_with_opts{
137 20     20   34 my ($self) = @_;
138 20 100       35 return { %{$self->connect_info}, %{$self->connect_opts || {}} };
  20         591  
  20         581  
139             }
140              
141             sub _build_fixture_command {
142 10     10   21 my $self = shift @_;
143 10         366 return $self->fixture_class->new(schema_manager=>$self);
144             }
145              
146             sub prepare_schema_class {
147 20     20 0 36 my ($self, $schema_class) = @_;
148 20         44 return $schema_class;
149             }
150              
151             sub initialize_schema {
152 20     20 0 38 my ($class, $config) = @_;
153              
154 20         60 my @traits = ();
155 20 50       101 if(defined $config->{traits}) {
156 0 0       0 @traits = ref $config->{traits} ? @{$config->{traits}} : ($config->{traits});
  0         0  
157             }
158              
159 20 100       79 if(my $connect_info = $config->{connect_info}) {
160 8         44 $connect_info = to_ConnectInfo($connect_info);
161 8         100 my ($driver) = $connect_info->{dsn} =~ /dbi:([^:]+):/i;
162 8 50       36 if(lc $driver eq "sqlite") {
163 8         20 push @traits, 'SQLite';
164             }
165             # Don't assume mysql means we want Testmysqld; we may
166             # want to connect to a real mysql server to test.
167             } else {
168 12 50       54 push @traits, 'SQLite'
169             unless @traits;
170             }
171 20         144 @traits = map { __PACKAGE__."::Trait::$_"} uniq @traits;
  20         89  
172 20         70 $config->{traits} = \@traits;
173              
174 20 50       135 my $self = Moose::Util::with_traits($class, @traits)->new($config)
175             or return;
176              
177 20         34290 $self->schema->storage->ensure_connected;
178 20 100       329992 if($config->{default_resultset_attributes})
179             {
180 1         39 $self->schema->default_resultset_attributes($config->{default_resultset_attributes});
181             }
182 20 100       834 $self->setup if $self->deploy_db;
183              
184 20         145 return $self;
185             }
186              
187             ## TODO we need to fix DBIC to allow debug levels and channels
188             sub _setup_debug {
189 0     0   0 my $self = shift @_;
190 0         0 my $cb = $self->schema->storage->debugcb;
191              
192 0         0 $self->schema->storage->debug(1);
193             $self->schema->storage->debugcb(sub {
194 0 0   0   0 $cb->(@_) if $cb;
195 0         0 $self->last_statement($_[1]);
196 0         0 });
197             }
198              
199             sub setup {
200 23     23 0 140 my $self = shift @_;
201 23 100       34 my $deploy_opts = {%{$self->deploy_opts}, $self->force_drop_table ? (add_drop_table => 1) : ()};
  23         695  
202 23 50       893 if(my $schema = $self->schema) {
203 23         34 eval {
204 23         233 $schema->deploy($deploy_opts);
205 23 50       38635902 };if($@) {
206 0         0 Test::More::fail("Error Deploying Schema: $@");
207             }
208 23         128 return $self;
209             }
210 0         0 return;
211             }
212              
213             sub cleanup {
214 15     15 0 314 my $self = shift @_;
215 15         498 my $schema = $self->schema;
216              
217 15 50       58 return unless $schema;
218 15 50       280 return unless $schema->storage;
219              
220 15 100       662 unless ($self->keep_db) {
221             $schema->storage->with_deferred_fk_checks(sub {
222 12     12   3666 foreach my $source_name (@{$self->deployed_sources()}) {
  12         70  
223 122         786 my $source = $schema->source($source_name);
224 122 50       4690 next unless $source;
225 122         290 $self->drop_source($source);
226             }
227 12         206 });
228             }
229              
230 15         568 $self->schema->storage->disconnect;
231             }
232              
233             sub deployed_sources
234             {
235 12     12 0 23 my ($self) = @_;
236              
237 12         337 my $deploy_opts = $self->deploy_opts;
238              
239 12 100       52 return $deploy_opts->{sources} if exists $deploy_opts->{sources};
240 11 100       38 return $deploy_opts->{parser_args}->{sources} if exists $deploy_opts->{parser_args}->{sources};
241              
242 10         247 return [ $self->schema->sources ];
243             }
244              
245             sub drop_source
246             {
247 122     122 0 112 my $self = shift;
248 122         128 my $source = shift;
249              
250 122         3280 my $schema = $self->schema;
251 122         353 my $source_name = $source->name;
252              
253 122         110 my $sql;
254 122 100       616 if ($source->isa('DBIx::Class::ResultSource::View')) {
255 20 100       110 $sql = $self->drop_view_sql($source_name) unless $source->is_virtual();
256             }
257             else {
258 102         189 $sql = $self->drop_table_sql($source_name);
259             }
260              
261 122 100       2416 $schema->storage->dbh->do($sql) if defined $sql;
262              
263 122         111674 return;
264             }
265              
266             # this has been pushed out to a method so that it can be overriden
267             # by the traits.
268             sub drop_table_sql
269             {
270 102     102 0 106 my $self = shift;
271 102         102 my $table = shift;
272 102         212 return "drop table $table";
273             }
274              
275             # this has been pushed out to a method so that it can be overriden
276             # by the traits.
277             sub drop_view_sql
278             {
279 10     10 0 20 my $self = shift;
280 10         14 my $view = shift;
281 10         29 return "drop view $view";
282             }
283              
284             sub reset {
285 4     4 0 8 my $self = shift @_;
286 4         22 $self->cleanup;
287 4         27 $self->setup;
288             }
289              
290             sub install_fixtures {
291 16     16 0 203395 my ($self, @args) = @_;
292 16         591 my $fixture_command = $self->fixture_command;
293 16 100 100     159 if(
      66        
      66        
294             (!ref($args[0]) && ($args[0]=~m/^::/))
295             or (ref $args[0] eq 'HASH' && $args[0]->{command}) ) {
296 3 50       11 my $arg = ref $args[0] ? $args[0]->{command} : $args[0];
297 3         15 my $fixture_class = to_FixtureClass($arg);
298 3 100       389 $self->builder->diag("Override default FixtureClass '".$self->fixture_class."' with $fixture_class") if $self->tdbic_debug;
299 3         326 $fixture_command = $fixture_class->new(schema_manager=>$self);
300 3         841 shift(@args);
301             }
302             return $self->schema->txn_do( sub {
303 16     16   9670 $fixture_command->install_fixtures(@args);
304 16         481 });
305             }
306              
307             sub DEMOLISH {
308 10     10 0 1245 my $self = shift @_;
309 10 50       38 if(defined $self) {
310 10         61 $self->cleanup;
311             }
312             }
313              
314             __PACKAGE__->meta->make_immutable;
315              
316             __END__
317              
318             =head1 NAME
319              
320             Test::DBIx::Class::SchemaManager - Manages a DBIx::Class::SchemaManager for Testing
321              
322             =head1 DESCRIPTION
323              
324             this class is a helper for L<Test::DBIx::Class>. Basically it is a type of
325             wrapper or adaptor for your schema so we can more easily and quickly deploy it
326             and cleanup it for the purposes of automated testing.
327              
328             You shouldn't need to use anything here. However, we do define %ENV variables
329             that you might be interested in using (although its probably best to define
330             inline configuration or use a configuration file).
331              
332             =over 4
333              
334             =item FORCE_DROP_TABLE
335              
336             Set to a true value will force dropping tables in the deploy phase. This will
337             generate warnings in a database (like sqlite) that can't detect if a table
338             exists before attempting to drop it. Safe for Mysql though.
339              
340             =item KEEP_DB
341              
342             Usually at the end of tests we cleanup your database and remove all the tables
343             created, etc. Sometimes you might want to preserve the database after testing
344             so that you can 'poke around'. Personally I think it's better to write tests
345             for the poking, but sometimes you just need a quick look.
346              
347             Please Note that KEEP_DB is not intended for use as a way to preserve deployed
348             databases across tests or test runs. I realize you may wish to try this as a
349             way to reduce testing time, since starting and deploying databases can be time
350             consuming, however it negates the goal we have to properly isolate the test
351             cases.
352              
353             If there is tremendous need for this type of feature, we may in the future try
354             to develop a good working system. For now I recomment using the C<reset_schema>
355             (see L<Test::DBIx::Class/reset_schema> for more) and subtests if you want to
356             try this for a given test run..
357              
358             =back
359              
360             =head1 SEE ALSO
361              
362             The following modules or resources may be of interest.
363              
364             L<DBIx::Class>, L<Test::DBIx::Class>
365              
366             =head1 AUTHOR
367              
368             John Napiorkowski C<< <jjnapiork@cpan.org> >>
369              
370             =head1 COPYRIGHT & LICENSE
371              
372             Copyright 2009, John Napiorkowski C<< <jjnapiork@cpan.org> >>
373              
374             This program is free software; you can redistribute it and/or modify
375             it under the same terms as Perl itself.
376              
377             =cut
378