Permalink
Browse files

Allow for tests to run in parallel (simultaneously from multiple chec…

…kouts)

This is an interim solution and is by no means the final thing. It simply
was possible to do in a short timeframe and cuts the test run time in half.

If you have DSN envvars set, use at least -s -j8 for best results (the
shuffling un-bunches similar tests, see discussion below)

Two things are at play:

First of all every SQLite database and every temp work directory is created
separately using the pid of the *main* test process (there can be children)
for disambiguation. Extra cleanup passes have been added to ensure t/var
remains clean between runs.

All other DSNs are reduced to their ->sqlt_type form and the result is used
for a global lockfile. Said lockfile is kept in /tmp so that multiple
testruns from multiple directories can be run against the same set of
databases with no conflicts.

Some of the tests are explicitly exempt from any locking and will run
regardless of environment, for example t/storage/dbi_env.t

The lockfiles are deliberately placed in File::Spec->tmpdir. This is done
so that multiple dbic checkouts can run against the same set of DSNs without
stepping on each other's toes.

Some notes on why this is not a great idea, even though it works flawlessly
under continuous test cycling: The problem is that our tests are not yet
ordered in a spwecific way. This means that multiple tests competing for
the same resource will inevitably lock all available test threads forming
several bottlenecks along the path of execution. This issue will be adressed
in a later patch, with the following considerations:
  - prove -l t/... must continue to work as is
  - test aggregation is something the test suite should try to avoid in
    general - after all DBIC is intended to be usable in CGI (yes, pure CGI)
    environments, so if the tests are getting heavy to run - this is an
    actual problem in need of fixing. Aggregation will instead sweep it under
    the rug
  - general reorganization of test groups / various path changes should only
    be attempted once we have a solid base for multi-db test runs
  • Loading branch information...
1 parent d9bd519 commit 8d6b1478d8fa6f7c76e313ee72a72d5eb4c24d03 @ribasushi ribasushi committed Feb 3, 2012
View
@@ -65,6 +65,8 @@ Revision history for DBIx::Class
of storage capabilities
- Fixed carp_once only emitting one single warning per package
regardless of warning content
+ - Test suite now can be safely executed in parallel (prove -jN
+ or HARNESS_OPTIONS=jN)
0.08196 2011-11-29 05:35 (UTC)
* Fixes
View
@@ -119,27 +119,6 @@ if ($ENV{DBICTEST_SQLT_DEPLOY}) {
}
}
-# Bail out on parallel testing
-if (
- ($ENV{HARNESS_OPTIONS}||'') =~ / (?: ^ | \: ) j(\d+) /x
- and
- $1 > 1
-) { die <<EOP }
-
-******************************************************************************
-******************************************************************************
-*** ***
-*** PARALLEL TESTING DETECTED ( \$ENV{HARNESS_OPTIONS} = 'j$1' ) ***
-*** ***
-*** DBIC tests WILL FAIL. It is harder to make them parallel-friendly than ***
-*** it should be (though work is underway). In the meantime you will have ***
-*** to adjust your environment and re-run the installation. Sorry! ***
-*** ***
-******************************************************************************
-******************************************************************************
-
-EOP
-
# this is so we can order requires alphabetically
# copies are needed for potential author requires injection
my $reqs = {
View
@@ -3,6 +3,7 @@ use warnings;
use Test::More;
use lib qw(t/lib);
+use DBICTest;
use DBICTest::ForeignComponent;
# Tests if foreign component was loaded by calling foreign's method
View
@@ -2,9 +2,8 @@ use strict;
use warnings;
use Test::More;
-unshift(@INC, './t/lib');
-
-plan tests => 4;
+use lib 't/lib';
+use DBICTest;
my $warnings;
eval {
@@ -21,3 +20,4 @@ isa_ok($source_a, 'DBIx::Class::ResultSource::Table');
my $rset_a = DBICTest::Schema->resultset('Artist');
isa_ok($rset_a, 'DBIx::Class::ResultSet');
+done_testing;
@@ -3,6 +3,7 @@ use warnings;
use Test::More;
use lib qw(t/lib);
+use DBICTest;
plan tests => 4;
my $exp_warn = qr/The many-to-many relationship 'bars' is trying to create/;
View
@@ -4,6 +4,9 @@ use strict;
use Test::More;
use Test::Exception;
+use lib 't/lib';
+use DBICTest;
+
throws_ok (
sub {
package BuggyTable;
View
@@ -4,6 +4,7 @@ use Test::More;
use Test::Warn;
use lib qw(t/lib);
+use DBICTest;
warnings_exist { require DBICTest::ResultSetManager }
[
View
@@ -1,8 +1,13 @@
use strict;
use warnings;
use Test::More;
+
+use lib qw(t/lib);
+use DBICTest;
use DBIx::Class::Optional::Dependencies ();
+my $main_pid = $$;
+
plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('rdbms_pg')
unless DBIx::Class::Optional::Dependencies->req_ok_for ('rdbms_pg');
@@ -18,12 +23,6 @@ if($num_children !~ /^[0-9]+$/ || $num_children < 10) {
$num_children = 10;
}
-plan tests => ($num_children*2) + 6;
-
-use lib qw(t/lib);
-
-use_ok('DBICTest::Schema');
-
my $schema = DBICTest::Schema->connect($dsn, $user, $pass, { AutoCommit => 1 });
my $parent_rs;
@@ -117,4 +116,9 @@ while(@pids) {
ok(1, "Made it to the end");
-$schema->storage->dbh->do("DROP TABLE cd");
+done_testing;
+
+END {
+ $schema->storage->dbh->do("DROP TABLE cd") if ($schema and $main_pid == $$);
+ undef $schema;
+}
View
@@ -18,6 +18,7 @@ plan skip_all => 'DBIC does not actively support threads before perl 5.8.5'
use DBIx::Class::Optional::Dependencies ();
use lib qw(t/lib);
+use DBICTest;
plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('rdbms_pg')
unless DBIx::Class::Optional::Dependencies->req_ok_for ('rdbms_pg');
View
@@ -21,6 +21,7 @@ plan skip_all => 'DBIC does not actively support threads before perl 5.8.5'
use DBIx::Class::Optional::Dependencies ();
use Scalar::Util 'weaken';
use lib qw(t/lib);
+use DBICTest;
my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/};
plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test'
View
@@ -79,6 +79,10 @@ unless (DBICTest::RunMode->is_plain) {
}
);
+ # unicode is tricky, and now we happen to invoke it early via a
+ # regex in connection()
+ return $obj if (ref $obj) =~ /^utf8/;
+
# Test Builder is now making a new object for every pass/fail (que bloat?)
# and as such we can't really store any of its objects (since it will
# re-populate the registry while checking it, ewwww!)
View
@@ -20,6 +20,7 @@ BEGIN {
use Test::More;
use Test::Exception;
use lib qw(t/lib);
+use DBICTest;
throws_ok (
sub { $ENV{PATH} . (kill (0)) },
View
@@ -35,6 +35,9 @@ use warnings;
use Test::More;
+use lib 't/lib';
+use DBICTest;
+
use File::Find;
use File::Spec;
use B qw/svref_2object/;
View
@@ -5,6 +5,7 @@ use Test::Exception;
use Test::More;
use DBIx::Class::Optional::Dependencies ();
use lib qw(t/lib);
+use DBICTest::RunMode;
use DBIC::SqlMakerTest;
use DBIx::Class::SQLMaker::LimitDialects;
@@ -38,6 +39,7 @@ BEGIN {
);
}
+use DBICTest;
use DBICTest::Schema;
my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
View
@@ -10,7 +10,7 @@ use File::Copy;
use Time::HiRes qw/time sleep/;
use lib qw(t/lib);
-use DBICTest; # do not remove even though it is not used
+use DBICTest;
my ($dsn, $user, $pass);
@@ -30,13 +30,18 @@ BEGIN {
unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_mysql');
}
+# this is just to grab a lock
+{
+ my $s = DBICTest::Schema->connect($dsn, $user, $pass);
+}
+
use_ok('DBICVersion_v1');
my $version_table_name = 'dbix_class_schema_versions';
my $old_table_name = 'SchemaVersions';
-my $ddl_dir = dir ('t', 'var');
-mkdir ($ddl_dir) unless -d $ddl_dir;
+my $ddl_dir = dir(qw/t var/, "versioning_ddl-$$");
+$ddl_dir->mkpath unless -d $ddl_dir;
my $fn = {
v1 => $ddl_dir->file ('DBICVersion-Schema-1.0-MySQL.sql'),
@@ -271,8 +276,10 @@ system( qq($^X -pi.bak -e "s/ALTER/-- this is a comment\nALTER/" $fn->{trans_v23
ok($get_db_version_run == 0, "attributes pulled from list connect_info");
}
-unless ($ENV{DBICTEST_KEEP_VERSIONING_DDL}) {
- unlink $_ for (values %$fn);
+END {
+ unless ($ENV{DBICTEST_KEEP_VERSIONING_DDL}) {
+ $ddl_dir->rmtree;
+ }
}
done_testing;
View
@@ -3,6 +3,9 @@ use warnings;
use Test::More;
+use lib 't/lib';
+use DBICTest;
+
BEGIN {
require DBIx::Class;
plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for('admin')
View
@@ -21,22 +21,27 @@ BEGIN {
use_ok 'DBIx::Class::Admin';
-my $sql_dir = dir(qw/t var/);
-my @connect_info = DBICTest->_database(
- no_deploy=>1,
- no_populate=>1,
- sqlite_use_file => 1,
+# lock early
+DBICTest->init_schema(no_deploy => 1, no_populate => 1);
+
+my $db_fn = DBICTest->_sqlite_dbfilename;
+my @connect_info = (
+ "dbi:SQLite:$db_fn",
+ undef,
+ undef,
+ { on_connect_do => 'PRAGMA synchronous = OFF' },
);
+my $ddl_dir = dir(qw/t var/, "admin_ddl-$$");
{ # create the schema
# make sure we are clean
-clean_dir($sql_dir);
+clean_dir($ddl_dir);
my $admin = DBIx::Class::Admin->new(
schema_class=> "DBICTest::Schema",
- sql_dir=> $sql_dir,
+ sql_dir=> $ddl_dir,
connect_info => \@connect_info,
);
isa_ok ($admin, 'DBIx::Class::Admin', 'create the admin object');
@@ -50,12 +55,12 @@ lives_ok {
{ # upgrade schema
-clean_dir($sql_dir);
+clean_dir($ddl_dir);
require DBICVersion_v1;
my $admin = DBIx::Class::Admin->new(
schema_class => 'DBICVersion::Schema',
- sql_dir => $sql_dir,
+ sql_dir => $ddl_dir,
connect_info => \@connect_info,
);
@@ -71,11 +76,11 @@ is($schema->get_db_version, $DBICVersion::Schema::VERSION, 'Schema deployed and
require DBICVersion_v2;
-DBICVersion::Schema->upgrade_directory (undef); # so that we can test use of $sql_dir
+DBICVersion::Schema->upgrade_directory (undef); # so that we can test use of $ddl_dir
$admin = DBIx::Class::Admin->new(
schema_class => 'DBICVersion::Schema',
- sql_dir => $sql_dir,
+ sql_dir => $ddl_dir,
connect_info => \@connect_info
);
@@ -92,11 +97,11 @@ is($schema->get_db_version, $DBICVersion::Schema::VERSION, 'Schema and db versio
{ # install
-clean_dir($sql_dir);
+clean_dir($ddl_dir);
my $admin = DBIx::Class::Admin->new(
schema_class => 'DBICVersion::Schema',
- sql_dir => $sql_dir,
+ sql_dir => $ddl_dir,
_confirm => 1,
connect_info => \@connect_info,
);
@@ -111,20 +116,16 @@ warnings_exist ( sub {
lives_ok { $admin->install("4.0") } 'can force install to allready existing version'
}, qr/Forcing install may not be a good idea/, 'Force warning emitted' );
is($admin->schema->get_db_version, "4.0", 'db thinks its version 4.0');
-#clean_dir($sql_dir);
}
sub clean_dir {
my ($dir) = @_;
- $dir = $dir->resolve;
- if ( ! -d $dir ) {
- $dir->mkpath();
- }
- foreach my $file ($dir->children) {
- # skip any hidden files
- next if ($file =~ /^\./);
- unlink $file;
- }
+ $dir->rmtree if -d $dir;
+ unlink $db_fn;
+}
+
+END {
+ clean_dir($ddl_dir);
}
done_testing;
View
@@ -79,9 +79,10 @@ sub test_dbicadmin {
}
sub default_args {
+ my $dbname = DBICTest->_sqlite_dbfilename;
return (
qw|--quiet --schema=DBICTest::Schema --class=Employee|,
- q|--connect=["dbi:SQLite:dbname=t/var/DBIxClass.db","","",{"AutoCommit":1}]|,
+ qq|--connect=["dbi:SQLite:dbname=$dbname","","",{"AutoCommit":1}]|,
qw|--force -I testincludenoniterference|,
);
}
@@ -293,6 +293,3 @@ package main;
}
done_testing;
-
-END { unlink $DB if -e $DB }
-
@@ -48,10 +48,7 @@ use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/CDBICompat Core DB/);
-use File::Temp qw/tempfile/;
-my (undef, $DB) = tempfile();
-END { unlink $DB if -e $DB }
-
+my $DB = DBICTest->_sqlite_dbfilename;
my @DSN = ("dbi:SQLite:dbname=$DB", '', '', { AutoCommit => 1, RaiseError => 1 });
__PACKAGE__->connection(@DSN);
Oops, something went wrong.

0 comments on commit 8d6b147

Please sign in to comment.