Added a number of features and fixed several bugs. RC1.

git-svn-id: svn://10.0.0.236/trunk@202845 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ghendricks%novell.com
2006-07-18 17:38:13 +00:00
parent 890571e50d
commit 75cc9bd1b8
122 changed files with 3465 additions and 1322 deletions

View File

@@ -335,7 +335,7 @@ Returns true if the logged in user has rights to edit this attachment
sub canedit {
}
=head2 canview
=head2 candelete
Returns true if the logged in user has rights to delete this attachment

View File

@@ -24,7 +24,7 @@ Bugzilla::Testopia::Environment - A test environment
=head1 DESCRIPTION
Environments are a set of paramaters dictating what conditions
Environments are a set of parameters dictating what conditions
a test was conducted in. Each test run must have an environment.
Environments can be very simple or very complex. Since there is
no easy way of defining a limit for an environment, the bulk of

View File

@@ -20,6 +20,8 @@
package Bugzilla::Testopia::Product;
use strict;
# Extends Bugzilla::Product;
use base "Bugzilla::Product";

View File

@@ -37,7 +37,7 @@ Bugzilla::Testopia::Search - A module to support searches in Testopis
=head1 DESCRIPTION
Testopia::Search is based heavilly on Bugzilla::Search. It takes a
CGI instance and parses its paramaters to generate an SQL query that
CGI instance and parses its parameters to generate an SQL query that
can be used to get a result set from the database. The query is
usually passed to Table.pm to execute and display the results.
Search.pm supports searching for all major objects in the Testopia
@@ -218,15 +218,22 @@ sub init {
my @funcdefs =
(
"^category," => sub {
if ($obj eq 'case_run'){
push(@supptables,
"INNER JOIN test_cases " .
"ON test_case_runs.case_id = test_cases.case_id");
}
push(@supptables,
"INNER JOIN test_case_categories AS categories " .
"ON test_cases.category_id = categories.category_id");
$f = "categories.name";
},
"^build," => sub {
my $str = '';
$str = 'case_' if ($obj eq 'case_run');
push(@supptables,
"INNER JOIN test_builds AS builds " .
"ON test_runs.build_id = builds.build_id");
"ON test_". $str ."runs.build_id = builds.build_id");
$f = "builds.name";
},
"^(tcaction|tceffect)," => sub {
@@ -296,6 +303,27 @@ sub init {
"^run_plan_id," => sub {
$f = "test_runs.plan_id";
},
"^case_prod," => sub {
push(@supptables,
"INNER JOIN test_case_plans AS case_plans " .
"ON test_cases.case_id = case_plans.case_id");
push(@supptables,
"INNER JOIN test_plans " .
"ON case_plans.plan_id = test_plans.plan_id");
push(@supptables,
"INNER JOIN products " .
"ON test_plans.product_id = products.id");
$f = "test_plans.product_id";
},
"^run_prod," => sub {
push(@supptables,
"INNER JOIN test_plans " .
"ON test_runs.plan_id = test_plans.plan_id");
push(@supptables,
"INNER JOIN products " .
"ON test_plans.product_id = products.id");
$f = "test_plans.product_id";
},
"^(author|editor|manager|default_tester)," => sub {
push(@supptables,
"INNER JOIN profiles AS map_$1 " .
@@ -422,9 +450,24 @@ sub init {
}
push(@specialchart, ["bug", $type, join(',', $cgi->param('bug_id'))]);
}
if ($cgi->param("product_id")){
my $type = "anyexact";
if ($cgi->param('prodidtype') && $cgi->param('prodidtype') eq 'exclude') {
$type = "nowords";
}
if ($obj eq 'run'){
push(@specialchart, ["run_prod", $type, join(',', $cgi->param('product_id'))]);
}
elsif ($obj eq 'case'){
push(@specialchart, ["case_prod", $type, join(',', $cgi->param('product_id'))]);
}
else{
push(@specialchart, ["product_id", $type, join(',', $cgi->param('product_id'))]);
}
}
# Check the Multi select fields and add them to the chart
my @legal_fields = ("case_status_id", "category", "priority_id",
"component", "isautomated", "product_id",
"component", "isautomated", "case_run_status_id",
"default_product_version", "type_id",
"build", "environment_id", "milestone");
@@ -465,7 +508,7 @@ sub init {
push(@specialchart, ["tags", $t, $cgi->param('tags')]);
}
# Check for author
my @clist;
foreach my $profile ("author", "editor", "manager", "default_tester",
"assignee", "testedby"){
$t = $cgi->param($profile . "_type") || '';
@@ -479,9 +522,18 @@ sub init {
}
}
if ($cgi->param($profile)){
push(@specialchart, [$profile, $t, $cgi->param($profile)]);
if ($cgi->param('andor')){
push(@specialchart, [$profile, $t, trim($cgi->param($profile))]);
}
else{
push(@clist, $profile, $t, trim($cgi->param($profile)));
}
}
}
if (@clist) {
push(@specialchart, \@clist);
}
# check static text fields
foreach my $f ("summary", "tcaction", "tceffect", "script",
"requirement", "name", "plan_text", "environment",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/perl -wT
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
@@ -105,13 +104,9 @@ sub init {
$self->{'type'} = $type || ThrowCodeError('bad_arg',
{argument => 'type',
function => 'Testopia::Table::_init'});
$self->{'url_loc'} = $url || ThrowCodeError('bad_arg',
{argument => 'url',
function => 'Testopia::Table::_init'});
$self->{'cgi'} = $cgi || ThrowCodeError('bad_arg',
{argument => 'cgi',
function => 'Testopia::Table::_init'});
my $debug = $cgi->param('debug');
$self->{'url_loc'} = $url;
$self->{'cgi'} = $cgi;
my $debug = $cgi->param('debug') if $cgi;
my @list;
if ($query){
print "$query" if $debug;
@@ -120,11 +115,11 @@ sub init {
my $countquery = $query;
$countquery =~ s/ LIMIT.*$//;
print "<br> $countquery" if $debug;
my $count = $dbh->selectcol_arrayref($countquery);
$count = scalar @$count;
my $count_res = $dbh->selectcol_arrayref($countquery);
my $count = scalar @$count_res;
print "<br> total rows: $count" if $debug;
$self->{'list_count'} = $count;
my @ids;
my $list = $dbh->selectcol_arrayref($query);
foreach my $id (@$list){
my $o;
@@ -140,12 +135,16 @@ sub init {
elsif ($type eq 'case_run'){
$o = Bugzilla::Testopia::TestCaseRun->new($id);
}
push (@ids, $id);
push (@list, $o);
}
$self->{'list'} = \@list;
$self->{'id_list'} = join(",", @$count_res);
}
if ($cgi){
$self->{'viewall'} = $cgi->param('viewall');
$self->{'page'} = $cgi->param('page') || 0;
}
$self->{'viewall'} = $cgi->param('viewall');
$self->{'page'} = $cgi->param('page') || 0;
# elsif (!$query && !$list){
# my @list;
@@ -206,9 +205,9 @@ sub init {
# $self->{'reverse_sort'} = ($self->{'reverse_sort'} ? 1 : 0);
# }
#
# #### SAVE ####
# # Save the list of testcases for use in paginating and sorting
# $self->save_list;
#### SAVE ####
# Save the list of testcases for use in paginating and sorting
$self->save_list;
#
# #### SPLICE ####
# # If we are using a paged view of the data we split it up here
@@ -228,31 +227,32 @@ Used only in list context
=cut
sub save_list {
my $self = shift;
my @ids;
foreach my $i (@{$self->{'list'}}){
push @ids, $i->id;
}
my $list = join(",", @ids);
my $self = shift;
return if ($self->{'user'}->id == 0);
# my @ids;
# foreach my $i (@{$self->{'list'}}){
# push @ids, $i->id;
# }
# my $list = join(",", @ids);
my $dbh = Bugzilla->dbh;
$dbh->bz_lock_tables('test_named_queries WRITE');
my ($is) = $dbh->selectrow_array(
"SELECT 1 FROM test_named_queries
WHERE userid = ? AND name = ?", undef,
($self->{'user'}->id, "__". $self->{'type'} ."__"));
if ($is) {
$dbh->do("UPDATE test_named_queries SET query = ?
WHERE userid = ? AND name = ?", undef,
($list, $self->{'user'}->id, "__". $self->{'type'} ."__"));
if ($self->{'id_list'}){
$dbh->bz_lock_tables('test_named_queries WRITE');
my ($is) = $dbh->selectrow_array(
"SELECT 1 FROM test_named_queries
WHERE userid = ? AND name = ?", undef,
($self->{'user'}->id, "__". $self->{'type'} ."__"));
if ($is) {
$dbh->do("UPDATE test_named_queries SET query = ?
WHERE userid = ? AND name = ?", undef,
(join(",", $self->{'id_list'}), $self->{'user'}->id, "__". $self->{'type'} ."__"));
}
else {
$dbh->do("INSERT INTO test_named_queries VALUES(?,?,?,?)", undef,
($self->{'user'}->id, "__". $self->{'type'} ."__", 0, join(",", $self->{'id_list'})));
}
$dbh->bz_unlock_tables();
}
else {
$dbh->do("INSERT INTO test_named_queries VALUES(?,?,?,?)", undef,
($self->{'user'}->id, "__". $self->{'type'} ."__", 0, $list));
}
$dbh->bz_unlock_tables();
# $self->{'list_count'} = scalar @ids unless $self->{'query'};
}
@@ -264,13 +264,16 @@ Used only in list context
=cut
sub get_saved_list {
my $self = shift;
my $self = shift;
return undef if ($self->{'user'}->id == 0);
my $type = shift || $self->{'type'};
my $dbh = Bugzilla->dbh;
my ($list) = $dbh->selectrow_array(
"SELECT query FROM test_named_queries
WHERE userid = ? AND name = ?", undef,
($self->{'user'}->id, "__". $self->{'type'} ."__"));
return $list;
($self->{'user'}->id, "__". $type ."__"));
my @list = split(',', $list);
return \@list;
}
# used by the sort function to check which field to sort on
@@ -305,28 +308,37 @@ sub sort_fields {
}
sub sort_list {
my $self = shift;
$field = shift;
$reverse = $self->{'reverse_sort'};
my @list = sort sort_fields @{$self->{'list'}};
$self->{'list'} = \@list;
return $self->{'list'};
my $self = shift;
$field = shift;
$reverse = $self->{'reverse_sort'};
my @list = sort sort_fields @{$self->{'list'}};
$self->{'list'} = \@list;
return $self->{'list'};
}
sub get_page {
my $self = shift;
if ($self->{'viewall'}){
return $self->{'list'};
}
my $pagenum = shift || $self->{'page'};
$self->{'page'} = $pagenum;
my $offset = $pagenum * $self->page_size;
my @list = @{$self->{'list'}};
my $self = shift;
if ($self->{'viewall'}){
return $self->{'list'};
}
my $pagenum = shift || $self->{'page'};
$self->{'page'} = $pagenum;
my $offset = $pagenum * $self->page_size;
my @list = @{$self->{'list'}};
@list = splice(@list, $offset, $self->page_size);
$self->{'list'} = \@list;
return $self->{'list'};
}
sub get_next{
my $self = shift;
my ($curr) = @_;
my $list = $self->get_list;
my $ref = lsearch($curr, $list);
return undef if $ref == -1;
return $list->[$ref];
}
###############################
#### Accessors ####
###############################
@@ -341,11 +353,11 @@ Returns an ineger representing how many items should appear on a page
=cut
sub page_size {
sub page_size {
my $self = shift;
my $cgi = $self->{'cgi'};
return $cgi->param('pagesize') if $cgi->param('pagesize');
return 25; #TODO: make this a user setting
return 25; #TODO: make this a user setting
}
=head2 get_order_url
@@ -376,19 +388,19 @@ sub get_url {
my $self = shift;
my $regxp = shift;
my $cgi = $self->{'cgi'};
my @keys = $cgi->param;
my $qstring;
foreach my $key (@keys){
if ($key =~ /$regxp/){
my @keys = $cgi->param;
my $qstring;
foreach my $key (@keys){
if ($key =~ /$regxp/){
next;
}
my @vals = $cgi->param($key);
}
my @vals = $cgi->param($key);
foreach my $val (@vals){
$qstring .= $key ."=". $val ."&";
}
}
chop $qstring;
$qstring = $self->{'url_loc'} ."?". $qstring;
}
chop $qstring;
$qstring = $self->{'url_loc'} ."?". $qstring;
$self->{'url'} = $qstring;
return $self->{'url'};
}
@@ -406,7 +418,7 @@ sub page_count {
use integer;
return ($self->list_count / $self->page_size) + 1;
}
return $self->list_count/$self->page_size;
return $self->list_count/$self->page_size;
}
sub reverse_sort {

View File

@@ -191,11 +191,14 @@ associated with this case.
#TODO: This can be done with a single query
sub get_selectable_components {
my $self = shift;
my ($byid) = @_;
my $dbh = Bugzilla->dbh;
my @compids;
my @exclusions;
foreach my $e (@{$self->components}){
push @exclusions, $e->{'id'};
unless ($byid) {
foreach my $e (@{$self->components}){
push @exclusions, $e->{'id'};
}
}
my $query = "SELECT id FROM components
WHERE product_id = ?";
@@ -214,7 +217,7 @@ sub get_selectable_components {
@compids = keys %compseen;
#TODO: 2.22 use Bugzilla::Component
my @comps;
push @comps, {'id' => '0', 'name' => '--Please Select--'};
push @comps, {'id' => '0', 'name' => '--Please Select--'} unless $byid;
foreach my $id (@compids){
my $ref = $dbh->selectrow_hashref(
"SELECT id, name FROM components
@@ -946,6 +949,36 @@ sub update_deps {
}
}
=head2 get_dep_tree
Returns a list of test case dependencies
=cut
sub get_dep_tree {
my $self = shift;
$self->{'dep_list'} = ();
$self->_generate_dep_tree($self->{'case_id'});
return $self->{'dep_list'};
}
=head2 _generate_dep_tree
Private method that recursivly gets a list of the test cases this blocks
=cut
sub _generate_dep_tree {
my $self = shift;
my ($case_id) = @_;
my $deps = _get_dep_lists("dependson", "blocked", $case_id);
return unless scalar @$deps;
foreach my $id (@$deps){
#print "ID $id";
$self->_generate_dep_tree($id);
push @{$self->{'dep_list'}}, $id
}
}
=head2 canedit
Returns true if the logged in user has rights to edit this test case.
@@ -1230,7 +1263,7 @@ sub plans {
return $self->{'plans'};
}
=head2 plans
=head2 bugs
Returns a reference to a list of Bugzilla::Bug objects
associated with this case.
@@ -1241,12 +1274,12 @@ sub bugs {
my $self = shift;
my $dbh = Bugzilla->dbh;
return $self->{'bugs'} if exists $self->{'bugs'};
my $ref = $dbh->selectcol_arrayref("SELECT bug_id
FROM test_case_bugs b
JOIN test_case_runs r
ON r.case_run_id = b.case_run_id
WHERE case_id = ?",
undef, $self->{'case_id'});
my $ref = $dbh->selectcol_arrayref(
"SELECT DISTINCT bug_id
FROM test_case_bugs b
JOIN test_case_runs r ON r.case_run_id = b.case_run_id
WHERE case_id = ?",
undef, $self->{'case_id'});
my @bugs;
foreach my $id (@{$ref}){
push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id);

View File

@@ -163,7 +163,7 @@ sub store {
$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'},
undef, 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' );
@@ -181,13 +181,37 @@ sub clone {
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 check_exists
Checks for an existing entry with the same build for this case and run
and returns the id if it is found
=cut
sub check_exists {
my $self = shift;
my ($run_id, $case_id, $build_id) = @_;
$run_id ||= $self->{'run_id'};
$case_id ||= $self->{'case_id'};
$build_id ||= $self->{'build_id'};
my $dbh = Bugzilla->dbh;
my ($is) = $dbh->selectrow_array(
"SELECT case_run_id
FROM test_case_runs
WHERE run_id = ? AND case_id = ? AND build_id = ?",
undef, ($run_id, $case_id, $build_id));
return $is;
}
=head2 update
Update this case-run in the database. This method checks which
@@ -203,13 +227,17 @@ sub update {
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);
my ($is) = $self->check_exists($self->run_id, $self->case_id, $fields->{'build_id'});
if ($fields->{'build_id'} != $self->{'build_id'}){
if ($is){
return $is;
}
if ($self->{'case_run_status_id'} != IDLE){
return $self->clone($fields);
}
}
else {
return $self->_update_fields($fields);
}
return $self->_update_fields($fields);
}
=head2 _update_fields
@@ -224,6 +252,13 @@ sub _update_fields{
my ($newvalues) = @_;
my $dbh = Bugzilla->dbh;
if ($newvalues->{'case_run_status_id'} && $newvalues->{'case_run_status_id'} == FAILED){
$self->_update_deps(BLOCKED);
}
elsif ($newvalues->{'case_run_status_id'} && $newvalues->{'case_run_status_id'} == PASSED){
$self->_update_deps(IDLE);
}
$dbh->bz_lock_tables('test_case_runs WRITE');
foreach my $field (keys %{$newvalues}){
$dbh->do("UPDATE test_case_runs
@@ -302,9 +337,9 @@ sub set_status {
$self->_update_fields({'close_date' => $timestamp});
$self->_update_fields({'testedby' => Bugzilla->user->id});
$self->{'close_date'} = $timestamp;
$self->{'tested_by'} = Bugzilla->user;
$self->{'testedby'} = Bugzilla->user->id;
}
$self->{'case_run_status_id'} = $status_id;
$self->{'status'} = undef;
}
@@ -337,6 +372,43 @@ sub set_note {
$self->{'notes'} = $note;
}
=head2 _update_deps
Private method for updating blocked test cases. If the pre-requisite
case fails, the blocked test cases in a run get a status of BLOCKED
if it passes they are set back to IDLE. This only happens to the
current case run and only if it doesn't already have a closed status.
=cut
sub _update_deps {
my $self = shift;
my ($status) = @_;
my $deplist = $self->case->get_dep_tree;
return unless $deplist;
my $dbh = Bugzilla->dbh;
$dbh->bz_lock_tables("test_case_runs WRITE");
my $caseruns = $dbh->selectcol_arrayref(
"SELECT case_run_id
FROM test_case_runs
WHERE iscurrent = 1
AND run_id = ?
AND case_run_status_id IN(". join(',', (IDLE,RUNNING,PAUSED,BLOCKED)) .")
AND case_id IN (". join(',', @$deplist) .")",
undef, $self->{'run_id'});
my $sth = $dbh->prepare_cached(
"UPDATE test_case_runs
SET case_run_status_id = ?
WHERE case_run_id = ?");
foreach my $id (@$caseruns){
$sth->execute($status, $id);
}
$dbh->bz_unlock_tables;
$self->{'updated_deps'} = $caseruns;
}
=head2 get_case_run_list
Returns a reference to a list of case-runs for the given case and run
@@ -379,7 +451,8 @@ Attaches the specified bug to this test case-run
sub attach_bug {
my $self = shift;
my ($bug) = @_;
my ($bug, $caserun_id) = @_;
$caserun_id ||= $self->{'case_run_id'};
my $dbh = Bugzilla->dbh;
$dbh->bz_lock_tables('test_case_bugs WRITE');
@@ -388,7 +461,7 @@ sub attach_bug {
FROM test_case_bugs
WHERE case_run_id=?
AND bug_id=?",
undef, ($self->{'case_run_id'}, $bug));
undef, ($caserun_id, $bug));
if ($is) {
$dbh->bz_unlock_tables();
return;
@@ -398,6 +471,24 @@ sub attach_bug {
$dbh->bz_unlock_tables();
}
=head2 detach_bug
Removes the association of the specified bug from this test case-run
=cut
sub detach_bug {
my $self = shift;
my ($bug) = @_;
my $dbh = Bugzilla->dbh;
$dbh->do("DELETE FROM test_case_bugs
WHERE bug_id = ?
AND case_run_id = ?",
undef, ($bug, $self->{'case_run_id'}));
}
=head2 get_buglist
Returns a comma separated string off bug ids associated with
@@ -466,6 +557,12 @@ Returns the true if this case-run is private.
=cut
=head2 updated_deps
Returns a reference to a list of dependent caseruns that were updated
=cut
sub id { return $_[0]->{'case_run_id'}; }
sub case_id { return $_[0]->{'case_id'}; }
sub run_id { return $_[0]->{'run_id'}; }
@@ -478,6 +575,7 @@ sub iscurrent { return $_[0]->{'iscurrent'}; }
sub status_id { return $_[0]->{'case_run_status_id'}; }
sub sortkey { return $_[0]->{'sortkey'}; }
sub isprivate { return $_[0]->{'isprivate'}; }
sub updated_deps { return $_[0]->{'updated_deps'}; }
=head2 run
@@ -520,7 +618,7 @@ sub build {
return $self->{'build'};
}
=head2 run
=head2 status
Looks up the status name of the associated status_id for this object
@@ -612,10 +710,10 @@ 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;
return 1 if lsearch(\@open_status_list, $status) > -1;
}
=head2 is_open_status
=head2 is_closed_status
Returns true if the status of this case-run is a closed status
@@ -625,7 +723,7 @@ 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;
return 1 if lsearch(\@closed_status_list, $status) > -1;
}
=head2 canview
@@ -705,13 +803,13 @@ Returns true if the logged in user has rights to edit this case-run.
sub canedit {
my $self = shift;
return $self->canview
return !$self->run->stop_date && $self->canview
&& (UserInGroup('managetestplans')
|| UserInGroup('edittestcases')
|| UserInGroup('runtests'));
}
=head2 canedit
=head2 candelete
Returns true if the logged in user has rights to delete this case-run.

View File

@@ -326,17 +326,19 @@ specified, use the plan product
sub get_product_versions {
my $self = shift;
my ($product) = @_;
detaint_natural($product);
$product = $self->{'product_id'} unless ($product);
$product = $self->{'product_id'} unless $product;
$product ||= 0;
my $dbh = Bugzilla->dbh;
#TODO: 2.22 use product->versions
# Can't use placeholders as this could be a single id (integer)
# or a comma separated list (string)
my $versions = $dbh->selectall_arrayref(
"SELECT value AS id, value AS name
"SELECT DISTINCT value AS id, value AS name
FROM versions
WHERE product_id = ?
WHERE product_id IN($product)
ORDER BY name",
{'Slice'=>{}}, $product);
{'Slice'=>{}});
return $versions;
}
@@ -354,11 +356,80 @@ sub get_product_milestones {
$product_id ||= $self->{'product_id'};
my $dbh = Bugzilla->dbh;
my $ref = $dbh->selectall_arrayref(
"SELECT value AS id, value AS name
"SELECT DISTINCT value AS id, value AS name
FROM milestones
WHERE product_id = ?
WHERE product_id IN($product_id)
ORDER BY sortkey",
{'Slice'=>{}}, $product_id);
{'Slice'=>{}});
return $ref;
}
=head2 get_product_builds
Returns al list of builds for the given product. If one
is not specified, use the plan product.
=cut
sub get_product_builds {
#TODO: 2.22 use product.pm
my $self = shift;
my ($product_id) = @_;
$product_id ||= $self->{'product_id'};
my $dbh = Bugzilla->dbh;
my $ref = $dbh->selectall_arrayref(
"SELECT DISTINCT name AS id, name
FROM test_builds
WHERE product_id IN($product_id)
ORDER BY name",
{'Slice'=>{}});
return $ref;
}
=head2 get_product_categories
Returns a list of product categories for the given product. If one
is not specified, use the plan product.
=cut
sub get_product_categories {
#TODO: 2.22 use product.pm
my $self = shift;
my ($product_id) = @_;
$product_id ||= $self->{'product_id'};
my $dbh = Bugzilla->dbh;
my $ref = $dbh->selectall_arrayref(
"SELECT DISTINCT name AS id, name
FROM test_case_categories
WHERE product_id IN($product_id)
ORDER BY name",
{'Slice'=>{}});
return $ref;
}
=head2 get_product_components
Returns a list of product components for the given product. If one
is not specified, use the plan product.
=cut
sub get_product_components {
#TODO: 2.22 use product.pm
my $self = shift;
my ($product_id) = @_;
$product_id ||= $self->{'product_id'};
my $dbh = Bugzilla->dbh;
my $ref = $dbh->selectall_arrayref(
"SELECT DISTINCT name AS id, name
FROM components
WHERE product_id IN($product_id)
ORDER BY name",
{'Slice'=>{}});
return $ref;
}

View File

@@ -184,7 +184,7 @@ sub validate_test_id {
$res
|| ThrowUserError("invalid-test-id-non-existent",
{'id' => $id, 'type' => $type});
{'id' => $alias, 'type' => $type});
return $res;
}