|
|
@@ -92,6 +92,31 @@ source filenames). Idempotent — safe to call on every analysis run.
|
|
|
Given C<resources> (arrayref of changed filenames), return a list of test
|
|
|
filenames that should be run.
|
|
|
|
|
|
+=head2 start_build(%args)
|
|
|
+
|
|
|
+Queue tests for a build. Required: C<build> (build identifier, e.g. a git SHA),
|
|
|
+C<resources> (arrayref of changed filenames). Returns a list of test filenames
|
|
|
+queued for this build.
|
|
|
+
|
|
|
+=head2 pending_tests(%args)
|
|
|
+
|
|
|
+Return unclaimed test filenames for a build. Required: C<build>.
|
|
|
+
|
|
|
+=head2 claim_test(%args)
|
|
|
+
|
|
|
+Atomically claim a test for a worker. Required: C<build>, C<test>, C<worker>.
|
|
|
+Returns 1 if claimed, 0 if already claimed by another worker.
|
|
|
+
|
|
|
+=head2 complete_test(%args)
|
|
|
+
|
|
|
+Record a test result. Required: C<build>, C<test>, C<result> (integer; 0=pass,
|
|
|
+non-zero=fail convention recommended). Sets finish timestamp automatically.
|
|
|
+
|
|
|
+=head2 build_results(%args)
|
|
|
+
|
|
|
+Return all results for a build as an arrayref of hashrefs, each with keys
|
|
|
+C<test_file>, C<run_by>, C<result>, C<start>, C<finish>. Required: C<build>.
|
|
|
+
|
|
|
=cut
|
|
|
|
|
|
use Test::Mapper::DB;
|
|
|
@@ -163,6 +188,131 @@ sub tests_for_resources {
|
|
|
return @tests;
|
|
|
}
|
|
|
|
|
|
+sub start_build {
|
|
|
+ my ( $self, %args ) = @_;
|
|
|
+ die "build is required" unless $args{build};
|
|
|
+ die "resources is required" unless $args{resources};
|
|
|
+
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ my $build_id = $self->_ensure_build( $args{build} );
|
|
|
+ my $env_id = $self->{_env_id};
|
|
|
+
|
|
|
+ return () unless @{ $args{resources} };
|
|
|
+
|
|
|
+ my $placeholders = join( ",", ("?") x scalar @{ $args{resources} } );
|
|
|
+ $dbh->do(
|
|
|
+ "INSERT OR IGNORE INTO test_for_build (build_id, test_id)
|
|
|
+ SELECT ?, tr.test_id
|
|
|
+ FROM to_run AS tr
|
|
|
+ WHERE tr.environment = (SELECT environment FROM environment WHERE id = ?)
|
|
|
+ AND tr.source_file IN ($placeholders)",
|
|
|
+ undef, $build_id, $env_id, @{ $args{resources} }
|
|
|
+ ) or die "Could not queue tests for build: " . $dbh->errstr;
|
|
|
+
|
|
|
+ my $sth = $dbh->prepare(
|
|
|
+ "SELECT t.filename
|
|
|
+ FROM test AS t
|
|
|
+ JOIN test_for_build AS tfb ON tfb.test_id = t.id
|
|
|
+ WHERE tfb.build_id = ?"
|
|
|
+ ) or die "Could not prepare query: " . $dbh->errstr;
|
|
|
+ $sth->execute($build_id) or die $sth->errstr;
|
|
|
+
|
|
|
+ my @tests;
|
|
|
+ while ( my ($filename) = $sth->fetchrow_array ) {
|
|
|
+ push @tests, $filename;
|
|
|
+ }
|
|
|
+ return @tests;
|
|
|
+}
|
|
|
+
|
|
|
+sub pending_tests {
|
|
|
+ my ( $self, %args ) = @_;
|
|
|
+ die "build is required" unless $args{build};
|
|
|
+
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ my ($build_id) = $dbh->selectrow_array(
|
|
|
+ "SELECT id FROM build WHERE build=? AND product_id=?",
|
|
|
+ undef, $args{build}, $self->{_product_id}
|
|
|
+ );
|
|
|
+ return () unless $build_id;
|
|
|
+
|
|
|
+ my $sth = $dbh->prepare(
|
|
|
+ "SELECT t.filename
|
|
|
+ FROM test AS t
|
|
|
+ JOIN test_for_build AS tfb ON tfb.test_id = t.id
|
|
|
+ WHERE tfb.build_id = ? AND tfb.run_by IS NULL"
|
|
|
+ ) or die "Could not prepare query: " . $dbh->errstr;
|
|
|
+ $sth->execute($build_id) or die $sth->errstr;
|
|
|
+
|
|
|
+ my @tests;
|
|
|
+ while ( my ($filename) = $sth->fetchrow_array ) {
|
|
|
+ push @tests, $filename;
|
|
|
+ }
|
|
|
+ return @tests;
|
|
|
+}
|
|
|
+
|
|
|
+sub claim_test {
|
|
|
+ my ( $self, %args ) = @_;
|
|
|
+ die "build is required" unless $args{build};
|
|
|
+ die "test is required" unless $args{test};
|
|
|
+ die "worker is required" unless $args{worker};
|
|
|
+
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ my $build_id = $self->_build_id( $args{build} ) // return 0;
|
|
|
+ my $test_id = $self->_test_id( $args{test} ) // return 0;
|
|
|
+
|
|
|
+ my $rows = $dbh->do(
|
|
|
+ "UPDATE test_for_build SET run_by=?
|
|
|
+ WHERE build_id=? AND test_id=? AND run_by IS NULL",
|
|
|
+ undef, $args{worker}, $build_id, $test_id
|
|
|
+ );
|
|
|
+ return ( $rows && $rows > 0 ) ? 1 : 0;
|
|
|
+}
|
|
|
+
|
|
|
+sub complete_test {
|
|
|
+ my ( $self, %args ) = @_;
|
|
|
+ die "build is required" unless $args{build};
|
|
|
+ die "test is required" unless $args{test};
|
|
|
+ die "result is required" unless defined $args{result};
|
|
|
+
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ my $build_id = $self->_build_id( $args{build} ) // die "Unknown build: $args{build}";
|
|
|
+ my $test_id = $self->_test_id( $args{test} ) // die "Unknown test: $args{test}";
|
|
|
+
|
|
|
+ $dbh->do(
|
|
|
+ "UPDATE test_for_build SET result=?, finish=CURRENT_TIMESTAMP
|
|
|
+ WHERE build_id=? AND test_id=?",
|
|
|
+ undef, $args{result}, $build_id, $test_id
|
|
|
+ ) or die "Could not complete test: " . $dbh->errstr;
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+sub build_results {
|
|
|
+ my ( $self, %args ) = @_;
|
|
|
+ die "build is required" unless $args{build};
|
|
|
+
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ my $sth = $dbh->prepare(
|
|
|
+ "SELECT t.filename, tfb.run_by, tfb.result, tfb.start, tfb.finish
|
|
|
+ FROM test_for_build AS tfb
|
|
|
+ JOIN test AS t ON t.id = tfb.test_id
|
|
|
+ JOIN build AS b ON b.id = tfb.build_id
|
|
|
+ WHERE b.build=? AND b.product_id=?"
|
|
|
+ ) or die "Could not prepare query: " . $dbh->errstr;
|
|
|
+ $sth->execute( $args{build}, $self->{_product_id} ) or die $sth->errstr;
|
|
|
+
|
|
|
+ my @results;
|
|
|
+ while ( my $row = $sth->fetchrow_hashref ) {
|
|
|
+ push @results, {
|
|
|
+ test_file => $row->{filename},
|
|
|
+ run_by => $row->{run_by},
|
|
|
+ result => $row->{result},
|
|
|
+ start => $row->{start},
|
|
|
+ finish => $row->{finish},
|
|
|
+ };
|
|
|
+ }
|
|
|
+ return \@results;
|
|
|
+}
|
|
|
+
|
|
|
# ---- private helpers ----
|
|
|
|
|
|
sub _ensure_product {
|
|
|
@@ -209,6 +359,38 @@ sub _ensure_test {
|
|
|
return $id;
|
|
|
}
|
|
|
|
|
|
+sub _ensure_build {
|
|
|
+ my ( $self, $build ) = @_;
|
|
|
+ my $dbh = $self->{dbh};
|
|
|
+ $dbh->do(
|
|
|
+ "INSERT OR IGNORE INTO build (build, product_id) VALUES (?, ?)",
|
|
|
+ undef, $build, $self->{_product_id}
|
|
|
+ ) or die $dbh->errstr;
|
|
|
+ my ($id) = $dbh->selectrow_array(
|
|
|
+ "SELECT id FROM build WHERE build=? AND product_id=?",
|
|
|
+ undef, $build, $self->{_product_id}
|
|
|
+ );
|
|
|
+ return $id;
|
|
|
+}
|
|
|
+
|
|
|
+sub _build_id {
|
|
|
+ my ( $self, $build ) = @_;
|
|
|
+ my ($id) = $self->{dbh}->selectrow_array(
|
|
|
+ "SELECT id FROM build WHERE build=? AND product_id=?",
|
|
|
+ undef, $build, $self->{_product_id}
|
|
|
+ );
|
|
|
+ return $id;
|
|
|
+}
|
|
|
+
|
|
|
+sub _test_id {
|
|
|
+ my ( $self, $filename ) = @_;
|
|
|
+ my ($id) = $self->{dbh}->selectrow_array(
|
|
|
+ "SELECT id FROM test WHERE filename=? AND product_id=?",
|
|
|
+ undef, $filename, $self->{_product_id}
|
|
|
+ );
|
|
|
+ return $id;
|
|
|
+}
|
|
|
+
|
|
|
sub _ensure_resource {
|
|
|
my ( $self, $filename ) = @_;
|
|
|
my $dbh = $self->{dbh};
|