зеркало из https://github.com/mozilla/gecko-dev.git
736 строки
18 KiB
Perl
736 строки
18 KiB
Perl
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public
|
|
# License Version 1.1 (the "License"); you may not use this file
|
|
# except in compliance with the License. You may obtain a copy of
|
|
# the License at http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS
|
|
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
# implied. See the License for the specific language governing
|
|
# rights and limitations under the License.
|
|
#
|
|
# The Original Code is the Bugzilla Test Runner System.
|
|
#
|
|
# The Initial Developer of the Original Code is Maciej Maczynski.
|
|
# Portions created by Maciej Maczynski are Copyright (C) 2001
|
|
# Maciej Maczynski. All Rights Reserved.
|
|
#
|
|
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
|
=head1 NAME
|
|
|
|
Bugzilla::Testopia::TestCaseRun - Testopia Test Case Run object
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This module represents a test case run in Testopia.
|
|
A test case run is a record in the test_case_runs table which joins
|
|
test cases to test runs. Basically, for each test run a selction of
|
|
test cases is made to be included in that run. As a test run
|
|
progresses, testers set statuses on each of the cases in the run.
|
|
If the build is changed on a case-run with a status, a clone of that
|
|
case-run is made in the table for historical purposes.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Bugzilla::Testopia::TestCaseRun;
|
|
|
|
$caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id);
|
|
$caserun = Bugzilla::Testopia::TestCaseRun->new(\%caserun_hash);
|
|
|
|
=cut
|
|
|
|
package Bugzilla::Testopia::TestCaseRun;
|
|
|
|
use strict;
|
|
|
|
use Bugzilla::Util;
|
|
use Bugzilla::Error;
|
|
use Bugzilla::User;
|
|
use Bugzilla::Config;
|
|
use Bugzilla::Testopia::Util;
|
|
use Bugzilla::Testopia::Constants;
|
|
|
|
###############################
|
|
#### Initialization ####
|
|
###############################
|
|
|
|
=head1 FIELDS
|
|
|
|
case_run_id
|
|
run_id
|
|
case_id
|
|
assignee
|
|
testedby
|
|
case_run_status_id
|
|
case_text_version
|
|
build_id
|
|
notes
|
|
close_date
|
|
iscurrent
|
|
sortkey
|
|
|
|
=cut
|
|
|
|
use constant DB_COLUMNS => qw(
|
|
test_case_runs.case_run_id
|
|
test_case_runs.run_id
|
|
test_case_runs.case_id
|
|
test_case_runs.assignee
|
|
test_case_runs.testedby
|
|
test_case_runs.case_run_status_id
|
|
test_case_runs.case_text_version
|
|
test_case_runs.build_id
|
|
test_case_runs.notes
|
|
test_case_runs.close_date
|
|
test_case_runs.iscurrent
|
|
test_case_runs.sortkey
|
|
);
|
|
|
|
our $columns = join(", ", DB_COLUMNS);
|
|
|
|
|
|
###############################
|
|
#### Methods ####
|
|
###############################
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 new
|
|
|
|
Instantiate a new case run. This takes a single argument
|
|
either a test case ID or a reference to a hash containing keys
|
|
identical to a test case-run's fields and desired values.
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my $invocant = shift;
|
|
my $class = ref($invocant) || $invocant;
|
|
my $self = {};
|
|
bless($self, $class);
|
|
return $self->_init(@_);
|
|
}
|
|
|
|
=head2 _init
|
|
|
|
Private constructor for this object
|
|
|
|
=cut
|
|
|
|
sub _init {
|
|
my $self = shift;
|
|
my ($param) = (@_);
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
my $id = $param unless (ref $param eq 'HASH');
|
|
my $obj;
|
|
|
|
if (defined $id && detaint_natural($id)) {
|
|
|
|
$obj = $dbh->selectrow_hashref(qq{
|
|
SELECT $columns FROM test_case_runs
|
|
WHERE case_run_id = ?}, undef, $id);
|
|
|
|
} elsif (ref $param eq 'HASH'){
|
|
$obj = $param;
|
|
|
|
} else {
|
|
Bugzilla::Error::ThrowCodeError('bad_arg',
|
|
{argument => 'param',
|
|
function => 'Testopia::TestCaseRun::_init'});
|
|
}
|
|
|
|
return undef unless (defined $obj);
|
|
|
|
foreach my $field (keys %$obj) {
|
|
$self->{$field} = $obj->{$field};
|
|
}
|
|
|
|
return $self;
|
|
}
|
|
|
|
=head2 store
|
|
|
|
Stores a test case run object in the database. This method is used
|
|
to store a newly created test case run. It returns the new id.
|
|
|
|
=cut
|
|
|
|
sub store {
|
|
my $self = shift;
|
|
my $dbh = Bugzilla->dbh;
|
|
$dbh->do("INSERT INTO test_case_runs ($columns)
|
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", undef,
|
|
(undef, $self->{'run_id'}, $self->{'case_id'}, $self->{'assignee'},
|
|
$self->{'testedby'}, IDLE, $self->{'case_text_version'},
|
|
$self->{'build_id'}, $self->{'notes'}, undef, 1, 0));
|
|
|
|
my $key = $dbh->bz_last_key( 'test_case_runs', 'case_run_id' );
|
|
return $key;
|
|
}
|
|
|
|
=head2 clone
|
|
|
|
Creates a copy of this caserun and sets it as the current record
|
|
|
|
=cut
|
|
|
|
sub clone {
|
|
my $self = shift;
|
|
my ($fields) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
$self->{'build_id'} = $fields->{'build_id'};
|
|
$self->{'case_run_status_id'} = IDLE;
|
|
$self->{'notes'} = '';
|
|
my $entry = $self->store;
|
|
$self->set_as_current($entry);
|
|
return $entry;
|
|
}
|
|
|
|
=head2 update
|
|
|
|
Update this case-run in the database. This method checks which
|
|
fields have been changed and either creates a clone of the case-run
|
|
or updates the existing one.
|
|
|
|
=cut
|
|
|
|
sub update {
|
|
my $self = shift;
|
|
my ($fields) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
if ($self->is_closed_status($fields->{'case_run_status_id'})){
|
|
$fields->{'close_date'} = Bugzilla::Testopia::Util::get_time_stamp();
|
|
}
|
|
if ($fields->{'build_id'} != $self->{'build_id'} && $self->{'case_run_status_id'} != IDLE){
|
|
return $self->clone($fields);
|
|
}
|
|
else {
|
|
return $self->_update_fields($fields);
|
|
}
|
|
|
|
}
|
|
|
|
=head2 _update_fields
|
|
|
|
Update this case-run in the database if a change is made to an
|
|
updatable field.
|
|
|
|
=cut
|
|
|
|
sub _update_fields{
|
|
my $self = shift;
|
|
my ($newvalues) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
$dbh->bz_lock_tables('test_case_runs WRITE');
|
|
foreach my $field (keys %{$newvalues}){
|
|
$dbh->do("UPDATE test_case_runs
|
|
SET $field = ? WHERE case_run_id = ?",
|
|
undef, $newvalues->{$field}, $self->{'case_run_id'});
|
|
}
|
|
$dbh->bz_unlock_tables();
|
|
|
|
return $self->{'case_run_id'};
|
|
}
|
|
|
|
=head2 set_as_current
|
|
|
|
Sets this case-run as the current or active one in the history
|
|
list of case-runs of this build and case_id
|
|
|
|
=cut
|
|
|
|
sub set_as_current {
|
|
my $self = shift;
|
|
my ($caserun) = @_;
|
|
$caserun = $self->{'case_run_id'} unless defined $caserun;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $list = $self->get_case_run_list;
|
|
|
|
$dbh->bz_lock_tables('test_case_runs WRITE');
|
|
foreach my $c (@{$list}){
|
|
$dbh->do("UPDATE test_case_runs
|
|
SET iscurrent = 0
|
|
WHERE case_run_id = ?",
|
|
undef, $c);
|
|
}
|
|
$dbh->do("UPDATE test_case_runs
|
|
SET iscurrent = 1
|
|
WHERE case_run_id = ?",
|
|
undef, $caserun);
|
|
$dbh->bz_unlock_tables();
|
|
}
|
|
|
|
=head2 set_build
|
|
|
|
Sets the build on a case-run
|
|
|
|
=cut
|
|
|
|
sub set_build {
|
|
my $self = shift;
|
|
my ($build_id) = @_;
|
|
$self->_update_fields({'build_id' => $build_id});
|
|
$self->{'build_id'} = $build_id;
|
|
}
|
|
|
|
=head2 set_status
|
|
|
|
Sets the status on a case-run and updates the close_date and testedby
|
|
if the status is a closed status.
|
|
|
|
=cut
|
|
|
|
sub set_status {
|
|
my $self = shift;
|
|
my ($status_id) = @_;
|
|
$self->_update_fields({'case_run_status_id' => $status_id});
|
|
if ($status_id == IDLE){
|
|
$self->_update_fields({'close_date' => undef});
|
|
$self->_update_fields({'testedby' => undef});
|
|
$self->{'close_date'} = undef;
|
|
$self->{'testedby'} = undef;
|
|
}
|
|
elsif ($status_id == RUNNING || $status_id == PAUSED){
|
|
$self->_update_fields({'close_date' => undef});
|
|
$self->{'close_date'} = undef;
|
|
}
|
|
else {
|
|
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
|
$self->_update_fields({'close_date' => $timestamp});
|
|
$self->_update_fields({'testedby' => Bugzilla->user->id});
|
|
$self->{'close_date'} = $timestamp;
|
|
$self->{'tested_by'} = Bugzilla->user;
|
|
}
|
|
|
|
$self->{'case_run_status_id'} = $status_id;
|
|
$self->{'status'} = undef;
|
|
}
|
|
|
|
=head2 set_assignee
|
|
|
|
Sets the assigned tester for the case-run
|
|
|
|
=cut
|
|
|
|
sub set_assignee {
|
|
my $self = shift;
|
|
my ($user_id) = @_;
|
|
$self->_update_fields({'assignee' => $user_id});
|
|
}
|
|
|
|
=head2 set_note
|
|
|
|
Updates the notes field for the case-run
|
|
|
|
=cut
|
|
|
|
sub set_note {
|
|
my $self = shift;
|
|
my ($note, $append) = @_;
|
|
if ($append){
|
|
my $note = $self->notes . "\n" . $note;
|
|
}
|
|
$self->_update_fields({'notes' => $note});
|
|
$self->{'notes'} = $note;
|
|
}
|
|
|
|
=head2 get_case_run_list
|
|
|
|
Returns a reference to a list of case-runs for the given case and run
|
|
|
|
=cut
|
|
|
|
sub get_case_run_list {
|
|
my $self = shift;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $ref = $dbh->selectcol_arrayref(
|
|
"SELECT case_run_id FROM test_case_runs
|
|
WHERE case_id = ? AND run_id = ?", undef,
|
|
($self->{'case_id'}, $self->{'run_id'}));
|
|
|
|
return $ref;
|
|
}
|
|
|
|
=head2 get_status_list
|
|
|
|
Returns a list reference of the legal statuses for a test case-run
|
|
|
|
=cut
|
|
|
|
sub get_status_list {
|
|
my $self = shift;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $ref = $dbh->selectall_arrayref(
|
|
"SELECT case_run_status_id AS id, name
|
|
FROM test_case_run_status
|
|
ORDER BY sortkey", {'Slice' =>{}});
|
|
|
|
return $ref
|
|
}
|
|
|
|
=head2 attach_bug
|
|
|
|
Attaches the specified bug to this test case-run
|
|
|
|
=cut
|
|
|
|
sub attach_bug {
|
|
my $self = shift;
|
|
my ($bug) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
$dbh->bz_lock_tables('test_case_bugs WRITE');
|
|
my ($is) = $dbh->selectrow_array(
|
|
"SELECT bug_id
|
|
FROM test_case_bugs
|
|
WHERE case_run_id=?
|
|
AND bug_id=?",
|
|
undef, ($self->{'case_run_id'}, $bug));
|
|
if ($is) {
|
|
$dbh->bz_unlock_tables();
|
|
return;
|
|
}
|
|
$dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id)
|
|
VALUES(?,?)", undef, ($bug, $self->{'case_run_id'}));
|
|
$dbh->bz_unlock_tables();
|
|
}
|
|
|
|
=head2 get_buglist
|
|
|
|
Returns a comma separated string off bug ids associated with
|
|
this case-run
|
|
|
|
=cut
|
|
|
|
sub get_buglist {
|
|
my $self = shift;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $bugids = $dbh->selectcol_arrayref("SELECT bug_id
|
|
FROM test_case_bugs
|
|
WHERE case_run_id=?",
|
|
undef, $self->{'case_run_id'});
|
|
return join(',', @{$bugids});
|
|
}
|
|
|
|
###############################
|
|
#### Accessors ####
|
|
###############################
|
|
|
|
=head1 ACCESSOR METHODS
|
|
|
|
=head2 id
|
|
|
|
Returns the ID of the object
|
|
|
|
=head2 testedby
|
|
|
|
Returns a Bugzilla::User object representing the user that closed
|
|
this case-run.
|
|
|
|
=head2 assignee
|
|
|
|
Returns a Bugzilla::User object representing the user assigned to
|
|
update this case-run.
|
|
|
|
=head2 case_text_version
|
|
|
|
Returns the version of the test case document that this case-run
|
|
was run against.
|
|
|
|
=head2 notes
|
|
|
|
Returns the notes of the object
|
|
|
|
=head2 close_date
|
|
|
|
Returns the time stamp of when this case-run was closed
|
|
|
|
=head2 iscurrent
|
|
|
|
Returns true if this is the current case-run in the history list
|
|
|
|
=head2 status_id
|
|
|
|
Returns the status id of the object
|
|
|
|
=head2 sortkey
|
|
|
|
Returns the sortkey of the object
|
|
|
|
=head2 isprivate
|
|
|
|
Returns the true if this case-run is private.
|
|
|
|
=cut
|
|
|
|
sub id { return $_[0]->{'case_run_id'}; }
|
|
sub case_id { return $_[0]->{'case_id'}; }
|
|
sub run_id { return $_[0]->{'run_id'}; }
|
|
sub testedby { return Bugzilla::User->new($_[0]->{'testedby'}); }
|
|
sub assignee { return Bugzilla::User->new($_[0]->{'assignee'}); }
|
|
sub case_text_version { return $_[0]->{'case_text_version'}; }
|
|
sub notes { return $_[0]->{'notes'}; }
|
|
sub close_date { return $_[0]->{'close_date'}; }
|
|
sub iscurrent { return $_[0]->{'iscurrent'}; }
|
|
sub status_id { return $_[0]->{'case_run_status_id'}; }
|
|
sub sortkey { return $_[0]->{'sortkey'}; }
|
|
sub isprivate { return $_[0]->{'isprivate'}; }
|
|
|
|
=head2 run
|
|
|
|
Returns the TestRun object that this case-run is associated with
|
|
|
|
=cut
|
|
|
|
# The potential exists for creating a circular reference here.
|
|
sub run {
|
|
my $self = shift;
|
|
return $self->{'run'} if exists $self->{'run'};
|
|
$self->{'run'} = Bugzilla::Testopia::TestRun->new($self->{'run_id'});
|
|
return $self->{'run'};
|
|
}
|
|
|
|
=head2 case
|
|
|
|
Returns the TestCase object that this case-run is associated with
|
|
|
|
=cut
|
|
|
|
# The potential exists for creating a circular reference here.
|
|
sub case {
|
|
my $self = shift;
|
|
return $self->{'case'} if exists $self->{'case'};
|
|
$self->{'case'} = Bugzilla::Testopia::TestCase->new($self->{'case_id'});
|
|
return $self->{'case'};
|
|
}
|
|
|
|
=head2 build
|
|
|
|
Returns the Build object that this case-run is associated with
|
|
|
|
=cut
|
|
|
|
sub build {
|
|
my $self = shift;
|
|
return $self->{'build'} if exists $self->{'build'};
|
|
$self->{'build'} = Bugzilla::Testopia::Build->new($self->{'build_id'});
|
|
return $self->{'build'};
|
|
}
|
|
|
|
=head2 run
|
|
|
|
Looks up the status name of the associated status_id for this object
|
|
|
|
=cut
|
|
|
|
sub status {
|
|
my $self = shift;
|
|
my $dbh = Bugzilla->dbh;
|
|
($self->{'status'}) = $dbh->selectrow_array(
|
|
"SELECT name FROM test_case_run_status
|
|
WHERE case_run_status_id=?", undef,
|
|
$self->{'case_run_status_id'});
|
|
return $self->{'status'};
|
|
}
|
|
|
|
=head2 bugs
|
|
|
|
Returns a list of Bugzilla::Bug objects associated with this case-run
|
|
|
|
=cut
|
|
|
|
sub bugs {
|
|
my $self = shift;
|
|
#return $self->{'bug'} if exists $self->{'bug'};
|
|
my $dbh = Bugzilla->dbh;
|
|
my @bugs;
|
|
my $bugids = $dbh->selectcol_arrayref("SELECT bug_id
|
|
FROM test_case_bugs
|
|
WHERE case_run_id=?",
|
|
undef, $self->{'case_run_id'});
|
|
#TODO: Check that user can see bug
|
|
foreach my $bugid (@{$bugids}){
|
|
push @bugs, Bugzilla::Bug->new($bugid, Bugzilla->user->id);
|
|
}
|
|
$self->{'bugs'} = \@bugs; #join(",", @$bugids);
|
|
|
|
return $self->{'bugs'};
|
|
}
|
|
|
|
=head2 bug_list
|
|
|
|
Returns a comma separated list of bug ids associated with this case-run
|
|
|
|
=cut
|
|
|
|
sub bug_list {
|
|
my $self = shift;
|
|
#return $self->{'bug'} if exists $self->{'bug'};
|
|
my $dbh = Bugzilla->dbh;
|
|
my @bugs;
|
|
my $bugids = $dbh->selectcol_arrayref("SELECT bug_id
|
|
FROM test_case_bugs
|
|
WHERE case_run_id=?",
|
|
undef, $self->{'case_run_id'});
|
|
#TODO: Check that user can see bug
|
|
foreach my $bugid (@{$bugids}){
|
|
push @bugs, Bugzilla::Bug->new($bugid, Bugzilla->user->id);
|
|
}
|
|
$self->{'bug_list'} = join(",", @$bugids);
|
|
|
|
return $self->{'bug_list'};
|
|
}
|
|
|
|
=head2 bug_count
|
|
|
|
Retuns a count of the bugs associated with this case-run
|
|
|
|
=cut
|
|
|
|
sub bug_count{
|
|
my $self = shift;
|
|
return $self->{'bug_count'} if exists $self->{'bug_count'};
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
$self->{'bug_count'} = $dbh->selectrow_array("SELECT COUNT(bug_id)
|
|
FROM test_case_bugs
|
|
WHERE case_run_id=?",
|
|
undef, $self->{'case_run_id'});
|
|
return $self->{'bug_count'};
|
|
}
|
|
|
|
=head2 is_open_status
|
|
|
|
Returns true if the status of this case-run is an open status
|
|
|
|
=cut
|
|
|
|
sub is_open_status {
|
|
my $self = shift;
|
|
my $status = shift;
|
|
my @open_status_list = (IDLE, RUNNING, PAUSED);
|
|
return 1 if lsearch(\@open_status_list, $status) > 0;
|
|
}
|
|
|
|
=head2 is_open_status
|
|
|
|
Returns true if the status of this case-run is a closed status
|
|
|
|
=cut
|
|
|
|
sub is_closed_status {
|
|
my $self = shift;
|
|
my $status = shift;
|
|
my @closed_status_list = (PASSED, FAILED, BLOCKED);
|
|
return 1 if lsearch(\@closed_status_list, $status) > 0;
|
|
}
|
|
|
|
=head2 canview
|
|
|
|
Returns true if the logged in user has rights to view this case-run.
|
|
|
|
=cut
|
|
|
|
sub canview {
|
|
|
|
my $self = shift;
|
|
# return $self->{'canview'} if exists $self->{'canview'};
|
|
# my ($case_log_id, $run_id, $plan_id, $current_user_id) = @_;
|
|
#
|
|
# my $dbh = Bugzilla->dbh;
|
|
# my $canview = 0;
|
|
# my $current_user_id = Bugzilla->user->id;
|
|
# my ($plan_id) = $dbh->selectrow_array("SELECT plan_id FROM test_runs
|
|
# WHERE run_id=?",
|
|
# undef, $self->{'test_run_id'});
|
|
#
|
|
# if (0 == &Bugzilla::Param('private-cases-log')) {
|
|
# $canview = 1;
|
|
# } else {
|
|
#
|
|
# if (0 == $self->{'isprivate'}) {
|
|
# # if !isprivate, then everybody can run it and should be able to see
|
|
# # the current status
|
|
# $canview = 1;
|
|
# } else {
|
|
# # check is the current user is a tester:
|
|
# if (defined $current_user_id) {
|
|
#
|
|
# SendSQL("select 1 from test_case_run_testers ".
|
|
# "where case_log_id=". $self->{'id'} ." and userid=$current_user_id");
|
|
#
|
|
# if (FetchOneColumn()) {
|
|
# # current user is a tester
|
|
# $canview = 1;
|
|
# } else {
|
|
# # check editors
|
|
# SendSQL("select 1 from test_plans where plan_id=$plan_id and editor=$current_user_id");
|
|
#
|
|
# if (FetchOneColumn()) {
|
|
# $canview = 1;
|
|
# } else {
|
|
# # check watchers
|
|
# SendSQL("select 1 from test_plans_watchers where plan_id=$plan_id and userid=$current_user_id");
|
|
# if (FetchOneColumn()) {
|
|
# $canview = 1;
|
|
# } else {
|
|
# #check test run manager
|
|
# SendSQL("select 1 from test_runs where test_run_id=". $self->{'test_run_id'} ." and manager=$current_user_id");
|
|
# $canview = FetchOneColumn()? 1 : 0;
|
|
#
|
|
# if (0 == $canview) {
|
|
# if (UserInGroup('admin')) {
|
|
# $canview = 1;
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
|
|
$self->{'canview'} = 1;
|
|
return $self->{'canview'};
|
|
}
|
|
|
|
=head2 canedit
|
|
|
|
Returns true if the logged in user has rights to edit this case-run.
|
|
|
|
=cut
|
|
|
|
sub canedit {
|
|
my $self = shift;
|
|
return $self->canview
|
|
&& (UserInGroup('managetestplans')
|
|
|| UserInGroup('edittestcases')
|
|
|| UserInGroup('runtests'));
|
|
}
|
|
|
|
=head2 canedit
|
|
|
|
Returns true if the logged in user has rights to delete this case-run.
|
|
|
|
=cut
|
|
|
|
sub candelete {
|
|
my $self = shift;
|
|
return $self->canedit && Param('allow-test-deletion');
|
|
}
|
|
|
|
=head1 SEE ALSO
|
|
|
|
TestCase TestRun
|
|
|
|
=head1 AUTHOR
|
|
|
|
Greg Hendricks <ghendricks@novell.com>
|
|
|
|
=cut
|
|
|
|
1;
|