Bug 40933
Bug 16361 "Big Bad Sinbad" (strike) Commit: Testopia 2.2 BETA1 for Bugzilla 3.2 git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@110 6955db30-a419-402b-8a0d-67ecbb4d7f56custis
parent
038a3ed7ec
commit
5c161773e0
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>testopia-1.0-2.22</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.epic.perleditor.perlbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.epic.perleditor.perlnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -2750,6 +2750,14 @@ sub bug_alias_to_id {
|
|||
"SELECT bug_id FROM bugs WHERE alias = ?", undef, $alias);
|
||||
}
|
||||
|
||||
sub get_test_case_count {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $row_count = $dbh->selectall_arrayref(
|
||||
"SELECT DISTINCT case_id FROM test_case_bugs WHERE bug_id = ?",
|
||||
undef, $self->bug_id);
|
||||
return scalar @$row_count;
|
||||
}
|
||||
#####################################################################
|
||||
# Subroutines
|
||||
#####################################################################
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
# -*- 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): Maciej Maczynski <macmac@xdsnet.pl>
|
||||
# Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Config::Testopia;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config::Common;
|
||||
|
||||
$Bugzilla::Config::Admin::sortkey = "20";
|
||||
|
||||
sub get_param_list {
|
||||
my $class = shift;
|
||||
my @param_list = (
|
||||
# {
|
||||
# name => 'private-cases-log',
|
||||
# type => 'b',
|
||||
# default => 0,
|
||||
# },
|
||||
|
||||
{
|
||||
name => 'allow-test-deletion',
|
||||
type => 'b',
|
||||
default => 0,
|
||||
},
|
||||
|
||||
{
|
||||
name => 'testopia-allow-group-member-deletes',
|
||||
type => 'b',
|
||||
default => 0,
|
||||
},
|
||||
|
||||
{
|
||||
name => 'testopia-default-plan-testers-regexp',
|
||||
type => 't',
|
||||
},
|
||||
|
||||
# {
|
||||
# name => 'print-tag-in-case-log',
|
||||
# type => 'b',
|
||||
# default => 0,
|
||||
# },
|
||||
|
||||
{
|
||||
name => 'new-case-action-template',
|
||||
type => 'l',
|
||||
default => qq{<ol>
|
||||
<li></li>
|
||||
</ol>},
|
||||
},
|
||||
|
||||
{
|
||||
name => 'new-case-results-template',
|
||||
type => 'l',
|
||||
default => qq{<ol>
|
||||
<li></li>
|
||||
</ol>},
|
||||
},
|
||||
|
||||
{
|
||||
name => 'bug-to-test-case-summary',
|
||||
type => 'l',
|
||||
default => 'Test for bug %id% - %summary%',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'bug-to-test-case-action',
|
||||
type => 'l',
|
||||
default => 'Verify that bug %id% is fixed: %description%'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'bug-to-test-case-results',
|
||||
type => 'l',
|
||||
default => '',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'default-test-case-status',
|
||||
type => 's',
|
||||
choices => ['PROPOSED', 'CONFIRMED', 'DISABLED'],
|
||||
default => 'PROPOSED'
|
||||
},
|
||||
|
||||
{
|
||||
name => 'testopia-max-allowed-plan-testers',
|
||||
type => 't',
|
||||
default => '500',
|
||||
},
|
||||
|
||||
{
|
||||
name => 'testopia-debug',
|
||||
type => 's',
|
||||
choices => ['ON', 'OFF', 'Developer'],
|
||||
default => 'OFF'
|
||||
},
|
||||
# {
|
||||
# name => 'new-testrun-email-notif',
|
||||
# type => 'l',
|
||||
# default => 'From: bugzilla-daemon'."\n".
|
||||
# 'To: %to%'."\n".
|
||||
# 'Subject: Test run started.'."\n".
|
||||
# "\n".
|
||||
# 'Test run \'%summary%\' for product \'%product%\' and test plan \'%plan%\' has '.
|
||||
# 'just been started.'
|
||||
# },
|
||||
|
||||
# {
|
||||
# name => 'case-failed-email-notif',
|
||||
# type => 'l',
|
||||
# default => 'From: bugzilla-daemon'."\n".
|
||||
# 'To: %manager%'."\n".
|
||||
# 'Subject: Case log \'%id%\' marked as failed.'."\n".
|
||||
# "\n".
|
||||
# 'Test case log \'%id%\' in test run \'%test_run%\' was marked as \'failed\' by %tester%.'
|
||||
# },
|
||||
|
||||
# {
|
||||
# name => 'tester-completed-email-notif',
|
||||
# type => 'l',
|
||||
# default => 'From: bugzilla-daemon'."\n".
|
||||
# 'To: %manager%'."\n".
|
||||
# 'Subject: Test run completed for tester.'."\n".
|
||||
# "\n".
|
||||
# 'Tester %tester% has completed the test run \'%test_run%\'.'
|
||||
# },
|
||||
|
||||
# {
|
||||
# name => 'test-run-completed-email-notif',
|
||||
# type => 'l',
|
||||
# default => 'From: bugzilla-daemon'."\n".
|
||||
# 'To: %manager%'."\n".
|
||||
# 'Subject: Test run completed.'."\n".
|
||||
# "\n".
|
||||
# 'Test run \'%test_run%\' completed.'
|
||||
# }
|
||||
);
|
||||
}
|
||||
1;
|
|
@ -131,6 +131,7 @@ use File::Basename;
|
|||
ERROR_MODE_WEBPAGE
|
||||
ERROR_MODE_DIE
|
||||
ERROR_MODE_DIE_SOAP_FAULT
|
||||
ERROR_MODE_AJAX
|
||||
|
||||
INSTALLATION_MODE_INTERACTIVE
|
||||
INSTALLATION_MODE_NON_INTERACTIVE
|
||||
|
@ -370,6 +371,7 @@ use constant USAGE_MODE_EMAIL => 3;
|
|||
use constant ERROR_MODE_WEBPAGE => 0;
|
||||
use constant ERROR_MODE_DIE => 1;
|
||||
use constant ERROR_MODE_DIE_SOAP_FAULT => 2;
|
||||
use constant ERROR_MODE_AJAX => 3;
|
||||
|
||||
# The various modes that checksetup.pl can run in.
|
||||
use constant INSTALLATION_MODE_INTERACTIVE => 0;
|
||||
|
|
|
@ -690,6 +690,9 @@ EOT
|
|||
$self->bz_drop_index('bugs_fulltext', $index);
|
||||
}
|
||||
}
|
||||
if ($table eq 'test_runs' && $name eq 'summary') {
|
||||
$self->bz_drop_index('test_runs', 'test_runs_summary_idx');
|
||||
}
|
||||
|
||||
print "Converting $table.$name to be stored as UTF-8...\n";
|
||||
my $col_info =
|
||||
|
|
|
@ -32,6 +32,7 @@ use Bugzilla::Constants;
|
|||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Date::Format;
|
||||
use JSON;
|
||||
|
||||
# We cannot use $^S to detect if we are in an eval(), because mod_perl
|
||||
# already eval'uates everything, so $^S = 1 in all cases under mod_perl!
|
||||
|
@ -114,6 +115,16 @@ sub _throw_error {
|
|||
}
|
||||
die SOAP::Fault->faultcode($code)->faultstring($message);
|
||||
}
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_AJAX) {
|
||||
# JSON can't handle strings across lines.
|
||||
$message =~ s/\n/ /gm;
|
||||
my $err;
|
||||
$err->{'success'} = JSON::false;
|
||||
$err->{'error'} = $error;
|
||||
$err->{'message'} = $message;
|
||||
my $json = new JSON;
|
||||
print $json->encode($err);
|
||||
}
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ sub SETTINGS {
|
|||
# 2006-12-10 LpSolit@gmail.com -- Bug 297186
|
||||
lang => { subclass => 'Lang',
|
||||
default => ${Bugzilla->languages}[0] },
|
||||
view_testopia => { options => ['on', 'off'],
|
||||
default => 'on' },
|
||||
# 2007-07-02 altlist@gmail.com -- Bug 225731
|
||||
quote_replies => { options => ['quoted_reply', 'simple_reply', 'off'],
|
||||
default => "quoted_reply" },
|
||||
|
|
|
@ -111,6 +111,8 @@ sub FILESYSTEM {
|
|||
'runtests.pl' => { perms => $owner_executable },
|
||||
'testserver.pl' => { perms => $ws_executable },
|
||||
'whine.pl' => { perms => $ws_executable },
|
||||
'tr_csv2xml.pl' => { perms => $ws_executable },
|
||||
'tr_importxml.pl' => { perms => $ws_executable },
|
||||
'customfield.pl' => { perms => $owner_executable },
|
||||
'email_in.pl' => { perms => $ws_executable },
|
||||
'sanitycheck.pl' => { perms => $ws_executable },
|
||||
|
@ -157,6 +159,8 @@ sub FILESYSTEM {
|
|||
dirs => $ws_dir_readable },
|
||||
$extlib => { files => $ws_readable,
|
||||
dirs => $ws_dir_readable },
|
||||
"$libdir/testopia" => { files => $ws_readable,
|
||||
dirs => $ws_dir_readable },
|
||||
$templatedir => { files => $ws_readable,
|
||||
dirs => $ws_dir_readable },
|
||||
$extensionsdir => { files => $ws_readable,
|
||||
|
@ -171,6 +175,8 @@ sub FILESYSTEM {
|
|||
dirs => $ws_dir_readable },
|
||||
t => { files => $owner_readable,
|
||||
dirs => $owner_dir_readable },
|
||||
'testopia/t' => { files => $owner_readable,
|
||||
dirs => $owner_dir_readable },
|
||||
'docs/*/html' => { files => $ws_readable,
|
||||
dirs => $ws_dir_readable },
|
||||
'docs/*/pdf' => { files => $ws_readable,
|
||||
|
|
|
@ -0,0 +1,795 @@
|
|||
# -*- 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.
|
||||
#
|
||||
# Large portions lifted uncerimoniously from Bugzilla::Attachment.pm
|
||||
# and bugzilla's attachment.cgi
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::Attachment;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use Bugzilla::Testopia::Util;
|
||||
|
||||
use base qw(Exporter Bugzilla::Object);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
use constant DB_TABLE => "test_attachments";
|
||||
use constant NAME_FIELD => "description";
|
||||
use constant ID_FIELD => "attachment_id";
|
||||
use constant DB_COLUMNS => qw(
|
||||
attachment_id
|
||||
submitter_id
|
||||
description
|
||||
filename
|
||||
creation_ts
|
||||
mime_type
|
||||
);
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(submitter_id description filename mime_type);
|
||||
use constant UPDATE_COLUMNS => qw(description filename mime_type);
|
||||
|
||||
use constant VALIDATORS => {
|
||||
plan_id => \&_check_plan,
|
||||
case_id => \&_check_case,
|
||||
filename => \&_check_filename,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Validators ####
|
||||
###############################
|
||||
sub _validate_data {
|
||||
my $data = shift;
|
||||
my $maxsize = Bugzilla->params->{"maxattachmentsize"};
|
||||
$maxsize *= 1024; # Convert from K
|
||||
|
||||
# Make sure the attachment does not exceed the maximum permitted size
|
||||
my $len = $data ? length($data) : 0;
|
||||
if ($maxsize && $len > $maxsize) {
|
||||
my $vars = { filesize => sprintf("%.0f", $len/1024) };
|
||||
ThrowUserError("file_too_large", $vars);
|
||||
}
|
||||
trick_taint($data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub _check_plan {
|
||||
my ($invocant, $plan_id) = @_;
|
||||
Bugzilla::Testopia::Util::validate_test_id($plan_id, 'plan');
|
||||
trick_taint($plan_id);
|
||||
return $plan_id;
|
||||
}
|
||||
|
||||
sub _check_case {
|
||||
my ($invocant, $case_id) = @_;
|
||||
Bugzilla::Testopia::Util::validate_test_id($case_id, 'case');
|
||||
trick_taint($case_id);
|
||||
return $case_id;
|
||||
}
|
||||
|
||||
sub _check_filename {
|
||||
my ($invocant, $filename) = @_;
|
||||
# Remove path info (if any) from the file name. The browser should do this
|
||||
# for us, but some are buggy. This may not work on Mac file names and could
|
||||
# mess up file names with slashes in them, but them's the breaks. We only
|
||||
# use this as a hint to users downloading attachments anyway, so it's not
|
||||
# a big deal if it munges incorrectly occasionally.
|
||||
$filename =~ s/^.*[\/\\]//;
|
||||
|
||||
# Truncate the filename to 100 characters, counting from the end of the string
|
||||
# to make sure we keep the filename extension.
|
||||
$filename = substr($filename, -100, 100);
|
||||
|
||||
trick_taint($filename);
|
||||
return $filename;
|
||||
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Mutators ####
|
||||
###############################
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_mime_type { $_[0]->set('mime_type', $_[1]); }
|
||||
sub set_filename { $_[0]->set('filename', $_[1]); }
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $param = shift;
|
||||
|
||||
# We want to be able to supply an empty object to the templates for numerous
|
||||
# lists etc. This is much cleaner than exporting a bunch of subroutines and
|
||||
# adding them to $vars one by one. Probably just Laziness shining through.
|
||||
if (ref $param eq 'HASH'){
|
||||
if (!keys %$param || $param->{PREVALIDATED}){
|
||||
bless($param, $class);
|
||||
return $param;
|
||||
}
|
||||
}
|
||||
|
||||
unshift @_, $param;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub create {
|
||||
my ($class, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$class->SUPER::check_required_create_fields($params);
|
||||
|
||||
# This is an either/or operation. We need either a plan or a case
|
||||
# to attach this to.
|
||||
if (!$params->{'case_id'} && !$params->{'plan_id'}){
|
||||
ThrowCodeError("testopia-missing-attachment-key");
|
||||
}
|
||||
|
||||
my $field_values = $class->run_create_validators($params);
|
||||
|
||||
# Windows screenshots are usually uncompressed BMP files which
|
||||
# makes for a quick way to eat up disk space. Let's compress them.
|
||||
# We do this before we check the size since the uncompressed version
|
||||
# could easily be greater than maxattachmentsize.
|
||||
if (Bugzilla->params->{'convert_uncompressed_images'}
|
||||
&& $field_values->{'mime_type'} eq 'image/bmp'){
|
||||
require Image::Magick;
|
||||
my $img = Image::Magick->new(magick=>'bmp');
|
||||
$img->BlobToImage($field_values->{'contents'});
|
||||
$img->set(magick=>'png');
|
||||
my $imgdata = $img->ImageToBlob();
|
||||
$field_values->{'contents'} = $imgdata;
|
||||
$field_values->{'mime_type'} = 'image/png';
|
||||
}
|
||||
|
||||
$field_values->{contents} = _validate_data($field_values->{contents});
|
||||
$field_values->{creation_ts} = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my $contents = $field_values->{contents};
|
||||
my $case_id = $field_values->{case_id};
|
||||
my $plan_id = $field_values->{plan_id};
|
||||
my $caserun_id = $field_values->{caserun_id};
|
||||
|
||||
delete $field_values->{contents};
|
||||
delete $field_values->{case_id};
|
||||
delete $field_values->{plan_id};
|
||||
delete $field_values->{caserun_id};
|
||||
|
||||
my $self = $class->SUPER::insert_create_data($field_values);
|
||||
|
||||
# Store the data
|
||||
$dbh->do("INSERT INTO test_attachment_data (attachment_id, contents) VALUES(?,?)",
|
||||
undef, $self->id, $contents);
|
||||
|
||||
# Link it to the case or plan
|
||||
if ($case_id){
|
||||
$dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id, case_run_id)
|
||||
VALUES (?,?,?)",
|
||||
undef, ($self->id, $case_id, $caserun_id));
|
||||
}
|
||||
elsif ($plan_id){
|
||||
$dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id)
|
||||
VALUES (?,?)",
|
||||
undef, ($self->id, $plan_id));
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
sub TO_JSON {
|
||||
my $self = shift;
|
||||
my $cgi = shift;
|
||||
my $obj;
|
||||
my $json = new JSON;
|
||||
|
||||
foreach my $field ($self->DB_COLUMNS){
|
||||
$obj->{$field} = $self->{$field};
|
||||
}
|
||||
|
||||
# Add the calculated fields
|
||||
$obj->{'isviewable'} = $self->is_browser_safe($cgi);
|
||||
$obj->{'datasize'} = $self->datasize;
|
||||
$obj->{'submitter'} = $self->submitter->name if $self->submitter;
|
||||
$obj->{'canedit'} = $self->canedit;
|
||||
$obj->{'candelete'} = $self->candelete;
|
||||
$obj->{'caserun_id'} = $self->{'caserun_id'} if $self->{'caserun_id'};
|
||||
|
||||
return $json->encode($obj);
|
||||
}
|
||||
|
||||
# Returns 1 if the parameter is a content-type viewable in this browser
|
||||
# Note that we don't use $cgi->Accept()'s ability to check if a content-type
|
||||
# matches, because this will return a value even if it's matched by the generic
|
||||
# */* which most browsers add to the end of their Accept: headers.
|
||||
|
||||
sub is_browser_safe {
|
||||
my $self = shift;
|
||||
my $cgi = shift;
|
||||
my $contenttype = $self->mime_type;
|
||||
|
||||
# We assume we can view all text and image types
|
||||
if ($contenttype =~ /^(text|image)\//) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Mozilla can view XUL. Note the trailing slash on the Gecko detection to
|
||||
# avoid sending XUL to Safari.
|
||||
if (($contenttype =~ /^application\/vnd\.mozilla\./) &&
|
||||
($cgi->user_agent() =~ /Gecko\//))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
# If it's not one of the above types, we check the Accept: header for any
|
||||
# types mentioned explicitly.
|
||||
my $accept = join(",", $cgi->Accept());
|
||||
|
||||
if ($accept =~ /^(.*,)?\Q$contenttype\E(,.*)?$/) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub obliterate {
|
||||
my $self = shift;
|
||||
return 0 unless $self->candelete;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->do("DELETE FROM test_attachment_data
|
||||
WHERE attachment_id = ?", undef, $self->{'attachment_id'});
|
||||
$dbh->do("DELETE FROM test_case_attachments
|
||||
WHERE attachment_id = ?", undef, $self->{'attachment_id'});
|
||||
$dbh->do("DELETE FROM test_plan_attachments
|
||||
WHERE attachment_id = ?", undef, $self->{'attachment_id'});
|
||||
$dbh->do("DELETE FROM test_attachments
|
||||
WHERE attachment_id = ?", undef, $self->{'attachment_id'});
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub link_plan {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
my ($is) = $dbh->selectrow_array(
|
||||
"SELECT 1
|
||||
FROM test_plan_attachments
|
||||
WHERE attachment_id = ?
|
||||
AND plan_id = ?",
|
||||
undef, ($self->id, $plan_id));
|
||||
if ($is) {
|
||||
$dbh->bz_commit_transaction();
|
||||
return;
|
||||
}
|
||||
|
||||
$dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id)
|
||||
VALUES (?,?)",
|
||||
undef, ($self->id, $plan_id));
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
sub link_case {
|
||||
my $self = shift;
|
||||
my ($case_id) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
my ($is) = $dbh->selectrow_array(
|
||||
"SELECT 1
|
||||
FROM test_case_attachments
|
||||
WHERE attachment_id = ?
|
||||
AND case_id = ?",
|
||||
undef, ($self->id, $case_id));
|
||||
if ($is) {
|
||||
$dbh->bz_commit_transaction();
|
||||
return;
|
||||
}
|
||||
|
||||
$dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id)
|
||||
VALUES (?,?)",
|
||||
undef, ($self->id, $case_id));
|
||||
$dbh->bz_bz_commit_transaction();
|
||||
}
|
||||
|
||||
sub unlink_plan {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($refcount) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_plan_attachments
|
||||
WHERE attachment_id = ?", undef, $self->id);
|
||||
if ($refcount > 1){
|
||||
$dbh->do("DELETE FROM test_plan_attachments
|
||||
WHERE plan_id = ? AND attachment_id = ?",
|
||||
undef, ($plan_id, $self->id));
|
||||
}
|
||||
else {
|
||||
$self->obliterate;
|
||||
}
|
||||
}
|
||||
|
||||
sub unlink_case {
|
||||
my $self = shift;
|
||||
my ($case_id) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($refcount) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_case_attachments
|
||||
WHERE attachment_id = ?", undef, $self->id);
|
||||
if ($refcount > 1){
|
||||
$dbh->do("DELETE FROM test_case_attachments
|
||||
WHERE case_id = ? AND attachment_id = ?",
|
||||
undef, ($case_id, $self->id));
|
||||
}
|
||||
else {
|
||||
$self->obliterate;
|
||||
}
|
||||
}
|
||||
|
||||
sub canview {
|
||||
my $self = shift;
|
||||
return 1 if Bugzilla->user->in_group('Testers');
|
||||
foreach my $i (@{$self->cases}){
|
||||
return 0 unless $i->canview;
|
||||
}
|
||||
foreach my $i (@{$self->plans}){
|
||||
return 0 unless $i->canview;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub canedit {
|
||||
my $self = shift;
|
||||
return 1 if Bugzilla->user->in_group('Testers');
|
||||
foreach my $i (@{$self->cases}){
|
||||
return 0 unless $i->canedit;
|
||||
}
|
||||
foreach my $i (@{$self->plans}){
|
||||
return 0 unless $i->canedit;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 1 if Bugzilla->user->in_group("admin");
|
||||
return 0 unless $self->canedit && Bugzilla->params->{"allow-test-deletion"};
|
||||
return 1 if Bugzilla->user->id == $self->submitter->id;
|
||||
foreach my $i (@{$self->cases}){
|
||||
return 0 unless $i->canedit;
|
||||
}
|
||||
foreach my $i (@{$self->plans}){
|
||||
return 0 unless $i->canedit;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
sub id { return $_[0]->{'attachment_id'}; }
|
||||
sub submitter { return Bugzilla::User->new($_[0]->{'submitter_id'}); }
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
sub filename { return $_[0]->{'filename'}; }
|
||||
sub creation_ts { return $_[0]->{'creation_ts'}; }
|
||||
sub mime_type { return $_[0]->{'mime_type'}; }
|
||||
|
||||
sub contents {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'contents'} if exists $self->{'contents'};
|
||||
my ($contents) = $dbh->selectrow_array("SELECT contents
|
||||
FROM test_attachment_data
|
||||
WHERE attachment_id = ?",
|
||||
undef, $self->{'attachment_id'});
|
||||
|
||||
$self->{'contents'} = $contents;
|
||||
return $self->{'contents'};
|
||||
}
|
||||
|
||||
sub datasize {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'datasize'} if exists $self->{'datasize'};
|
||||
|
||||
my ($datasize) = $dbh->selectrow_array("SELECT LENGTH(contents)
|
||||
FROM test_attachment_data
|
||||
WHERE attachment_id = ?",
|
||||
undef, $self->{'attachment_id'});
|
||||
$self->{'datasize'} = $datasize;
|
||||
return $self->{'datasize'};
|
||||
}
|
||||
|
||||
sub cases {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
require Bugzilla::Testopia::TestCase;
|
||||
|
||||
return $self->{'cases'} if exists $self->{'cases'};
|
||||
my $caseids = $dbh->selectcol_arrayref(
|
||||
"SELECT case_id FROM test_case_attachments
|
||||
WHERE attachment_id = ?",
|
||||
undef, $self->id);
|
||||
my @cases;
|
||||
foreach my $id (@{$caseids}){
|
||||
push @cases, Bugzilla::Testopia::TestCase->new($id);
|
||||
}
|
||||
|
||||
$self->{'cases'} = \@cases;
|
||||
return $self->{'cases'};
|
||||
}
|
||||
|
||||
sub plans {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
require Bugzilla::Testopia::TestPlan;
|
||||
|
||||
return $self->{'plans'} if exists $self->{'plans'};
|
||||
my $planids = $dbh->selectcol_arrayref(
|
||||
"SELECT plan_id FROM test_plan_attachments
|
||||
WHERE attachment_id = ?",
|
||||
undef, $self->id);
|
||||
my @plans;
|
||||
foreach my $id (@{$planids}){
|
||||
push @plans, Bugzilla::Testopia::TestPlan->new($id);
|
||||
}
|
||||
|
||||
$self->{'plans'} = \@plans;
|
||||
return $self->{'plans'};
|
||||
}
|
||||
|
||||
sub type {
|
||||
my $self = shift;
|
||||
$self->{'type'} = 'attachment';
|
||||
return $self->{'type'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Attachment - Attachment object for Testopia
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Object
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides support for attachments to Test Cases, Test
|
||||
Plans and Test Case Runs in Testopia. Attachments can be linked
|
||||
to multiple cases or plans. If linked to a test case, there is
|
||||
an optional id for the case_run in which it was linked.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head2 Creating
|
||||
|
||||
$attachment = Bugzilla::Testopia::Attachment->new($attachment_id);
|
||||
$attachment = Bugzilla::Testopia::Attachment->new({name => $name});
|
||||
|
||||
$new_attachment = Bugzilla::Testopia::Attachment->create({name => $name,
|
||||
description => $desc
|
||||
... });
|
||||
|
||||
=head2 Updating
|
||||
|
||||
$attachment->set_filename($name);
|
||||
$attachment->set_description($desc);
|
||||
$attachment->set_mime_type($mime_type);
|
||||
|
||||
$attachment->update();
|
||||
|
||||
=head2 Accessors
|
||||
|
||||
my $id = $attachment->id;
|
||||
my $fname = $attachment->filename;
|
||||
my $desc = $attachment->description;
|
||||
my $size = $attachment->datasize;
|
||||
my $contents = $attachment->contents;
|
||||
my $created = $attachment->creation_ts;
|
||||
my $submitter = $attachment->submitter;
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
Table test_attachments
|
||||
+---------------+------------------+------+-----+---------------------+----------------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+---------------+------------------+------+-----+---------------------+----------------+
|
||||
| attachment_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| submitter_id | mediumint(9) | NO | MUL | 0 | |
|
||||
| description | mediumtext | YES | | NULL | |
|
||||
| filename | mediumtext | YES | | NULL | |
|
||||
| creation_ts | datetime | NO | | 0000-00-00 00:00:00 | |
|
||||
| mime_type | varchar(100) | NO | | | |
|
||||
+---------------+------------------+------+-----+---------------------+----------------+
|
||||
|
||||
Table test_attachment_data
|
||||
+---------------+----------+------+-----+---------+-------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+---------------+----------+------+-----+---------+-------+
|
||||
| attachment_id | int(11) | NO | MUL | | |
|
||||
| contents | longblob | YES | | NULL | |
|
||||
+---------------+----------+------+-----+---------+-------+
|
||||
|
||||
=over
|
||||
|
||||
=item C<attachment_id>
|
||||
|
||||
The unique id of this attachment in the database.
|
||||
|
||||
=item C<creation_ts>
|
||||
|
||||
Timestamp - when this attachment was created
|
||||
|
||||
=item C<description>
|
||||
|
||||
A description of this attachment. Becomes the link on the plan and case pages.
|
||||
|
||||
=item C<filename>
|
||||
|
||||
The file name from the users harddrive before uploading with its path removed.
|
||||
|
||||
=item C<mime_type>
|
||||
|
||||
The MIME type associated with this attachment. This is used to determine if the
|
||||
attachment if viewable in a browser.
|
||||
|
||||
=item C<test_attachment_data.contents>
|
||||
|
||||
The actual attachment data. This is stored in a separate table to reduce lookup times.
|
||||
|
||||
=item C<submitter_id>
|
||||
|
||||
The ID of the person that uploaded the attachment.
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<new($param)>
|
||||
|
||||
Description: Used to load an existing attachment from the database.
|
||||
|
||||
Params: $param - An integer representing the ID in the database
|
||||
or a hash with the "name" key representing the named
|
||||
attachment in the database.
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Attachment object
|
||||
|
||||
=item C<candelete()>
|
||||
|
||||
Description: Check that the current attachment can be safely deleted and that
|
||||
the current user has rights to do so.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: 0 if the user does not have rights to delete this attachment.
|
||||
1 if the user does have rights.
|
||||
|
||||
=item C<canedit()>
|
||||
|
||||
Description: Check that the current user has rights to edit this attachment.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: 0 if the user does not have rights.
|
||||
1 if the user does have rights.
|
||||
|
||||
=item C<canview()>
|
||||
|
||||
Description: Chec that the current user has rights to view this attachment.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: 0 if the user does not have rights.
|
||||
1 if the user does have rights.
|
||||
|
||||
=item C<create()>
|
||||
|
||||
Description: Creates a new attachment object and stores it in the database.
|
||||
Also links the associated plans or cases to the object.
|
||||
|
||||
Params: A hash with keys and values matching the fields of the attachment to
|
||||
be created.
|
||||
|
||||
Returns: The newly created object.
|
||||
|
||||
=item C<is_browser_safe()>
|
||||
|
||||
Description: Checks that the attachment is viewable in the browser based on
|
||||
its mime_type.
|
||||
|
||||
Params: CGI - a Bugzilla::CGI object.
|
||||
|
||||
Returns: 1 if this attachment can be viewed inline in the browser.
|
||||
0 if the attachment must be downloaded for viewing in an external
|
||||
application.
|
||||
|
||||
=item C<link_case()>
|
||||
|
||||
Description: Links this attachment to the specified case.
|
||||
|
||||
Params: case_id - id of the case to link to.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<link_plan()>
|
||||
|
||||
Description: Links this attachment to the specified plan.
|
||||
|
||||
Params: plan_id - id of the plan to link to.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<obliterate()>
|
||||
|
||||
Description: Completely removes this attachment from the database and clears
|
||||
references to it.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_description()>
|
||||
|
||||
Description: Replaces the current attachment's description. Must call update to
|
||||
store the change in the database.
|
||||
|
||||
Params: text - the new description.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_filename()>
|
||||
|
||||
Description: Sets the isactive field.
|
||||
|
||||
Params: string - the new filename
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_mime_type()>
|
||||
|
||||
Description: Changes the assigned mime_type
|
||||
|
||||
Params: string - the new mime_type
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<store()> DEPRECATED
|
||||
|
||||
Description: Similar to create except validation is not performed during store.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: The id of the newly stored attachment.
|
||||
|
||||
=item C<to_json()>
|
||||
|
||||
Description: Outputs a JSON representation of the object.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A JSON string.
|
||||
|
||||
=item C<unlink_case()>
|
||||
|
||||
Description: Unlinks the this attachment from the specified test case. If only
|
||||
attached to a single case, delete the attachment instead.
|
||||
|
||||
Params: case_id - id of the case to unlink.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<unlink_plan()>
|
||||
|
||||
Description: Unlinks the this attachment from the specified test plan. If only
|
||||
attached to a single plan, delete the attachment instead.
|
||||
|
||||
Params: plan_id - id of the plan to unlink.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=over
|
||||
|
||||
=item C<cases()>
|
||||
|
||||
Returns a list of TestCase objects that this attachment is associated with.
|
||||
|
||||
=item C<contents()>
|
||||
|
||||
Returns the content data of this attachment.
|
||||
|
||||
=item C<creation_ts()>
|
||||
|
||||
Returns the timestamp when this attachment was created.
|
||||
|
||||
=item C<datasize()>
|
||||
|
||||
Returns the size in byts of the contents of this attachment.
|
||||
|
||||
=item C<description()>
|
||||
|
||||
Returns the description of this attachment.
|
||||
|
||||
=item C<filename()>
|
||||
|
||||
Returns the filename from the users harddrive that was uploaded when the
|
||||
attachemnt was created with any path information stripped off.
|
||||
|
||||
=item C<id()>
|
||||
|
||||
Returns the id of the attachment.
|
||||
|
||||
=item C<mime_type()>
|
||||
|
||||
Returns the MIME type of this attachment.
|
||||
|
||||
=item C<plans()>
|
||||
|
||||
Returns a list of TestPlan objects associated with this attachment.
|
||||
|
||||
=item C<submitter()>
|
||||
|
||||
Returns the login name of the person who submitted this attachment.
|
||||
|
||||
=item C<type()>
|
||||
|
||||
Returns "attachment". For use in internal code.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
L<Bugzilla::Testopia::TestCase>
|
||||
L<Bugzilla::Testopia::TestPlan>
|
||||
L<Bugzilla::Object>
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,584 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::Build;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Testopia::Product;
|
||||
use JSON;
|
||||
|
||||
use base qw(Exporter Bugzilla::Object);
|
||||
@Bugzilla::Testopia::Build::EXPORT = qw(check_build);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
use constant DB_TABLE => "test_builds";
|
||||
use constant NAME_FIELD => "name";
|
||||
use constant ID_FIELD => "build_id";
|
||||
use constant DB_COLUMNS => qw(
|
||||
build_id
|
||||
product_id
|
||||
name
|
||||
description
|
||||
milestone
|
||||
isactive
|
||||
);
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(product_id name milestone isactive);
|
||||
use constant UPDATE_COLUMNS => qw(name description milestone isactive);
|
||||
|
||||
use constant VALIDATORS => {
|
||||
product_id => \&_check_product,
|
||||
isactive => \&_check_isactive,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Validators ####
|
||||
###############################
|
||||
sub _check_product {
|
||||
my ($invocant, $product_id) = @_;
|
||||
$product_id = trim($product_id);
|
||||
|
||||
ThrowUserError("testopia-create-denied", {'object' => 'build'}) unless Bugzilla->user->in_group('Testers');
|
||||
|
||||
my $product;
|
||||
if (trim($product_id) !~ /^\d+$/ ){
|
||||
$product = Bugzilla::Product::check_product($product_id);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Testopia::Product->new($product_id);
|
||||
}
|
||||
|
||||
if (ref $invocant){
|
||||
$invocant->{'product'} = $product;
|
||||
return $product->id;
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub _check_name {
|
||||
my ($invocant, $name, $product_id) = @_;
|
||||
$name = clean_text($name) if $name;
|
||||
|
||||
if (!defined $name || $name eq '') {
|
||||
ThrowUserError('testopia-missing-required-field', {'field' => 'name'});
|
||||
}
|
||||
|
||||
trick_taint($name);
|
||||
|
||||
# Check that we don't already have a build with that name in this product.
|
||||
my $orig_id = check_build($name, $product_id);
|
||||
my $notunique;
|
||||
|
||||
if (ref $invocant){
|
||||
# If updating, we have matched ourself at least
|
||||
$notunique = 1 if (($orig_id && $orig_id != $invocant->id))
|
||||
}
|
||||
else {
|
||||
# In new build any match is one too many
|
||||
$notunique = 1 if $orig_id;
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-name-not-unique',
|
||||
{'object' => 'Build',
|
||||
'name' => $name}) if $notunique;
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _check_milestone {
|
||||
my ($invocant, $milestone, $product) = @_;
|
||||
if (ref $invocant){
|
||||
$product = $invocant->product;
|
||||
}
|
||||
$milestone = trim($milestone);
|
||||
$milestone = Bugzilla::Milestone->check({product => $product, name => $milestone});
|
||||
return $milestone->name;
|
||||
}
|
||||
|
||||
sub _check_isactive {
|
||||
my ($invocant, $isactive) = @_;
|
||||
ThrowCodeError('bad_arg', {argument => 'isactive', function => 'set_isactive'}) unless ($isactive =~ /(1|0)/);
|
||||
return $isactive;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Mutators ####
|
||||
###############################
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_isactive { $_[0]->set('isactive', $_[1]); }
|
||||
sub set_milestone {
|
||||
my ($self, $value) = @_;
|
||||
$value = $self->_check_milestone($value);
|
||||
$self->set('milestone', $value);
|
||||
}
|
||||
sub set_name {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$value = $self->_check_name($value, $self->product);
|
||||
$self->set('name', $value);
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $param = shift;
|
||||
|
||||
# We want to be able to supply an empty object to the templates for numerous
|
||||
# lists etc. This is much cleaner than exporting a bunch of subroutines and
|
||||
# adding them to $vars one by one. Probably just Laziness shining through.
|
||||
if (ref $param eq 'HASH'){
|
||||
if (!keys %$param || $param->{PREVALIDATED}){
|
||||
bless($param, $class);
|
||||
return $param;
|
||||
}
|
||||
}
|
||||
|
||||
unshift @_, $param;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub run_create_validators {
|
||||
my $class = shift;
|
||||
my $params = $class->SUPER::run_create_validators(@_);
|
||||
my $product = $params->{product_id}; # Returns actual product object
|
||||
|
||||
$params->{milestone} = $class->_check_milestone($params->{milestone}, $product);
|
||||
$params->{name} = $class->_check_name($params->{name}, $product);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub create {
|
||||
my ($class, $params) = @_;
|
||||
|
||||
$class->SUPER::check_required_create_fields($params);
|
||||
my $field_values = $class->run_create_validators($params);
|
||||
|
||||
$field_values->{isactive} = 1;
|
||||
$field_values->{product_id} = $field_values->{product_id}->id;
|
||||
my $self = $class->SUPER::insert_create_data($field_values);
|
||||
|
||||
return $self;
|
||||
}
|
||||
###############################
|
||||
#### Functions ####
|
||||
###############################
|
||||
sub check_build {
|
||||
my ($name, $product, $throw) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $is = $dbh->selectrow_array(
|
||||
"SELECT build_id FROM test_builds
|
||||
WHERE name = ? AND product_id = ?",
|
||||
undef, $name, $product->id);
|
||||
if ($throw){
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $name}) unless $is;
|
||||
return Bugzilla::Testopia::Build->new($is);
|
||||
}
|
||||
return $is;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
sub TO_JSON {
|
||||
my $self = shift;
|
||||
my $obj;
|
||||
my $json = new JSON;
|
||||
|
||||
foreach my $field ($self->DB_COLUMNS){
|
||||
$obj->{$field} = $self->{$field};
|
||||
}
|
||||
|
||||
return $json->encode($obj);
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
sub id { return $_[0]->{'build_id'}; }
|
||||
sub product_id { return $_[0]->{'product_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub description { return $_[0]->{'description'};}
|
||||
sub milestone { return $_[0]->{'milestone'};}
|
||||
sub isactive { return $_[0]->{'isactive'};}
|
||||
|
||||
sub bugs {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'bugs'} if exists $self->{'bugs'};
|
||||
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 r.build_id = ?",
|
||||
undef, $self->id);
|
||||
my @bugs;
|
||||
foreach my $id (@{$ref}){
|
||||
push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id);
|
||||
}
|
||||
$self->{'bugs'} = \@bugs if @bugs;
|
||||
$self->{'bug_list'} = join(',', @$ref);
|
||||
return $self->{'bugs'};
|
||||
}
|
||||
|
||||
sub product {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'product'} if exists $self->{'product'};
|
||||
|
||||
$self->{'product'} = Bugzilla::Testopia::Product->new($self->product_id);
|
||||
return $self->{'product'};
|
||||
}
|
||||
|
||||
sub run_count {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'run_count'} if exists $self->{'run_count'};
|
||||
|
||||
$self->{'run_count'} = $dbh->selectrow_array(
|
||||
"SELECT COUNT(run_id) FROM test_runs
|
||||
WHERE build_id = ?", undef, $self->{'build_id'});
|
||||
|
||||
return $self->{'run_count'};
|
||||
}
|
||||
|
||||
sub case_run_count {
|
||||
my $self = shift;
|
||||
my ($status_id, $builds) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my @build_ids;
|
||||
if ($builds){
|
||||
push @build_ids, $_->id foreach (@$builds);
|
||||
}
|
||||
push @build_ids, $self->id if $self->id;
|
||||
|
||||
my $ids = join (',', @build_ids);
|
||||
|
||||
my $query = "SELECT COUNT(case_run_id) FROM test_case_runs
|
||||
WHERE build_id IN (". $ids . ")";
|
||||
$query .= " AND case_run_status_id = ?" if $status_id;
|
||||
|
||||
my $count;
|
||||
if ($status_id){
|
||||
$count = $dbh->selectrow_array($query, undef, ($status_id));
|
||||
}
|
||||
else {
|
||||
$count = $dbh->selectrow_array($query);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub runs {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'runs'} if exists $self->{'runs'};
|
||||
|
||||
require Bugzilla::Testopia::TestRun;
|
||||
|
||||
my $runids = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs
|
||||
WHERE build_id = ?",
|
||||
undef, $self->id);
|
||||
my @runs;
|
||||
foreach my $id (@{$runids}){
|
||||
push @runs, Bugzilla::Testopia::TestRun->new($id);
|
||||
}
|
||||
|
||||
$self->{'runs'} = \@runs;
|
||||
return $self->{'runs'};
|
||||
}
|
||||
|
||||
=head2 caseruns
|
||||
|
||||
Returns a reference to a list of test caseruns useing this build
|
||||
|
||||
=cut
|
||||
|
||||
sub caseruns {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'caseruns'} if exists $self->{'caseruns'};
|
||||
|
||||
require Bugzilla::Testopia::TestCaseRun;
|
||||
|
||||
my $ids = $dbh->selectcol_arrayref("SELECT case_run_id FROM test_case_runs
|
||||
WHERE build_id = ?",
|
||||
undef, $self->id);
|
||||
my @caseruns;
|
||||
foreach my $id (@{$ids}){
|
||||
push @caseruns, Bugzilla::Testopia::TestCaseRun->new($id);
|
||||
}
|
||||
|
||||
$self->{'caseruns'} = \@caseruns;
|
||||
return $self->{'caseruns'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Build
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Object
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Builds are used to classify test runs. They correspond to the results of
|
||||
a period of work in software development. Builds are product level attributes
|
||||
and are associated with a milestone if targetmilestones are used in Bugzilla.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head2 Creating
|
||||
|
||||
$build = Bugzilla::Testopia::Build->new($build_id);
|
||||
$build = Bugzilla::Testopia::Build->new({name => $name});
|
||||
|
||||
$new_build = Bugzilla::Testopia::Build->create({name => $name,
|
||||
description => $desc
|
||||
... });
|
||||
|
||||
=head2 Updating
|
||||
|
||||
$build->set_name($name);
|
||||
$build->set_description($name);
|
||||
$build->set_milestone($milestone);
|
||||
$build->set_isactive($isactive);
|
||||
|
||||
$build->update();
|
||||
|
||||
=head2 Accessors
|
||||
|
||||
my $id = $build->id;
|
||||
my $name = $build->name;
|
||||
my $desc = $build->description;
|
||||
my $pid = $build->product_id;
|
||||
my $milestone = $build->milestone;
|
||||
my $crc = $build->case_run_count;
|
||||
my $active = $build->isactive;
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
+-------------+------------------+------+-----+---------+----------------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+-------------+------------------+------+-----+---------+----------------+
|
||||
| build_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| product_id | smallint(6) | NO | MUL | 0 | |
|
||||
| milestone | varchar(20) | YES | MUL | NULL | |
|
||||
| name | varchar(255) | YES | MUL | NULL | |
|
||||
| description | text | YES | | NULL | |
|
||||
| isactive | tinyint(4) | NO | | 1 | |
|
||||
+-------------+------------------+------+-----+---------+----------------+
|
||||
|
||||
=over
|
||||
|
||||
=item C<build_id>
|
||||
|
||||
The unique id of this build in the database.
|
||||
|
||||
=item C<name> B<REQUIRED>
|
||||
|
||||
A unique name for this build.
|
||||
|
||||
=item C<product_id> B<REQUIRED> B<CREATE ONLY>
|
||||
|
||||
The id of the Bugzilla product this build is attached to.
|
||||
|
||||
=item C<milestone> I<OPTIONAL>
|
||||
|
||||
The value from the Bugzilla product milestone table this build is associated with.
|
||||
Defautlts to the product default milestone.
|
||||
|
||||
=item C<description> I<OPTIONAL>
|
||||
|
||||
A description of this build.
|
||||
|
||||
=item C<isactive> I<OPTIONAL>
|
||||
|
||||
Boolean - Determines whether to show this build in lists for selection.
|
||||
Defaults to true.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_build($name, $product_id)>
|
||||
|
||||
Description: Checks if a build of a given name exists for a given product.
|
||||
|
||||
Params: name - string representing the name to check for.
|
||||
product_id - the product to lookup the build in.
|
||||
|
||||
Returns: The id of the build if one matches.
|
||||
undef if it does not match any build.
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<new($param)>
|
||||
|
||||
Description: Used to load an existing build from the database.
|
||||
|
||||
Params: $param - An integer representing the ID in the database
|
||||
or a hash with the "name" key representing the named
|
||||
build in the database.
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Build object
|
||||
|
||||
=item C<create()>
|
||||
|
||||
Description: Creates a new build object and stores it in the database
|
||||
|
||||
Params: A hash with keys and values matching the fields of the build to
|
||||
be created.
|
||||
|
||||
Returns: The newly created object.
|
||||
|
||||
=item C<set_description()>
|
||||
|
||||
Description: Replaces the current build's description. Must call update to
|
||||
store the change in the database.
|
||||
|
||||
Params: text - the new description.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_isactive()>
|
||||
|
||||
Description: Sets the isactive field.
|
||||
|
||||
Params: boolean - 1 for active 0 for inactive.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_milestone()>
|
||||
|
||||
Description: Assigns this build to a different milestone
|
||||
|
||||
Params: string - the new milestone value
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<set_name()>
|
||||
|
||||
Description: Renames the current build. If the new name is already in use
|
||||
by another build in this product, an error will be thrown.
|
||||
The update method must be called to make the change in the database.
|
||||
|
||||
Params: string - the new name
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=item C<to_json()>
|
||||
|
||||
Description: Outputs a JSON representation of the object.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A JSON string.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=over
|
||||
|
||||
=item C<case_run_count()>
|
||||
|
||||
Params: case_run_status_id - optional;
|
||||
|
||||
Returns: The number of case-runs in this build. Optionally for a given status.
|
||||
|
||||
=item C<caseruns()>
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A list of blessed caserun objects that use this build
|
||||
|
||||
=item C<description()>
|
||||
|
||||
Returns the description of this build.
|
||||
|
||||
=item C<id()>
|
||||
|
||||
Returns the id of the build
|
||||
|
||||
=item C<isactive()>
|
||||
|
||||
Returns 1 if this build is visible in pick lists for runs and caserund and 0 if not.
|
||||
|
||||
=item C<milestone()>
|
||||
|
||||
Returns the milestone value that this build is associated with.
|
||||
|
||||
=item C<name()>
|
||||
|
||||
Returns the name of this build
|
||||
|
||||
=item C<product()>
|
||||
|
||||
Returns a Bugzilla::Testopia::Product object of the product this build is of.
|
||||
|
||||
=item C<product_id()>
|
||||
|
||||
Returns the product id of the build.
|
||||
|
||||
=item C<run_count()>
|
||||
|
||||
Returns an integer representing the number of runs this build is associated to.
|
||||
|
||||
=item C<runs()>
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A list of blessed run objects that use this build
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
L<Bugzilla::Testopia::Product>
|
||||
L<Bugzilla::Testopia::TestRun>
|
||||
L<Bugzilla::Object>
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,517 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Maciej Maczynski are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::Category;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Testopia::Product;
|
||||
|
||||
use JSON;
|
||||
|
||||
use base qw(Exporter Bugzilla::Object);
|
||||
@Bugzilla::Testopia::Category::EXPORT = qw(check_case_category);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
use constant DB_TABLE => "test_case_categories";
|
||||
use constant NAME_FIELD => "name";
|
||||
use constant ID_FIELD => "category_id";
|
||||
use constant DB_COLUMNS => qw(
|
||||
category_id
|
||||
product_id
|
||||
name
|
||||
description
|
||||
);
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(product_id name);
|
||||
use constant UPDATE_COLUMNS => qw(name description);
|
||||
|
||||
use constant VALIDATORS => {
|
||||
product_id => \&_check_product,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Validators ####
|
||||
###############################
|
||||
sub _check_product {
|
||||
my ($invocant, $product_id) = @_;
|
||||
$product_id = trim($product_id);
|
||||
|
||||
ThrowUserError("testopia-create-denied", {'object' => 'category'}) unless Bugzilla->user->in_group('Testers');
|
||||
|
||||
my $product;
|
||||
if (trim($product_id) !~ /^\d+$/ ){
|
||||
$product = Bugzilla::Product::check_product($product_id);
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Testopia::Product->new($product_id);
|
||||
}
|
||||
|
||||
|
||||
if (ref $invocant){
|
||||
$invocant->{'product'} = $product;
|
||||
return $product->id;
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub _check_name {
|
||||
my ($invocant, $name, $product) = @_;
|
||||
$name = clean_text($name) if $name;
|
||||
|
||||
if (!defined $name || $name eq '') {
|
||||
ThrowUserError('testopia-missing-required-field', {'field' => 'name'});
|
||||
}
|
||||
|
||||
trick_taint($name);
|
||||
|
||||
# Check that we don't already have a build with that name in this product.
|
||||
my $orig_id = check_case_category($name, $product);
|
||||
my $notunique;
|
||||
|
||||
if (ref $invocant){
|
||||
# If updating, we have matched ourself at least
|
||||
$notunique = 1 if (($orig_id && $orig_id != $invocant->id))
|
||||
}
|
||||
else {
|
||||
# In new build any match is one too many
|
||||
$notunique = 1 if $orig_id;
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-name-not-unique',
|
||||
{'object' => 'Case Category',
|
||||
'name' => $name}) if $notunique;
|
||||
|
||||
return $name;
|
||||
}
|
||||
##############################
|
||||
#### Mutators ####
|
||||
###############################
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_name {
|
||||
my ($self, $value) = @_;
|
||||
$value = $self->_check_name($value, $self->product);
|
||||
$self->set('name', $value);
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $param = shift;
|
||||
|
||||
# We want to be able to supply an empty object to the templates for numerous
|
||||
# lists etc. This is much cleaner than exporting a bunch of subroutines and
|
||||
# adding them to $vars one by one. Probably just Laziness shining through.
|
||||
if (ref $param eq 'HASH'){
|
||||
if (!keys %$param || $param->{PREVALIDATED}){
|
||||
bless($param, $class);
|
||||
return $param;
|
||||
}
|
||||
}
|
||||
|
||||
unshift @_, $param;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub run_create_validators {
|
||||
my $class = shift;
|
||||
my $params = $class->SUPER::run_create_validators(@_);
|
||||
my $product = $params->{product_id};
|
||||
|
||||
$params->{name} = $class->_check_name($params->{name}, $product);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub create {
|
||||
my ($class, $params) = @_;
|
||||
|
||||
$class->SUPER::check_required_create_fields($params);
|
||||
my $field_values = $class->run_create_validators($params);
|
||||
|
||||
$field_values->{product_id} = $field_values->{product_id}->id;
|
||||
my $self = $class->SUPER::insert_create_data($field_values);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Functions ####
|
||||
###############################
|
||||
sub check_case_category {
|
||||
my ($name, $product) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $is = $dbh->selectrow_array(
|
||||
"SELECT category_id FROM test_case_categories
|
||||
WHERE name = ? AND product_id = ?",
|
||||
undef, $name, $product->id);
|
||||
return $is;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
sub store {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
# Exclude the auto-incremented field from the column list.
|
||||
my $columns = join(", ", grep {$_ ne 'category_id'} DB_COLUMNS);
|
||||
|
||||
$dbh->do("INSERT INTO test_case_categories ($columns) VALUES (?,?,?)",
|
||||
undef, ($self->{'product_id'}, $self->{'name'}, $self->{'description'}));
|
||||
my $key = $dbh->bz_last_key( 'test_case_categories', 'category_id' );
|
||||
return $key;
|
||||
}
|
||||
|
||||
sub TO_JSON {
|
||||
my $self = shift;
|
||||
my $obj;
|
||||
my $json = new JSON;
|
||||
|
||||
foreach my $field ($self->DB_COLUMNS){
|
||||
$obj->{$field} = $self->{$field};
|
||||
}
|
||||
|
||||
return $json->encode($obj);
|
||||
}
|
||||
|
||||
sub remove {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("DELETE FROM test_case_categories
|
||||
WHERE category_id = ?", undef,
|
||||
$self->{'category_id'});
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 0 unless Bugzilla->user->in_group('Testers');
|
||||
return 0 if ($self->case_count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
sub id { return $_[0]->{'category_id'}; }
|
||||
sub product_id { return $_[0]->{'product_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub description { return $_[0]->{'description'}; }
|
||||
|
||||
sub product {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'product'} if exists $self->{'product'};
|
||||
|
||||
$self->{'product'} = Bugzilla::Testopia::Product->new($self->product_id);
|
||||
return $self->{'product'};
|
||||
}
|
||||
|
||||
sub case_count {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'case_count'} if exists $self->{'case_count'};
|
||||
|
||||
my ($count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(case_id)
|
||||
FROM test_cases
|
||||
WHERE category_id = ?",
|
||||
undef, $self->{'category_id'});
|
||||
$self->{'case_count'} = $count;
|
||||
return $self->{'case_count'};
|
||||
}
|
||||
|
||||
sub plan_case_ids {
|
||||
my ($self, $plan_id) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'case_ids'} if exists $self->{'case_ids'};
|
||||
|
||||
$self->{'case_ids'} = $dbh->selectcol_arrayref(
|
||||
"SELECT DISTINCT test_cases.case_id
|
||||
FROM test_cases
|
||||
INNER JOIN test_case_plans ON test_case_plans.case_id = test_cases.case_id
|
||||
WHERE category_id = ? AND test_case_plans.plan_id = ?",
|
||||
undef, ($self->{'category_id'}, $plan_id));
|
||||
|
||||
return $self->{'case_ids'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Category - An object representing a test case category
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Object
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Categories are used to classify test cases. Each test case must
|
||||
belong to one category. Categories are product level attributes.
|
||||
Every plan in a product will have access to that product's categories.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head2 Creating
|
||||
|
||||
$category = Bugzilla::Testopia::Category->new($category_id);
|
||||
$category = Bugzilla::Testopia::Category->new({name => $name});
|
||||
|
||||
$new_category = Bugzilla::Testopia::Category->create({name => $name,
|
||||
description => $desc});
|
||||
|
||||
=head2 Updating
|
||||
|
||||
$category->set_name($name);
|
||||
$category->set_description($name);
|
||||
|
||||
$category->update();
|
||||
|
||||
=head2 Accessors
|
||||
|
||||
my $id = $category->id;
|
||||
my $name = $category->name;
|
||||
my $desc = $category->description;
|
||||
my $c_cont = $category->case_count;
|
||||
my $pid = $category->product_id;
|
||||
my $case_ids = $category->plan_case_ids;
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
Table test_case_categories
|
||||
+-------------+----------------------+------+-----+---------+----------------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+-------------+----------------------+------+-----+---------+----------------+
|
||||
| category_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| product_id | smallint(6) | NO | MUL | 0 | |
|
||||
| name | varchar(240) | NO | MUL | | |
|
||||
| description | mediumtext | YES | | NULL | |
|
||||
+-------------+----------------------+------+-----+---------+----------------+
|
||||
|
||||
=over
|
||||
|
||||
=item C<category_id>
|
||||
|
||||
The unique id in the database.
|
||||
|
||||
=item C<product_id>
|
||||
|
||||
The product id of the Bugzilla product this category belongs to.
|
||||
|
||||
=item C<name>
|
||||
|
||||
A unique name for this category
|
||||
|
||||
=item C<description>
|
||||
|
||||
A detailed description for this category.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_case_category($param)>
|
||||
|
||||
Description: Checks if a category of a given name exists for a given product.
|
||||
|
||||
Params: name - string representing the name to check for.
|
||||
product_id - the product to lookup the category in.
|
||||
|
||||
Returns: The id of the category if one matches.
|
||||
undef if it does not match any category.
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<new($param)>
|
||||
|
||||
Description: Used to load an existing Category from the database.
|
||||
|
||||
Params: $param - An integer representing the Category ID in the database
|
||||
or a hash with the "name" key representing the named
|
||||
category in the database.
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Category object
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<candelete()>
|
||||
|
||||
Description: Tests to see if the current category can be safely deleted from
|
||||
the database. To be a candidate for removal, there can be no
|
||||
assigned test cases with this category. Also, the user must be in
|
||||
the Testers group.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: 1 if this category can be safely removed.
|
||||
0 if this category cannot be removed safely or if the logged in user
|
||||
does not have sufficient rights to perform the operation.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<create()>
|
||||
|
||||
Description: Creates a new category object and stores it in the database
|
||||
|
||||
Params: A hash with keys and values matching the fields of the category to
|
||||
be created.
|
||||
|
||||
Returns: The newly created object
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<plan_case_ids()>
|
||||
|
||||
Description: Looks up the case ids assigned to this category in a given plan.
|
||||
|
||||
Params: The plan id to look up.
|
||||
|
||||
Returns: Integer representing the count of cases found with this category.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<remove()>
|
||||
|
||||
Description: Completely removes this category from the database. This should not
|
||||
be called unless candelete has returned true.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<set_description()>
|
||||
|
||||
Description: Replaces the current category's description. Must call update to
|
||||
store the change in the database.
|
||||
|
||||
Params: text - the new description.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<set_name()>
|
||||
|
||||
Description: Renames the current category. If the new name is already in use
|
||||
by another category in this product, an error will be thrown.
|
||||
The update method must be called to make the change in the database.
|
||||
|
||||
Params: string - the new name
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<to_json()>
|
||||
|
||||
Description: Outputs a JSON representation of the object.
|
||||
|
||||
Params: none
|
||||
|
||||
Returns: A JSON string.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=over
|
||||
|
||||
=item C<case_count()>
|
||||
|
||||
Returns an integer representing the number of cases found in this category.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<description()>
|
||||
|
||||
Returns the description of the category.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<id()>
|
||||
|
||||
Returns the category id
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<name()>
|
||||
|
||||
Returns the name of the category.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item C<product_id()>
|
||||
|
||||
Returns the id of the product this category belongs to
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::TestCase>
|
||||
|
||||
L<Bugzilla::Testopia::Product>
|
||||
|
||||
L<Bugzilla::Object>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,92 @@
|
|||
# -*- 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>
|
||||
# Andrew Nelson <anelson@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::Classification;
|
||||
|
||||
use strict;
|
||||
use JSON;
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Config;
|
||||
|
||||
# Extends Bugzilla::Classification;
|
||||
use base "Bugzilla::Classification";
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
sub user_visible_products {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!$self->{'products'}) {
|
||||
my $query = "SELECT id FROM products " .
|
||||
"LEFT JOIN group_control_map " .
|
||||
"ON group_control_map.product_id = products.id ";
|
||||
if (Bugzilla->params->{'useentrygroupdefault'}) {
|
||||
$query .= "AND group_control_map.entry != 0 ";
|
||||
} else {
|
||||
$query .= "AND group_control_map.membercontrol = " .
|
||||
CONTROLMAPMANDATORY . " ";
|
||||
}
|
||||
if (%{Bugzilla->user->groups}) {
|
||||
$query .= "AND group_id NOT IN(" .
|
||||
join(',', values(%{Bugzilla->user->groups})) . ") ";
|
||||
}
|
||||
$query .= "WHERE group_id IS NULL AND products.classification_id= ? ORDER BY products.name";
|
||||
|
||||
my $product_ids = $dbh->selectcol_arrayref($query, undef, $self->id);
|
||||
|
||||
my @products;
|
||||
foreach my $product_id (@$product_ids) {
|
||||
push (@products, new Bugzilla::Testopia::Product($product_id));
|
||||
}
|
||||
$self->{'user_visible_products'} = \@products;
|
||||
}
|
||||
return $self->{'user_visible_products'};
|
||||
}
|
||||
|
||||
sub products_to_json {
|
||||
my $self = shift;
|
||||
my ($disable_move) = @_;
|
||||
my $json = new JSON;
|
||||
|
||||
$disable_move ||= '';
|
||||
$disable_move = ',"addChild","move"' if $disable_move;
|
||||
my $products = $self->user_visible_products;
|
||||
|
||||
my @values;
|
||||
|
||||
foreach my $product (@$products){
|
||||
my $leaf;
|
||||
|
||||
if(scalar @{$product->environment_categories}> 0){
|
||||
$leaf = JSON::false;
|
||||
}
|
||||
else{
|
||||
$leaf = JSON::true;
|
||||
}
|
||||
push @values, {text=> $product->{'name'}, id=> $product->{'id'} . ' product', type=> 'product', leaf=>$leaf, draggable => JSON::false, cls => 'product'};
|
||||
}
|
||||
|
||||
return $json->encode(\@values);
|
||||
|
||||
}
|
||||
1;
|
|
@ -0,0 +1,128 @@
|
|||
# -*- 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>
|
||||
|
||||
package Bugzilla::Testopia::Constants;
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
|
||||
@Bugzilla::Testopia::Constants::EXPORT = qw(
|
||||
PROPOSED
|
||||
CONFIRMED
|
||||
DISABLED
|
||||
|
||||
IDLE
|
||||
PASSED
|
||||
FAILED
|
||||
RUNNING
|
||||
PAUSED
|
||||
BLOCKED
|
||||
ERROR
|
||||
|
||||
TR_READ
|
||||
TR_WRITE
|
||||
TR_DELETE
|
||||
TR_ADMIN
|
||||
|
||||
REL_AUTHOR
|
||||
REL_EDITOR
|
||||
REL_TESTER
|
||||
REL_TEST_CC
|
||||
|
||||
TR_RELATIONSHIPS
|
||||
|
||||
CASE_RUN_STATUSES
|
||||
|
||||
SAVED_SEARCH
|
||||
SAVED_REPORT
|
||||
SAVED_FILTER
|
||||
SAVED_DASHBORD
|
||||
|
||||
);
|
||||
|
||||
#
|
||||
# Fields to include when exporting a Test Case.
|
||||
#
|
||||
# All _id fields but case_id are converted to a string representation.
|
||||
#
|
||||
@Bugzilla::Testopia::Constants::TESTCASE_EXPORT = qw(
|
||||
case_id
|
||||
summary
|
||||
set_up
|
||||
break_down
|
||||
action
|
||||
expected_results
|
||||
alias
|
||||
arguments
|
||||
author_id
|
||||
blocks
|
||||
case_status_id
|
||||
category_id
|
||||
components
|
||||
creation_date
|
||||
default_tester_id
|
||||
depends_on
|
||||
isautomated
|
||||
plans
|
||||
priority_id
|
||||
requirement
|
||||
script
|
||||
tags
|
||||
version
|
||||
);
|
||||
|
||||
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
|
||||
|
||||
# Test Case Status
|
||||
use constant PROPOSED => 1;
|
||||
use constant CONFIRMED => 2;
|
||||
use constant DISABLED => 3;
|
||||
|
||||
# Test case Run Status
|
||||
use constant IDLE => 1;
|
||||
use constant PASSED => 2;
|
||||
use constant FAILED => 3;
|
||||
use constant RUNNING => 4;
|
||||
use constant PAUSED => 5;
|
||||
use constant BLOCKED => 6;
|
||||
use constant ERROR => 7;
|
||||
|
||||
use constant CASE_RUN_STATUSES => IDLE, PASSED, FAILED, RUNNING, PAUSED, BLOCKED, ERROR;
|
||||
|
||||
# Test Plan Permissions (bit flags)
|
||||
use constant TR_READ => 1;
|
||||
use constant TR_WRITE => 2;
|
||||
use constant TR_DELETE => 4;
|
||||
use constant TR_ADMIN => 8;
|
||||
|
||||
# Save search types
|
||||
use constant SAVED_SEARCH => 0;
|
||||
use constant SAVED_REPORT => 1;
|
||||
use constant SAVED_FILTER => 2;
|
||||
use constant SAVED_DASHBORD => 3;
|
||||
|
||||
# Testopia Relationships
|
||||
use constant REL_AUTHOR => 100;
|
||||
use constant REL_EDITOR => 101;
|
||||
use constant REL_TESTER => 102;
|
||||
use constant REL_TEST_CC => 103;
|
||||
|
||||
use constant RELATIONSHIPS => REL_AUTHOR, REL_EDITOR, REL_TESTER, REL_TEST_CC;
|
||||
|
||||
1;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,544 @@
|
|||
# -*- 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>
|
||||
# Michael Hight <mjhight@gmail.com>
|
||||
# Garrett Braden <gbraden@novell.c
|
||||
# Andrew Nelson <anelson@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Environment::Category - A test element category
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Categories are used to organize environment elements.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$prop = Bugzilla::Testopia::Environment::Category->new($env_category_id);
|
||||
$prop = Bugzilla::Testopia::Environment::Category->new(\%cat_hash);
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Environment::Category;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Testopia::Environment::Element;
|
||||
use Bugzilla::Testopia::Product;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
env_category_id
|
||||
product_id
|
||||
name
|
||||
|
||||
=cut
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
env_category_id
|
||||
product_id
|
||||
name
|
||||
);
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new Category object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $self = {};
|
||||
bless( $self, $class );
|
||||
return $self->_init(@_);
|
||||
}
|
||||
|
||||
=head2 _init
|
||||
|
||||
Private constructor for the category class
|
||||
|
||||
=cut
|
||||
|
||||
sub _init {
|
||||
my $self = shift;
|
||||
my ($param) = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $columns = join( ", ", DB_COLUMNS );
|
||||
|
||||
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_environment_category
|
||||
WHERE env_category_id = ?}, undef, $id
|
||||
);
|
||||
}
|
||||
elsif ( ref $param eq 'HASH' ) {
|
||||
$obj = $param;
|
||||
}
|
||||
|
||||
return undef unless ( defined $obj );
|
||||
|
||||
foreach my $field ( keys %$obj ) {
|
||||
$self->{$field} = $obj->{$field};
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
=head2 get element list by category
|
||||
|
||||
Returns an array of element objects for a category
|
||||
|
||||
=cut
|
||||
|
||||
sub get_elements_by_category {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
qq{
|
||||
SELECT element_id
|
||||
FROM test_environment_element
|
||||
WHERE env_category_id = ?}, undef, $self->{'env_category_id'}
|
||||
);
|
||||
|
||||
my @elements;
|
||||
|
||||
foreach my $val (@$ref) {
|
||||
push @elements, Bugzilla::Testopia::Environment::Element->new($val);
|
||||
}
|
||||
|
||||
return \@elements;
|
||||
}
|
||||
|
||||
=head2 get_parent_elements
|
||||
|
||||
Returns an array of parent elements by category
|
||||
|
||||
=cut
|
||||
|
||||
sub get_parent_elements {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
qq{
|
||||
SELECT element_id
|
||||
FROM test_environment_element
|
||||
WHERE env_category_id = ? AND (parent_id is null or parent_id = 0) },
|
||||
undef, $self->{'env_category_id'}
|
||||
);
|
||||
|
||||
my @elements;
|
||||
|
||||
foreach my $val (@$ref) {
|
||||
push @elements, Bugzilla::Testopia::Environment::Element->new($val);
|
||||
}
|
||||
|
||||
return \@elements;
|
||||
}
|
||||
|
||||
=head2 check_for_elements
|
||||
|
||||
Returns 1 if a category has any elements
|
||||
|
||||
=cut
|
||||
|
||||
sub check_for_elements {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ref = $dbh->selectrow_array(
|
||||
qq{
|
||||
SELECT 1
|
||||
FROM test_environment_element
|
||||
WHERE env_category_id = ?}, undef, $self->{'env_category_id'}
|
||||
);
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub is_mapped {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($ref) = $dbh->selectrow_array(
|
||||
"SELECT tee.element_id
|
||||
FROM test_environment_map tem
|
||||
INNER JOIN test_environment_element tee on tee.element_id = tem.element_id
|
||||
WHERE tee.env_category_id = ?", undef, $self->id
|
||||
);
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
|
||||
=head2 get_product_list
|
||||
|
||||
Returns the product_id, product name, and count of categories
|
||||
|
||||
=cut
|
||||
|
||||
sub get_env_product_list {
|
||||
my $self = shift;
|
||||
my ($class_id) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $query = "SELECT p.id, p.name, COUNT(tec.env_category_id) AS cat_count
|
||||
FROM products p
|
||||
LEFT JOIN group_control_map
|
||||
ON group_control_map.product_id = p.id ";
|
||||
|
||||
if ( Bugzilla->params->{'useentrygroupdefault'} ) {
|
||||
$query .= "AND group_control_map.entry != 0 ";
|
||||
}
|
||||
else {
|
||||
$query .=
|
||||
"AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . " ";
|
||||
}
|
||||
if ( %{ Bugzilla->user->groups } ) {
|
||||
$query .= "AND group_id NOT IN("
|
||||
. join( ',', values( %{ Bugzilla->user->groups } ) ) . ") ";
|
||||
}
|
||||
|
||||
$query .= "LEFT OUTER JOIN test_environment_category AS tec
|
||||
ON p.id = tec.product_id ";
|
||||
$query .= "WHERE group_id IS NULL ";
|
||||
$query .= "AND classification_id = ? " if $class_id;
|
||||
$query .= $dbh->sql_group_by( "p.id", "p.name" );
|
||||
$query .= " ORDER BY p.name";
|
||||
|
||||
my $ref;
|
||||
if ($class_id) {
|
||||
$ref = $dbh->selectall_arrayref( $query, { 'Slice' => {} }, $class_id );
|
||||
}
|
||||
else {
|
||||
$ref = $dbh->selectall_arrayref( $query, { 'Slice' => {} } );
|
||||
}
|
||||
unshift @$ref,
|
||||
{
|
||||
'id' => 0,
|
||||
'name' => '--ANY PRODUCT--',
|
||||
'cat_count' => $self->get_all_child_count
|
||||
};
|
||||
return $ref;
|
||||
|
||||
}
|
||||
|
||||
sub get_all_child_count {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($all_count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_environment_category
|
||||
WHERE product_id = 0"
|
||||
);
|
||||
|
||||
return $all_count;
|
||||
}
|
||||
|
||||
sub product_categories_to_json {
|
||||
my $self = shift;
|
||||
my ( $product_id ) = @_;
|
||||
detaint_natural($product_id);
|
||||
my $json = new JSON;
|
||||
|
||||
my $categories = $self->get_element_categories_by_product($product_id);
|
||||
|
||||
my @values;
|
||||
|
||||
foreach my $cat (@$categories) {
|
||||
push @values,
|
||||
{
|
||||
text => $cat->{'name'},
|
||||
id => $cat->id,
|
||||
leaf => $cat->check_for_elements ? JSON::false : JSON::true,
|
||||
type => 'category',
|
||||
cls => 'category'
|
||||
};
|
||||
}
|
||||
|
||||
return $json->encode( \@values );
|
||||
}
|
||||
|
||||
=head2 get_element_categories_by_product
|
||||
|
||||
Returns the list of element category names and ids by product id
|
||||
|
||||
=cut
|
||||
|
||||
sub get_element_categories_by_product {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($product_id) = (@_);
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
"SELECT env_category_id
|
||||
FROM test_environment_category
|
||||
WHERE product_id = ?",
|
||||
undef, $product_id
|
||||
);
|
||||
my @objs;
|
||||
foreach my $id ( @{$ref} ) {
|
||||
push @objs, Bugzilla::Testopia::Environment::Category->new($id);
|
||||
}
|
||||
return \@objs;
|
||||
}
|
||||
|
||||
=head2 new_category_count
|
||||
|
||||
Returns 1 if element has children
|
||||
|
||||
=cut
|
||||
|
||||
sub new_category_count {
|
||||
my $self = shift;
|
||||
my ($prod_id) = @_;
|
||||
$prod_id ||= $self->{'product_id'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_environment_category
|
||||
WHERE name like 'New category%'
|
||||
AND product_id = ?",
|
||||
undef, $prod_id
|
||||
);
|
||||
|
||||
return $used + 1;
|
||||
}
|
||||
|
||||
sub elements_to_json {
|
||||
my $self = shift;
|
||||
|
||||
my @values;
|
||||
foreach my $element (@{$self->get_parent_elements}) {
|
||||
push @values,
|
||||
{
|
||||
text => $element->name,
|
||||
id => $element->id,
|
||||
type => 'element',
|
||||
leaf => $element->check_for_children ? JSON::false : JSON::true,
|
||||
cls => 'element'
|
||||
};
|
||||
}
|
||||
|
||||
my $json = new JSON();
|
||||
return $json->encode( \@values );
|
||||
}
|
||||
|
||||
=head2 check_category
|
||||
|
||||
Returns category id if a category exists
|
||||
|
||||
=cut
|
||||
|
||||
sub check_category {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
my ( $name, $prodID ) = (@_);
|
||||
|
||||
$prodID ||= $self->product_id;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
"SELECT env_category_id
|
||||
FROM test_environment_category
|
||||
WHERE name = ? AND product_id = ?",
|
||||
undef, ( $name, $prodID )
|
||||
);
|
||||
|
||||
return $used;
|
||||
}
|
||||
|
||||
=head2 store
|
||||
|
||||
Serializes this category to the database and returns the key or 0
|
||||
|
||||
=cut
|
||||
|
||||
sub store {
|
||||
my $self = shift;
|
||||
|
||||
# Exclude the auto-incremented field from the column list.
|
||||
my $columns = join( ", ", grep { $_ ne 'env_category_id' } DB_COLUMNS );
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
return 0 if $self->check_category( $self->{'name'}, $self->{'product_id'} );
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do( "INSERT INTO test_environment_category ($columns) VALUES (?, ?)",
|
||||
undef, ( $self->{'product_id'}, $self->{'name'} ) );
|
||||
my $key =
|
||||
$dbh->bz_last_key( 'test_environment_category', 'env_category_id' );
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
=head2 set_name
|
||||
|
||||
Updates the category name in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub set_name {
|
||||
my $self = shift;
|
||||
my ($name) = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
return undef if $self->check_category($name);
|
||||
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_category SET name = ?
|
||||
WHERE env_category_id = ? AND product_id = ?",
|
||||
undef, ( $name, $self->{'env_category_id'}, $self->{'product_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 set_product
|
||||
|
||||
Updates the category in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub set_product {
|
||||
my $self = shift;
|
||||
my ($product_id) = (@_);
|
||||
|
||||
return if ( $product_id == $self->{'product_id'} );
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_category SET product_id = ?
|
||||
WHERE env_category_id = ? AND product_id = ?",
|
||||
undef,
|
||||
( $product_id, $self->{'env_category_id'}, $self->{'product_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 obliterate
|
||||
|
||||
Completely removes the element category entry from the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub obliterate {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $children = $dbh->selectcol_arrayref(
|
||||
"SELECT element_id FROM test_environment_element
|
||||
WHERE env_category_id = ?", undef, $self->id
|
||||
);
|
||||
|
||||
foreach my $id (@$children) {
|
||||
my $element = Bugzilla::Testopia::Environment::Element->new($id);
|
||||
$element->obliterate;
|
||||
}
|
||||
$dbh->do( "DELETE FROM test_environment_category WHERE env_category_id = ?",
|
||||
undef, $self->id );
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
sub canview {
|
||||
my $self = shift;
|
||||
return 1 if ( $self->product_id == 0 );
|
||||
return 1 if Bugzilla->user->can_see_product( $self->product->name );
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub canedit {
|
||||
my $self = shift;
|
||||
if ( $self->product_id == 0 ) {
|
||||
return 1 if Bugzilla->user->in_group('Testers');
|
||||
return 0;
|
||||
}
|
||||
return 1 if $self->product->canedit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 0 unless $self->canedit;
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
=head2 id
|
||||
|
||||
Returns the ID of this category
|
||||
|
||||
=head2 name
|
||||
|
||||
Returns the name of this category
|
||||
|
||||
=head2 product_id
|
||||
|
||||
Returns the product_id of this category
|
||||
|
||||
=cut
|
||||
|
||||
sub id { return $_[0]->{'env_category_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub product_id { return $_[0]->{'product_id'}; }
|
||||
|
||||
sub product {
|
||||
my $self = shift;
|
||||
|
||||
$self->{'product'} = Bugzilla::Testopia::Product->new( $self->product_id );
|
||||
return $self->{'product'};
|
||||
}
|
||||
|
||||
=head2 type
|
||||
|
||||
Returns 'env_category'
|
||||
|
||||
=cut
|
||||
|
||||
sub type {
|
||||
my $self = shift;
|
||||
$self->{'type'} = 'env_category';
|
||||
return $self->{'type'};
|
||||
}
|
||||
1;
|
|
@ -0,0 +1,573 @@
|
|||
# -*- 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>
|
||||
# Michael Hight <mjhight@gmail.com>
|
||||
# Garrett Braden <gbraden@novell.com>
|
||||
# Andrew Nelson <anelson@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Environment::Element - A test environment element
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Environment elements are a set of environment entities that dictate
|
||||
the conditions a test was conducted in. Each environment must have an
|
||||
element. Elements can be very simple or very complex by adding properties.
|
||||
Elements can have child elements.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$elem = Bugzilla::Testopia::Environment::Element->new($elem_id);
|
||||
$elem = Bugzilla::Testopia::Environment::Element->new(\%elem_hash);
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Environment::Element;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Testopia::Product;
|
||||
use JSON;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
element_id
|
||||
env_category_id
|
||||
name
|
||||
parent_id
|
||||
isprivate
|
||||
|
||||
=cut
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
element_id
|
||||
env_category_id
|
||||
name
|
||||
parent_id
|
||||
isprivate
|
||||
);
|
||||
|
||||
our constant $max_depth = 5;
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new Element object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $self = {};
|
||||
bless( $self, $class );
|
||||
return $self->_init(@_);
|
||||
}
|
||||
|
||||
=head2 _init
|
||||
|
||||
Private constructor for the element class
|
||||
|
||||
=cut
|
||||
|
||||
sub _init {
|
||||
my $self = shift;
|
||||
my ($param) = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $columns = join( ", ", DB_COLUMNS );
|
||||
|
||||
my $id = $param unless ( ref $param eq 'HASH' );
|
||||
my $obj;
|
||||
|
||||
if ( defined $id && detaint_natural($id) ) {
|
||||
|
||||
$obj = $dbh->selectrow_hashref(
|
||||
"SELECT $columns
|
||||
FROM test_environment_element
|
||||
WHERE element_id = ?", undef, $id
|
||||
);
|
||||
}
|
||||
elsif ( ref $param eq 'HASH' ) {
|
||||
$obj = $param;
|
||||
}
|
||||
|
||||
return undef unless ( defined $obj );
|
||||
|
||||
foreach my $field ( keys %$obj ) {
|
||||
$self->{$field} = $obj->{$field};
|
||||
}
|
||||
|
||||
$self->get_properties();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub is_mapped {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($ref) = $dbh->selectrow_array(
|
||||
"SELECT element_id
|
||||
FROM test_environment_map
|
||||
WHERE element_id = ?", undef, $self->id
|
||||
);
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
=head2 get_child_elements
|
||||
|
||||
Returns an array of the children objects for an element and recursively
|
||||
gets all of their children until exhausted.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_child_elements {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
|
||||
return $self->{'children'} if exists $self->{'children'};
|
||||
|
||||
my %newvalues = (@_);
|
||||
my $depth = $max_depth;
|
||||
|
||||
if (%newvalues) {
|
||||
$depth = $newvalues{'depth'};
|
||||
}
|
||||
|
||||
$depth--;
|
||||
|
||||
if ( $depth == 0 ) { return; }
|
||||
|
||||
my $id = $self->{'element_id'};
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
qq{
|
||||
SELECT tee.element_id
|
||||
FROM test_environment_element as tee
|
||||
WHERE tee.parent_id = ?}, undef, $id
|
||||
);
|
||||
|
||||
my @children;
|
||||
|
||||
foreach my $val (@$ref) {
|
||||
my $child = Bugzilla::Testopia::Environment::Element->new($val);
|
||||
$child->get_child_elements( 'depth' => $depth );
|
||||
push( @children, $child );
|
||||
}
|
||||
|
||||
$self->{'children'} = \@children;
|
||||
|
||||
}
|
||||
|
||||
=head2 get_properties
|
||||
|
||||
Returns an array of the property objects for an element.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_properties {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
qq{
|
||||
SELECT tep.property_id
|
||||
FROM test_environment_property as tep
|
||||
WHERE tep.element_id = ?}, undef, ( $self->{'element_id'} )
|
||||
);
|
||||
|
||||
my @properties;
|
||||
|
||||
foreach my $val (@$ref) {
|
||||
my $property = Bugzilla::Testopia::Environment::Property->new($val);
|
||||
push( @properties, $property );
|
||||
}
|
||||
|
||||
$self->{'properties'} = \@properties;
|
||||
return $self->{'properties'};
|
||||
|
||||
}
|
||||
|
||||
=head2 check_element
|
||||
|
||||
Returns element id if element exists
|
||||
|
||||
=cut
|
||||
|
||||
sub check_element {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
my ( $name, $cat_id ) = @_;
|
||||
|
||||
# Since categories are uniquely identified by product_id we don't have to check by join on the product_id.
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
"SELECT element_id
|
||||
FROM test_environment_element
|
||||
WHERE name = ?
|
||||
AND env_category_id = ?",
|
||||
undef, ( $name, $cat_id )
|
||||
);
|
||||
|
||||
return $used;
|
||||
}
|
||||
|
||||
=head2 check_for_children
|
||||
|
||||
Returns 1 if element has children
|
||||
|
||||
=cut
|
||||
|
||||
sub check_for_children {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
|
||||
my ($has_element) = $dbh->selectrow_array(
|
||||
"SELECT 1
|
||||
FROM test_environment_element
|
||||
WHERE parent_id = ?",
|
||||
undef, $self->{'element_id'}
|
||||
);
|
||||
|
||||
my ($has_property) = $dbh->selectrow_array(
|
||||
"SELECT 1
|
||||
FROM test_environment_property
|
||||
WHERE element_id = ?",
|
||||
undef, $self->{'element_id'}
|
||||
);
|
||||
|
||||
return $has_element || $has_property;
|
||||
}
|
||||
|
||||
sub children_to_json {
|
||||
my $self = shift;
|
||||
my $json = new JSON;
|
||||
|
||||
my @values;
|
||||
foreach my $element ( @{ $self->get_child_elements } ) {
|
||||
push @values,
|
||||
{
|
||||
text => $element->{'name'},
|
||||
id => $element->{'element_id'},
|
||||
leaf => $element->check_for_children ? JSON::false : JSON::true,
|
||||
type => 'element',
|
||||
cls => 'element',
|
||||
draggable => JSON::true,
|
||||
};
|
||||
}
|
||||
|
||||
foreach my $property (@{$self->get_properties}) {
|
||||
push @values,
|
||||
{
|
||||
text => $property->{'name'},
|
||||
id => $property->id,
|
||||
leaf => $property->check_for_validexp ? JSON::false : JSON::true,
|
||||
type => 'property',
|
||||
cls => 'property',
|
||||
draggable => JSON::false,
|
||||
};
|
||||
}
|
||||
|
||||
return $json->encode( \@values );
|
||||
}
|
||||
|
||||
=head2 new_element_count
|
||||
|
||||
Returns 1 if element has children
|
||||
|
||||
=cut
|
||||
|
||||
sub new_element_count {
|
||||
my $self = shift;
|
||||
my ($cat_id) = @_;
|
||||
$cat_id ||= $self->{'env_category_id'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_environment_element
|
||||
WHERE name like 'New element%'
|
||||
AND env_category_id = ?",
|
||||
undef, $cat_id
|
||||
);
|
||||
|
||||
return $used + 1;
|
||||
}
|
||||
|
||||
sub this_to_json {
|
||||
my $self = shift;
|
||||
|
||||
my $element = {
|
||||
text => $self->{'name'},
|
||||
id => $self->{'element_id'},
|
||||
type => 'element',
|
||||
leaf => JSON::true,
|
||||
cls => 'element',
|
||||
allowDrop => JSON::false,
|
||||
disabled => JSON::true
|
||||
};
|
||||
my $json = new JSON();
|
||||
print $json->encode($element);
|
||||
|
||||
}
|
||||
|
||||
=head2 check_for_properties
|
||||
|
||||
Returns 1 if element has properties
|
||||
|
||||
=cut
|
||||
|
||||
sub check_for_properties {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $self = shift;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
qq{
|
||||
SELECT 1
|
||||
FROM test_environment_property
|
||||
WHERE element_id = ? }, undef, $self->{'element_id'}
|
||||
);
|
||||
|
||||
return $used;
|
||||
}
|
||||
|
||||
=head2 store
|
||||
|
||||
Serializes the new element to the database and returns the primary key or 0
|
||||
|
||||
=cut
|
||||
|
||||
sub store {
|
||||
my $self = shift;
|
||||
|
||||
# Exclude the auto-incremented field from the column list.
|
||||
my $columns = join( ", ", grep { $_ ne 'element_id' } DB_COLUMNS );
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
# Verify name is available
|
||||
return undef
|
||||
if $self->check_element( $self->{'name'}, $self->{'env_category_id'} );
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"INSERT INTO test_environment_element ($columns) VALUES (?,?,?,?)",
|
||||
undef,
|
||||
(
|
||||
$self->{'env_category_id'}, $self->{'name'},
|
||||
$self->{'parent_id'}, $self->{'isprivate'}
|
||||
)
|
||||
);
|
||||
my $key = $dbh->bz_last_key( 'test_environment_element', 'element_id' );
|
||||
return $key;
|
||||
}
|
||||
|
||||
=head2 set_name
|
||||
|
||||
Updates the element in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub set_name {
|
||||
my $self = shift;
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my ($name) = (@_);
|
||||
|
||||
return 0 if check_element( $name, $self->{'env_category_id'} );
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_element
|
||||
SET name = ? WHERE element_id = ?", undef,
|
||||
( $name, $self->{'element_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 update_element_category
|
||||
|
||||
Updates the category of the element in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub update_element_category {
|
||||
my $self = shift;
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my ($catid) = (@_);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_element
|
||||
SET env_category_id = ? WHERE element_id = ?", undef,
|
||||
( $catid, $self->{'element_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 update_element_parent
|
||||
|
||||
Updates the parent_id of the element in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub update_element_parent {
|
||||
my $self = shift;
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my ($parent_id) = (@_);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_element
|
||||
SET parent_id = ? WHERE element_id = ?", undef,
|
||||
( $parent_id, $self->{'element_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 obliterate
|
||||
|
||||
Completely removes the element entry from the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub obliterate {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
foreach my $p ( @{ $self->get_properties } ) {
|
||||
$p->obliterate;
|
||||
}
|
||||
|
||||
$dbh->do(
|
||||
"DELETE FROM test_environment_map
|
||||
WHERE element_id = ?", undef, $self->id
|
||||
);
|
||||
$dbh->do(
|
||||
"DELETE FROM test_environment_element
|
||||
WHERE element_id = ?", undef, $self->{'element_id'}
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub canview {
|
||||
my $self = shift;
|
||||
return 1 if $self->get_parent->canview;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub canedit {
|
||||
my $self = shift;
|
||||
return 1 if $self->get_parent->canedit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 0 unless $self->canedit;
|
||||
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
=head2 id
|
||||
|
||||
Returns the ID of this object
|
||||
|
||||
=head2 name
|
||||
|
||||
Returns the name of this object
|
||||
|
||||
=head2 product_id
|
||||
|
||||
Returns the product_id of this object
|
||||
|
||||
=head2 env_category_id
|
||||
|
||||
Returns the category_id associated with this element
|
||||
|
||||
=head2 parent_id
|
||||
|
||||
Returns the element's parent_id associated with this element
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
sub id { return $_[0]->{'element_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub product_id { return $_[0]->{'product_id'}; }
|
||||
sub env_category_id { return $_[0]->{'env_category_id'}; }
|
||||
sub parent_id { return $_[0]->{'parent_id'}; }
|
||||
sub isprivate { return $_[0]->{'isprivate'}; }
|
||||
|
||||
sub get_parent {
|
||||
my $self = shift;
|
||||
if ( $self->{'parent_id'} ) {
|
||||
return $self->new( $self->{'parent_id'} );
|
||||
}
|
||||
else {
|
||||
return Bugzilla::Testopia::Environment::Category->new(
|
||||
$self->{'env_category_id'} );
|
||||
}
|
||||
}
|
||||
|
||||
sub is_parent_a_category {
|
||||
my $self = shift;
|
||||
if ( $self->{'parent_id'} ) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub product {
|
||||
my $self = shift;
|
||||
return $self->{'product'} if exists $self->{'product'};
|
||||
$self->{'product'} = Bugzilla::Testopia::Product->new( $self->product_id );
|
||||
return $self->{'product'};
|
||||
}
|
||||
|
||||
=head2 type
|
||||
|
||||
Returns 'element'
|
||||
|
||||
=cut
|
||||
|
||||
sub type {
|
||||
my $self = shift;
|
||||
$self->{'type'} = 'element';
|
||||
return $self->{'type'};
|
||||
}
|
||||
1;
|
|
@ -0,0 +1,439 @@
|
|||
# -*- 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>
|
||||
# Michael Hight <mjhight@gmail.com>
|
||||
# Garrett Braden <gbraden@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Environment::Property - A test environment element property
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Element properties describe the elements in more detail. An
|
||||
element can have an unlimited number of properties to describe it.
|
||||
The valid expression limits the possible descriptions to valid choices
|
||||
for each property.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$prop = Bugzilla::Testopia::Environment::Property->new($prop_id);
|
||||
$prop = Bugzilla::Testopia::Environment::Property->new(\%prop_hash);
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Environment::Property;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Testopia::Environment::Element;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
property_id
|
||||
element_id
|
||||
name
|
||||
validexp
|
||||
|
||||
=cut
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
property_id
|
||||
element_id
|
||||
name
|
||||
validexp
|
||||
);
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new Property object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $self = {};
|
||||
bless( $self, $class );
|
||||
return $self->_init(@_);
|
||||
}
|
||||
|
||||
=head2 _init
|
||||
|
||||
Private constructor for the property class
|
||||
|
||||
=cut
|
||||
|
||||
sub _init {
|
||||
my $self = shift;
|
||||
my ($param) = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $columns = join( ", ", DB_COLUMNS );
|
||||
|
||||
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_environment_property
|
||||
WHERE property_id = ?}, undef, $id
|
||||
);
|
||||
}
|
||||
elsif ( ref $param eq 'HASH' ) {
|
||||
$obj = $param;
|
||||
}
|
||||
|
||||
return undef unless ( defined $obj );
|
||||
|
||||
foreach my $field ( keys %$obj ) {
|
||||
$self->{$field} = $obj->{$field};
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
=head2 check_property
|
||||
|
||||
Returns id if a property exists
|
||||
|
||||
=cut
|
||||
|
||||
sub check_property {
|
||||
my $self = shift;
|
||||
my ( $name, $element_id ) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
unless ( $name && $element_id ) {
|
||||
return "check_product must be passed a valid name and product_id";
|
||||
}
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
qq{
|
||||
SELECT property_id
|
||||
FROM test_environment_property
|
||||
WHERE name = ? AND element_id = ?}, undef, $name, $element_id
|
||||
);
|
||||
|
||||
return $used;
|
||||
}
|
||||
|
||||
=head2 check_for_validexp
|
||||
|
||||
Returns 1 if a validexp exist for the property
|
||||
|
||||
=cut
|
||||
|
||||
sub check_for_validexp {
|
||||
my $self = shift;
|
||||
my $name = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
qq{
|
||||
SELECT 1
|
||||
FROM test_environment_property
|
||||
WHERE property_id = ? AND (validexp IS NOT NULL AND validexp <> '')},
|
||||
undef, $self->{'property_id'}
|
||||
);
|
||||
|
||||
return $used;
|
||||
}
|
||||
|
||||
=head2 new_property_count
|
||||
|
||||
Returns the count + 1 of new properties
|
||||
|
||||
=cut
|
||||
|
||||
sub new_property_count {
|
||||
my $self = shift;
|
||||
my ($element_id) = @_;
|
||||
$element_id ||= $self->{'element_id'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($used) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*)
|
||||
FROM test_environment_property
|
||||
WHERE name like 'New property%'
|
||||
AND element_id = ?",
|
||||
undef, $element_id
|
||||
);
|
||||
|
||||
return $used + 1;
|
||||
}
|
||||
|
||||
=head2 get_validexp
|
||||
|
||||
Returns the validexp for the property
|
||||
|
||||
=cut
|
||||
|
||||
sub get_validexp {
|
||||
my $self = shift;
|
||||
my $name = (@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($validexp) = $dbh->selectrow_array(
|
||||
"SELECT validexp
|
||||
FROM test_environment_property
|
||||
WHERE property_id = ?",
|
||||
undef, $self->{'property_id'}
|
||||
);
|
||||
|
||||
return $validexp;
|
||||
}
|
||||
|
||||
=head2 store
|
||||
|
||||
Serializes the new property to the database
|
||||
|
||||
=cut
|
||||
|
||||
sub store {
|
||||
my $self = shift;
|
||||
|
||||
# Exclude the auto-incremented field from the column list.
|
||||
my $columns = join( ", ", grep { $_ ne 'property_id' } DB_COLUMNS );
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
# Verify name is available
|
||||
return undef
|
||||
if $self->check_property( $self->{'name'}, $self->{'element_id'} );
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do( "INSERT INTO test_environment_property ($columns) VALUES (?,?,?)",
|
||||
undef,
|
||||
( $self->{'element_id'}, $self->{'name'}, $self->{'validexp'} ) );
|
||||
my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' );
|
||||
return $key;
|
||||
}
|
||||
|
||||
=head2 set_name
|
||||
|
||||
Updates the property name in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub set_name {
|
||||
my $self = shift;
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my ($name) = (@_);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_property
|
||||
SET name = ? WHERE property_id = ?", undef,
|
||||
( $name, $self->{'property_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 set_element
|
||||
|
||||
Updates the elmnt_id in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub set_element {
|
||||
my $self = shift;
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
|
||||
my ($id) = (@_);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_property
|
||||
SET element_id = ? WHERE property_id = ?", undef,
|
||||
( $id, $self->{'property_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 update_property_validexp
|
||||
|
||||
Updates the property valid expression in the database
|
||||
|
||||
=cut
|
||||
|
||||
sub update_property_validexp {
|
||||
my $timestamp = Bugzilla::Testopia::Util::get_time_stamp();
|
||||
my $self = shift;
|
||||
my ($validexp) = (@_);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do(
|
||||
"UPDATE test_environment_property
|
||||
SET validexp = ? WHERE property_id = ?", undef,
|
||||
( $validexp, $self->{'property_id'} )
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub value_to_json {
|
||||
my $self = shift;
|
||||
my ($env_id) = @_;
|
||||
|
||||
my $env = Bugzilla::Testopia::Environment->new($env_id) if $env_id;
|
||||
my @values = split( /\|/, $self->get_validexp );
|
||||
|
||||
my @json;
|
||||
foreach (@values) {
|
||||
my $class;
|
||||
if ( $env
|
||||
&& $env->get_value_selected( $env->id, $self->element_id,
|
||||
$self->id ) eq $_ )
|
||||
{
|
||||
$class = "validexpYellow";
|
||||
}
|
||||
else {
|
||||
$class = "validexp";
|
||||
}
|
||||
push @json,
|
||||
{
|
||||
text => $_,
|
||||
id => " $_",
|
||||
property_id => $self->id,
|
||||
type => 'value',
|
||||
leaf => JSON::true,
|
||||
cls => $class,
|
||||
draggable => JSON::false,
|
||||
};
|
||||
}
|
||||
|
||||
my $json = new JSON;
|
||||
return $json->encode( \@json );
|
||||
|
||||
}
|
||||
|
||||
=head2 obliterate
|
||||
|
||||
Completely removes the element property entry from the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub obliterate {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$dbh->do(
|
||||
"DELETE FROM test_environment_map
|
||||
WHERE property_id = ?", undef, $self->id
|
||||
);
|
||||
$dbh->do(
|
||||
"DELETE FROM test_environment_property
|
||||
WHERE property_id = ?", undef, $self->id
|
||||
);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
sub is_mapped {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($ref) = $dbh->selectrow_array(
|
||||
"SELECT element_id
|
||||
FROM test_environment_map
|
||||
WHERE property_id = ?", undef, $self->id
|
||||
);
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
|
||||
sub canview {
|
||||
my $self = shift;
|
||||
my $element =
|
||||
Bugzilla::Testopia::Environment::Element->new( $self->element_id );
|
||||
return 1 if $element->canview;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub canedit {
|
||||
my $self = shift;
|
||||
my $element =
|
||||
Bugzilla::Testopia::Environment::Element->new( $self->element_id );
|
||||
return 1 if $element->canedit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 0 unless $self->canedit;
|
||||
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
=head2 id
|
||||
|
||||
Returns the ID of this object
|
||||
|
||||
=head2 name
|
||||
|
||||
Returns the name of this object
|
||||
|
||||
=head2 validexp
|
||||
|
||||
Returns the valid expression associated with this property
|
||||
|
||||
=head2 element_id
|
||||
|
||||
Returns the element's id associated with this property
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
sub id { return $_[0]->{'property_id'}; }
|
||||
sub name { return $_[0]->{'name'}; }
|
||||
sub validexp { return $_[0]->{'validexp'}; }
|
||||
sub element_id { return $_[0]->{'element_id'}; }
|
||||
|
||||
=head2 type
|
||||
|
||||
Returns 'property'
|
||||
|
||||
=cut
|
||||
|
||||
sub type {
|
||||
my $self = shift;
|
||||
$self->{'type'} = 'property';
|
||||
return $self->{'type'};
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,585 @@
|
|||
# -*- 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): Garrett Braden <gbraden@novell.com>
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Environment::Xml - An XML representation of the Environment Object.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used to import and export environments via XML. It can parse an XML representation
|
||||
of a Testopia Environment and persist it to the database. An Environment.pm XML object can be
|
||||
initialized using new and passing it an XML scalar. It can also take two other parameters:
|
||||
|
||||
$admin - a boolean that automatically will store the imported environment to the database.
|
||||
$max_depth - the max depth of child elements to import.
|
||||
|
||||
Example:
|
||||
my $env_xml = Bugzilla::Testopia::Environment::Xml->new($xml, 1, 5);
|
||||
|
||||
Other subroutines can be called on the object. For example:
|
||||
parse - takes the same three parameters as new
|
||||
store - stores the imported xml Environment object to the database
|
||||
|
||||
Misc. Other:
|
||||
the module also contains two other valueable fields on it's hash:
|
||||
$self->{'message'} - running list of valueable information upon parsing and storing
|
||||
$self->{'error'} - running list of error messages upon parsing and storing
|
||||
One other method exists(check_new_items) to check if there are new Elements, Properties, Categories,
|
||||
and Selected Values and returns a scalar value report of the new items not present in the database.
|
||||
|
||||
Import XML Environment Implementation Example: see tr_import_environment.cgi
|
||||
|
||||
To export an environment by env_id to XML use export
|
||||
|
||||
Example:
|
||||
my $xml = Bugzilla::Testopia::Environment::Xml->export($env_id);
|
||||
|
||||
Export Environment XML Implementation Example: see tr_export_environment.cgi
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Testopia::Environment::Xml;
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Environment::Xml;
|
||||
|
||||
#************************************************** Uses ****************************************************#
|
||||
use strict;
|
||||
use warnings;
|
||||
use CGI;
|
||||
use lib ".";
|
||||
use XML::Twig;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::Testopia::Util;
|
||||
use Bugzilla::Testopia::Environment;
|
||||
use Bugzilla::Testopia::Product;
|
||||
use Bugzilla::Testopia::Environment::Category;
|
||||
use Bugzilla::Testopia::Environment::Element;
|
||||
use Bugzilla::Testopia::Environment::Property;
|
||||
|
||||
our constant $max_depth = 7;
|
||||
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new Bugzilla::Testopia::Environment::Xml object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
return $self->_init(@_);
|
||||
}
|
||||
|
||||
|
||||
=head2 _init
|
||||
|
||||
Private constructor for the Bugzilla::Testopia::Environment::XML class
|
||||
|
||||
=cut
|
||||
|
||||
sub _init {
|
||||
my ($self, $xml, $admin, $depth) = @_;
|
||||
if (ref $xml eq 'HASH') {
|
||||
$self = $xml;
|
||||
}
|
||||
elsif($xml) {
|
||||
parse($self, $xml, $admin, $depth);
|
||||
}
|
||||
return undef unless (defined $xml);
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
=head2 Parse Environment
|
||||
|
||||
=head2 DESCRIPTION
|
||||
|
||||
Parses the Environment XML
|
||||
|
||||
=cut
|
||||
|
||||
sub parse() {
|
||||
my ($self, $xml, $admin, $depth) = @_;
|
||||
|
||||
if($depth eq undef) {
|
||||
$self->{'max_depth'} = $max_depth;
|
||||
}
|
||||
else {
|
||||
$self->{'max_depth'} = $depth
|
||||
}
|
||||
if ($admin) {
|
||||
$self->{'message'} = "Importing XML Environment...<BR />";
|
||||
}
|
||||
else {
|
||||
$self->{'message'} = "Parsing and Validating XML Environment...<BR />";
|
||||
}
|
||||
$self->{'error'} = undef;
|
||||
if ($xml) {
|
||||
trick_taint($xml);
|
||||
}
|
||||
my $twig = XML::Twig->new();
|
||||
$twig->parse($xml);
|
||||
my $root = $twig->root;
|
||||
# Checking if Product and Environment already exist.
|
||||
my $product_name = $root->{'att'}->{'product'};
|
||||
my $product_id;
|
||||
if (lc($product_name) eq "--all--") {
|
||||
$self->{'message'} .= "..Using the <U>--ANY PRODUCT--</U> <STRONG>PRODUCT</STRONG>.<BR />";
|
||||
$product_id = 0;
|
||||
}
|
||||
else {
|
||||
$self->{'message'} .= "..Checking if <U>$product_name</U> <STRONG>PRODUCT</STRONG> already exists...";
|
||||
($product_id) = Bugzilla::Testopia::Product->check_product_by_name($product_name);
|
||||
if ($product_id) {
|
||||
$self->{'message'} .= "EXISTS.<BR />";
|
||||
}
|
||||
else {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U>.<BR />Importing XML Environment Failed!<BR/>";
|
||||
$self->{'error'} .= "<U>$product_name</U> <STRONG>PRODUCT</STRONG> doesn't exist. Please be sure to use an existing product.<BR />";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
($self->{'product_id'}) = $product_id;
|
||||
$self->{'product_name'} = $product_name;
|
||||
my $environment_name = $root->{'att'}->{'name'};
|
||||
$self->{'name'} = $environment_name;
|
||||
$self->{'message'} .= "..Checking if <U>$environment_name</U> <STRONG>ENVIRONMENT NAME</STRONG> already exists for the <U>$product_name</U> <STRONG>PRODUCT</STRONG>...";
|
||||
my $environment = Bugzilla::Testopia::Environment->new({});
|
||||
my ($env_id) = $environment->check_environment($environment_name, $product_id);
|
||||
my $environment_id;
|
||||
if ($env_id < 1) {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U><BR />";
|
||||
# Storing New Environment if Admin
|
||||
if ($admin) {
|
||||
$self->{'message'} .= "....Storing new <U>$environment_name</U> <STRONG>ENVIRONMENT NAME</STRONG> in the <U>$self->{'product_name'}</U> <STRONG>PRODUCT</STRONG>...";
|
||||
$environment->{'name'} = $environment_name;
|
||||
($environment_id) = Bugzilla::Testopia::Environment->store_environment_name($self->{'name'}, $product_id);
|
||||
$self->{'message'} .= "DONE.<BR />";
|
||||
}
|
||||
}
|
||||
else {
|
||||
($environment_id) = $env_id;
|
||||
$self->{'message'} .= "EXISTS<BR />Importing XML Environment Failed!<BR/>";
|
||||
$self->{'error'} .= "<U>$environment_name</U> <STRONG>ENVIRONMENT NAME</STRONG> already exists for the <U>$product_name</U> <STRONG>PRODUCT</STRONG>. Please use another name.";
|
||||
return 0;
|
||||
}
|
||||
($self->{'environment_id'}) = $environment_id;
|
||||
($environment->{'product_id'}) = $self->{'product_id'};
|
||||
# Parse recursively through the nested child elements.
|
||||
foreach my $twig_category ($root->children("category")) {
|
||||
my $category_name = $twig_category->{'att'}->{'name'};
|
||||
# Makes sure to get the category_id by name and product_id
|
||||
my $category = Bugzilla::Testopia::Environment::Category->new({});
|
||||
my ($cat_id) = $category->check_category($category_name, $product_id);
|
||||
my $category_id;
|
||||
# Checking if Categories already exist.
|
||||
$self->{'message'} .= "..Checking if <U>$category_name</U> <STRONG>CATEGORY</STRONG> already exists...";
|
||||
if ($cat_id < 1) {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U>.<BR />";
|
||||
my $new_category_names = $self->{'new_category_names'};
|
||||
push (@$new_category_names, $category_name);
|
||||
$self->{'new_category_names'} = $new_category_names;
|
||||
# Storing New Categories if Admin
|
||||
if ($admin) {
|
||||
$self->{'message'} .= "....Storing new <U>$category_name</U> <STRONG>CATEGORY</STRONG> in the <U>$self->{'product_name'}</U> <STRONG>PRODUCT</STRONG>...";
|
||||
($category->{'product_id'}) = $product_id;
|
||||
$category->{'name'} = $category_name;
|
||||
($category_id) = $category->store();
|
||||
$self->{'message'} .= "DONE.<BR />";
|
||||
}
|
||||
}
|
||||
else {
|
||||
($category_id) = $cat_id;
|
||||
$self->{'message'} .= "EXISTS.<BR />";
|
||||
}
|
||||
foreach my $twig_element ($twig_category->children("element")) {
|
||||
my $element = $self->parse_child_elements(1, $category_id, $category_name, $twig_element, $admin);
|
||||
my $elements = $self->{'elements'};
|
||||
push (@$elements, $element);
|
||||
$self->{'elements'} = $elements;
|
||||
}
|
||||
}
|
||||
if ($admin) {
|
||||
$self->{'message'} .= "Finished Importing XML Environment!<BR/>";
|
||||
}
|
||||
else {
|
||||
$self->{'message'} .= "Finished Parsing and Validating XML Environment!<BR/>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=head2 Parse Children Elements
|
||||
|
||||
=head2 DESCRIPTION
|
||||
|
||||
Parses through elements and their children elements recursively
|
||||
|
||||
=cut
|
||||
|
||||
sub parse_child_elements() {
|
||||
my ($self, $depth, $env_category_id, $category_name, $twig_element, $admin, $parent_element) = @_;
|
||||
if ($depth > $self->{'max_depth'}) {
|
||||
return;
|
||||
}
|
||||
$depth++;
|
||||
my $element_name = $twig_element->{'att'}->{'name'};
|
||||
# Checking if Elements already exist.
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "Checking if <U>$element_name</U> <STRONG>ELEMENT</STRONG> already exists in the <U>$category_name</U> <STRONG>CATEGORY</STRONG>...";
|
||||
my ($product_id) = $self->{'product_id'};
|
||||
my $element = Bugzilla::Testopia::Environment::Element->new({});
|
||||
my ($elem_id) = $element->check_element($element_name, $env_category_id);
|
||||
my $element_id;
|
||||
if ($elem_id < 1) {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U>.<BR />";
|
||||
my $new_category_elements = $self->{'new_category_elements'};
|
||||
my $new_category_element = {'env_category_id' => $env_category_id, 'category_name' => $category_name, 'element_name' => $element_name};
|
||||
push (@$new_category_elements, $new_category_element);
|
||||
$self->{'new_category_elements'} = $new_category_elements;
|
||||
# Storing New Elements if Admin
|
||||
if ($admin) {
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "..Storing new <U>$element_name</U> <STRONG>ELEMENT</STRONG> in the <U>$category_name</U> <STRONG>CATEGORY</STRONG>...";
|
||||
($element->{'env_category_id'}) = $env_category_id;
|
||||
$element->{'name'} = $element_name;
|
||||
($element->{'product_id'}) = $self->{'product_id'};
|
||||
$element->{'isprivate'} = 0;
|
||||
if ($parent_element) {
|
||||
$element->{'parent_id'} = $parent_element->{'element_id'};
|
||||
}
|
||||
($element_id) = $element->store();
|
||||
$self->{'message'} .= "DONE.<BR />";
|
||||
}
|
||||
}
|
||||
else {
|
||||
($element_id) = $elem_id;
|
||||
$self->{'message'} .= "EXISTS.<BR />";
|
||||
}
|
||||
($element->{'element_id'}) = $element_id;
|
||||
($element->{'env_category_id'}) = $env_category_id;
|
||||
$element->{'name'} = $element_name;
|
||||
($element->{'parent_id'}) = $parent_element->{'parent_id'};
|
||||
my @properties;
|
||||
foreach my $twig_property ($twig_element->children("property")) {
|
||||
my $property_name = $twig_property->{'att'}->{'name'};
|
||||
# Checking if Properties already exist.
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "....Checking if <U>$property_name</U> <STRONG>PROPERTY</STRONG> already exists...";
|
||||
my $property = Bugzilla::Testopia::Environment::Property->new({});
|
||||
my ($prop_id) = $property->check_property($property_name, $element_id);
|
||||
my $property_id;
|
||||
if ($prop_id < 1) {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U>.<BR />";
|
||||
my $new_property_names = $self->{'new_property_names'};
|
||||
push (@$new_property_names, $property_name);
|
||||
$self->{'new_property_names'} = $new_property_names;
|
||||
# Storing New Property if Admin
|
||||
if ($admin) {
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "......Storing new <U>$property_name</U> <STRONG>PROPERTY</STRONG>...";
|
||||
$property->{'name'} = $property_name;
|
||||
($property->{'element_id'}) = $element_id;
|
||||
($property_id) = $property->store();
|
||||
$self->{'message'} .= "DONE.<BR />";
|
||||
}
|
||||
}
|
||||
else {
|
||||
($property_id) = $prop_id;
|
||||
$self->{'message'} .= "EXISTS.<BR />";
|
||||
}
|
||||
$property = Bugzilla::Testopia::Environment::Property->new($property_id);
|
||||
# Checking if new Selected Value and Valid Expression exist.
|
||||
my $validexp;
|
||||
if ($property) {
|
||||
$validexp = $property->validexp();
|
||||
}
|
||||
my $value = $twig_property->field('value');
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "........Checking if <U>$value</U> <STRONG>VALUE</STRONG> exists in the list of selectable values...";
|
||||
if ( $validexp !~ m/$value/) {
|
||||
$self->{'message'} .= "<U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U>.<BR/>";
|
||||
if ($admin) {
|
||||
if (!defined($validexp)) {
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "..........Setting <U>$value</U> <STRONG>VALID EXPRESSION</STRONG> equal to the <STRONG>VALUE</STRONG> for the first time...";
|
||||
$validexp = $value;
|
||||
}
|
||||
else {
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "..........Adding <U>$value</U> <STRONG>VALUE</STRONG> to the <STRONG>VALID EXPRESSION</STRONG>...";
|
||||
$validexp = "$validexp | $value";
|
||||
}
|
||||
$property->update_property_validexp($validexp);
|
||||
$self->{'message'} .= "DONE.<BR/>";
|
||||
}
|
||||
else {
|
||||
my $new_validexp_values = $self->{'new_validexp_values'};
|
||||
my $new_validexp_value = {'property_id' => $property_id, 'property_name' => $property_name, 'value' => $value};
|
||||
push (@$new_validexp_values, $new_validexp_value);
|
||||
$self->{'new_validexp_values'} = $new_validexp_values;
|
||||
}
|
||||
}
|
||||
elsif (!defined($validexp)) {
|
||||
$self->{'message'} .= "<STRONG>VALID EXPRESSION</STRONG> <U><FONT COLOR='RED'><STRONG>DOESN'T EXIST</STRONG></FONT></U> YET.<BR/>";
|
||||
}
|
||||
else {
|
||||
$self->{'message'} .= "EXISTS.<BR/>";
|
||||
}
|
||||
if ($property_id && $admin) {
|
||||
for (my $i = 1; $i < $depth; $i++) {
|
||||
$self->{'message'} .= "....";
|
||||
}
|
||||
$self->{'message'} .= "............Storing new <STRONG>VALUE SELECTED</STRONG> <U>$value</U>...";
|
||||
my $environment = Bugzilla::Testopia::Environment->new($self->{'environment_id'});
|
||||
$environment->store_property_value($property_id, $element_id, $value);
|
||||
$self->{'message'} .= "DONE.<BR/>";
|
||||
}
|
||||
$property->{'value_selected'} = $value;
|
||||
push (@properties, $property);
|
||||
}
|
||||
my $elm_properties = $element->{'properties'};
|
||||
push (@$elm_properties, @properties);
|
||||
if ($parent_element) {
|
||||
my $children = $parent_element->{'children'};
|
||||
push (@$children, $element);
|
||||
$parent_element->{'children'} = $children;
|
||||
}
|
||||
foreach my $twig_element_child ($twig_element->children("element")) {
|
||||
$self->parse_child_elements($depth, $env_category_id, $category_name, $twig_element_child, $admin, $element);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
||||
=head2 Checking Exists
|
||||
|
||||
=head2 DESCRIPTION
|
||||
|
||||
Checking if the Environment, Elements, Categories, and Properties already exist or not.
|
||||
|
||||
=cut
|
||||
|
||||
sub check_new_items() {
|
||||
my $self = shift;
|
||||
my $report;
|
||||
my $new_category_names = $self->{'new_category_names'};
|
||||
foreach my $new_category_name (@$new_category_names) {
|
||||
$report .= "New <U>$new_category_name</U> <STRONG>CATEGORY</STRONG>.<BR/>";
|
||||
}
|
||||
my $new_category_elements = $self->{'new_category_elements'};
|
||||
foreach my $new_category_element (@$new_category_elements) {
|
||||
$report .= "New <U>$new_category_element->{'element_name'}</U> <STRONG>ELEMENT</STRONG> in the ";
|
||||
if (!$new_category_element->{'env_category_id'}){
|
||||
$report .= "<STRONG>new</STRONG> ";
|
||||
}
|
||||
$report .= "<U>$new_category_element->{'category_name'}</U> <STRONG>CATEGORY</STRONG>.<BR/>";
|
||||
}
|
||||
my $new_property_names = $self->{'new_property_names'};
|
||||
foreach my $new_property_name (@$new_property_names) {
|
||||
$report .= "New <U>$new_property_name</U> <STRONG>PROPERTY</STRONG>.<BR/>";
|
||||
}
|
||||
my $new_validexp_values = $self->{'new_validexp_values'};
|
||||
foreach my $new_validexp_value (@$new_validexp_values) {
|
||||
$report .= "New <U>$new_validexp_value->{'value'}</U> <STRONG>VALUE</STRONG> for the ";
|
||||
if (!$new_validexp_value->{'property_id'}){
|
||||
$report .= "<STRONG>new</STRONG> ";
|
||||
}
|
||||
$report .= "<U>$new_validexp_value->{'property_name'}</U> <STRONG>PROPERTY</STRONG>'s selectable value list.<BR/>";
|
||||
}
|
||||
return $report;
|
||||
}
|
||||
|
||||
|
||||
=head2 Store the Environment
|
||||
|
||||
=head2 Description
|
||||
|
||||
Store the Environment Name, Element-Category relationship, Element-Property relationship,
|
||||
Element-ChildElement relationship, Environment-Element-Property-Value.
|
||||
|
||||
=cut
|
||||
|
||||
sub store() {
|
||||
my $self = shift;
|
||||
$self->{'message'} .= "Storing new XML Environment...";
|
||||
if (!$self->{'environment_id'}) {
|
||||
$self->{'environment_id'} = Bugzilla::Testopia::Environment->store_environment_name($self->{'name'}, $self->{'product_id'});
|
||||
}
|
||||
my $environment = Bugzilla::Testopia::Environment->new($self);
|
||||
my $success = $environment->update();
|
||||
if (!$success) {
|
||||
$self->{'message'} .= "ABORTED!<BR/>";
|
||||
$self->{'error'} .= "Failed to store the Environment!<BR/>";
|
||||
return 0;
|
||||
}
|
||||
$self->{'message'} .= "DONE!<BR/>";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
=head2 export
|
||||
|
||||
=head2 Description
|
||||
|
||||
Exports and Environment by env_id to a scalar XML value
|
||||
|
||||
=cut
|
||||
|
||||
sub export() {
|
||||
my $self = shift;
|
||||
my ($env_id) = @_;
|
||||
|
||||
my $xml;
|
||||
|
||||
my $environment = Bugzilla::Testopia::Environment->new($env_id);
|
||||
|
||||
$xml =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>" .
|
||||
"<!DOCTYPE environment SYSTEM 'environment.dtd' >" .
|
||||
"<environment name='$environment->{'name'}' product='";
|
||||
|
||||
my $product = Bugzilla::Product->new($environment->{'product_id'});
|
||||
|
||||
$xml .= "$product->{'name'}'>";
|
||||
|
||||
$environment->get_environment_elements();
|
||||
|
||||
my $categories = {};
|
||||
my $category_names = [];
|
||||
my $categorized_elements = [];
|
||||
my $elements = $environment->{'elements'};
|
||||
my $used_elements = {};
|
||||
foreach my $element (@$elements) {
|
||||
my $root_element = $self->get_root_parent($element);
|
||||
my $category_name = $root_element->cat_name();
|
||||
$categorized_elements = $categories->{ $category_name };
|
||||
for my $used_element (@$categorized_elements) {
|
||||
$used_elements->{ $used_element->{'element_id'} } = 1;
|
||||
}
|
||||
if (!$used_elements->{$root_element->{'element_id'}}) {
|
||||
push (@$categorized_elements, $root_element);
|
||||
$categories->{ $category_name } = $categorized_elements;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $category_name (keys %$categories) {
|
||||
$xml .= "<category name='$category_name'>";
|
||||
|
||||
$elements = $categories->{$category_name};
|
||||
|
||||
|
||||
foreach my $element (@$elements) {
|
||||
$element->get_child_elements();
|
||||
$xml .= $self->export_element_and_children(1, $element, $environment->{'environment_id'});
|
||||
}
|
||||
|
||||
$xml .= "</category>";
|
||||
}
|
||||
|
||||
return "$xml</environment>";
|
||||
}
|
||||
|
||||
|
||||
=head2 get_root_parent
|
||||
|
||||
=head2 Description
|
||||
|
||||
Helper sub that returns the root parent element in the environment of the passed in element
|
||||
|
||||
=cut
|
||||
|
||||
sub get_root_parent {
|
||||
my $self = shift;
|
||||
my ($element) = @_;
|
||||
my $parent = $element->get_parent();
|
||||
if ($parent->type eq 'env_category') {
|
||||
return $element;
|
||||
}
|
||||
else {
|
||||
$self->get_root_parent($parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=head2 export_element_and_children
|
||||
|
||||
=head2 Description
|
||||
|
||||
Exports Elements and their child elements to XML Recursively.
|
||||
|
||||
=cut
|
||||
|
||||
sub export_element_and_children() {
|
||||
my $self = shift;
|
||||
my ($depth, $element, $env_id) = @_;
|
||||
if ($depth > $max_depth) {
|
||||
return;
|
||||
}
|
||||
$depth++;
|
||||
|
||||
my $xml = "<element name='$element->{'name'}'>";
|
||||
|
||||
my $properties = $element->{'properties'};
|
||||
foreach my $property (@$properties) {
|
||||
my $value_selected = Bugzilla::Testopia::Environment->get_value_selected(
|
||||
$env_id, $element->{'element_id'}, $property->{'property_id'});
|
||||
if (defined($value_selected)) {
|
||||
$xml .=
|
||||
"<property name='$property->{'name'}'>" .
|
||||
"<value>$value_selected</value></property>";
|
||||
}
|
||||
}
|
||||
|
||||
my $children = $element->{'children'};
|
||||
foreach my $child_element (@$children) {
|
||||
$child_element->get_child_elements();
|
||||
$xml .= $self->export_element_and_children($depth, $child_element, $env_id);
|
||||
}
|
||||
$xml .= "</element>";
|
||||
return $xml;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,480 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::Product;
|
||||
|
||||
use strict;
|
||||
|
||||
# Extends Bugzilla::Product;
|
||||
use base "Bugzilla::Product";
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
sub environments {
|
||||
my $self = shift;
|
||||
my($active, $current) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
return $self->{'environments'} if defined $self->{'environments'};
|
||||
|
||||
require Bugzilla::Testopia::Environment;
|
||||
|
||||
my $query = "SELECT environment_id";
|
||||
$query .= " FROM test_environments";
|
||||
$query .= " WHERE product_id = ?";
|
||||
$query .= " AND isactive = 1 OR environment_id = ?" if $active;
|
||||
|
||||
my $ref;
|
||||
if ($active && $current){
|
||||
$ref = $dbh->selectcol_arrayref($query, undef, ($self->{'id'}, $current));
|
||||
}
|
||||
else{
|
||||
$ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'});
|
||||
}
|
||||
|
||||
my @objs;
|
||||
foreach my $id (@{$ref}){
|
||||
push @objs, Bugzilla::Testopia::Environment->new($id);
|
||||
}
|
||||
|
||||
$self->{'environments'} = \@objs;
|
||||
return $self->{'environments'};
|
||||
}
|
||||
|
||||
sub builds {
|
||||
my $self = shift;
|
||||
my($active, $current) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
require Bugzilla::Testopia::Build;
|
||||
|
||||
my $query = "SELECT build_id FROM test_builds WHERE product_id = ?";
|
||||
if ($active && $current){
|
||||
$query .= " AND isactive = 1 OR build_id = ?";
|
||||
}
|
||||
elsif ($active){
|
||||
$query .= " AND isactive = 1";
|
||||
}
|
||||
$query .= " ORDER BY name";
|
||||
|
||||
my $ref;
|
||||
if ($active && $current){
|
||||
$ref = $dbh->selectcol_arrayref($query, undef, ($self->{'id'}, $current));
|
||||
}
|
||||
elsif ($active){
|
||||
$ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'});
|
||||
}
|
||||
else{
|
||||
$ref = $dbh->selectcol_arrayref($query, undef, $self->{'id'});
|
||||
}
|
||||
|
||||
my @objs;
|
||||
foreach my $id (@{$ref}){
|
||||
push @objs, Bugzilla::Testopia::Build->new($id);
|
||||
}
|
||||
|
||||
$self->{'builds'} = \@objs;
|
||||
return $self->{'builds'};
|
||||
}
|
||||
|
||||
sub categories {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
"SELECT category_id
|
||||
FROM test_case_categories
|
||||
WHERE product_id = ?
|
||||
ORDER BY name",
|
||||
undef, $self->{'id'});
|
||||
my @objs;
|
||||
require Bugzilla::Testopia::Category;
|
||||
foreach my $id (@{$ref}){
|
||||
push @objs, Bugzilla::Testopia::Category->new($id);
|
||||
}
|
||||
$self->{'categories'} = \@objs;
|
||||
return $self->{'categories'};
|
||||
}
|
||||
|
||||
sub plans {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
return $self->{'plans'} if exists $self->{'plans'};
|
||||
|
||||
require Bugzilla::Testopia::TestPlan;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
"SELECT plan_id
|
||||
FROM test_plans
|
||||
WHERE product_id = ?
|
||||
ORDER BY name",
|
||||
undef, $self->{'id'});
|
||||
my @objs;
|
||||
|
||||
foreach my $id (@{$ref}){
|
||||
push @objs, Bugzilla::Testopia::TestPlan->new($id);
|
||||
}
|
||||
$self->{'plans'} = \@objs;
|
||||
return $self->{'plans'};
|
||||
}
|
||||
|
||||
sub cases {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'cases'} if exists $self->{'cases'};
|
||||
|
||||
require Bugzilla::Testopia::TestCase;
|
||||
|
||||
my $caseids = $dbh->selectcol_arrayref(
|
||||
"SELECT case_id FROM test_case_plans
|
||||
INNER JOIN test_plans on test_case_plans.plan_id = test_plans.plan_id
|
||||
WHERE test_plans.product_id = ?",
|
||||
undef, $self->id);
|
||||
|
||||
my @cases;
|
||||
foreach my $id (@{$caseids}){
|
||||
push @cases, Bugzilla::Testopia::TestCase->new($id);
|
||||
}
|
||||
|
||||
$self->{'cases'} = \@cases;
|
||||
return $self->{'cases'};
|
||||
}
|
||||
|
||||
sub runs {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return $self->{'runs'} if exists $self->{'runs'};
|
||||
|
||||
require Bugzilla::Testopia::TestRun;
|
||||
|
||||
my $runids = $dbh->selectcol_arrayref(
|
||||
"SELECT run_id FROM test_runs
|
||||
INNER JOIN test_plans ON test_runs.plan_id = test_plans.plan_id
|
||||
WHERE test_plans.product_id = ?",
|
||||
undef, $self->id);
|
||||
|
||||
my @runs;
|
||||
foreach my $id (@{$runids}){
|
||||
push @runs, Bugzilla::Testopia::TestRun->new($id);
|
||||
}
|
||||
|
||||
$self->{'runs'} = \@runs;
|
||||
return $self->{'runs'};
|
||||
}
|
||||
|
||||
sub environment_categories {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
"SELECT env_category_id
|
||||
FROM test_environment_category
|
||||
WHERE product_id = ?",
|
||||
undef, $self->id);
|
||||
my @objs;
|
||||
require Bugzilla::Testopia::Environment::Category;
|
||||
foreach my $id (@{$ref}){
|
||||
push @objs, Bugzilla::Testopia::Environment::Category->new($id);
|
||||
}
|
||||
$self->{'environment_categories'} = \@objs;
|
||||
return $self->{'environment_categories'};
|
||||
}
|
||||
|
||||
sub check_product_by_name {
|
||||
my $self = shift;
|
||||
my ($name) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($used) = $dbh->selectrow_array(qq{
|
||||
SELECT id
|
||||
FROM products
|
||||
WHERE name = ?},undef,$name);
|
||||
return $used;
|
||||
}
|
||||
|
||||
sub versions {
|
||||
my $self = shift;
|
||||
my ($byid) = shift;
|
||||
my $id = $byid ? 'id' : 'value';
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $values = $dbh->selectall_arrayref(
|
||||
"SELECT $id AS id, value AS name
|
||||
FROM versions
|
||||
WHERE product_id = ?
|
||||
ORDER BY value", {'Slice' =>{}}, $self->id);
|
||||
|
||||
$self->{'versions'} = $values;
|
||||
return $self->{'versions'};
|
||||
}
|
||||
|
||||
sub milestones {
|
||||
my $self = shift;
|
||||
my ($byid) = shift;
|
||||
my $id = $byid ? 'id' : 'value';
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $values = $dbh->selectall_arrayref(
|
||||
"SELECT $id AS id, value AS name
|
||||
FROM milestones
|
||||
WHERE product_id = ?
|
||||
ORDER BY value", {'Slice' =>{}}, $self->id);
|
||||
|
||||
$self->{'milestones'} = $values;
|
||||
return $self->{'milestones'};
|
||||
}
|
||||
|
||||
sub tags {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
require Bugzilla::Testopia::TestTag;
|
||||
|
||||
my $ref = $dbh->selectcol_arrayref(
|
||||
"(SELECT test_tags.tag_id, test_tags.tag_name AS name
|
||||
FROM test_tags
|
||||
INNER JOIN test_case_tags ON test_tags.tag_id = test_case_tags.tag_id
|
||||
INNER JOIN test_cases on test_cases.case_id = test_case_tags.case_id
|
||||
INNER JOIN test_case_plans on test_case_plans.case_id = test_cases.case_id
|
||||
INNER JOIN test_plans ON test_plans.plan_id = test_case_plans.plan_id
|
||||
WHERE test_plans.product_id = ?)
|
||||
UNION
|
||||
(SELECT test_tags.tag_id, test_tags.tag_name AS name
|
||||
FROM test_tags
|
||||
INNER JOIN test_plan_tags ON test_plan_tags.tag_id = test_tags.tag_id
|
||||
INNER JOIN test_plans ON test_plan_tags.plan_id = test_plans.plan_id
|
||||
WHERE test_plans.product_id = ?)
|
||||
UNION
|
||||
(SELECT test_tags.tag_id, test_tags.tag_name AS name
|
||||
FROM test_tags
|
||||
INNER JOIN test_run_tags ON test_run_tags.tag_id = test_tags.tag_id
|
||||
INNER JOIN test_runs ON test_runs.run_id = test_run_tags.run_id
|
||||
INNER JOIN test_plans ON test_plans.plan_id = test_runs.plan_id
|
||||
WHERE test_plans.product_id = ?)
|
||||
ORDER BY name", undef, ($self->id,$self->id,$self->id));
|
||||
|
||||
my @product_tags;
|
||||
foreach my $id (@$ref){
|
||||
push @product_tags, Bugzilla::Testopia::TestTag->new($id);
|
||||
}
|
||||
|
||||
$self->{'tags'} = \@product_tags;
|
||||
return $self->{'tags'};
|
||||
}
|
||||
|
||||
sub type {
|
||||
my $self = shift;
|
||||
$self->{'type'} = 'product';
|
||||
return $self->{'type'};
|
||||
}
|
||||
|
||||
sub canview {
|
||||
my $self = shift;
|
||||
my ($user) = @_;
|
||||
$user ||= Bugzilla->user;
|
||||
return 1 if $user->can_see_product($self->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub canedit {
|
||||
my $self = shift;
|
||||
my ($user) = @_;
|
||||
$user ||= Bugzilla->user;
|
||||
return 1 if $user->in_group('Testers') && $user->can_see_product($self->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub TO_JSON {
|
||||
my $self = shift;
|
||||
my $obj;
|
||||
my $json = new JSON;
|
||||
|
||||
foreach my $field ($self->DB_COLUMNS){
|
||||
$field =~ s/product\.//;
|
||||
$obj->{$field} = $self->{$field};
|
||||
}
|
||||
|
||||
# Add the calculated fields
|
||||
$obj->{'type'} = $self->type;
|
||||
$obj->{'id'} = $self->id;
|
||||
$obj->{'canedit'} = $self->canedit;
|
||||
|
||||
return $json->encode($obj);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Product
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Product
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides additional methods and functionality to Bugzilla products
|
||||
for Testopia specific usage. Methods are read only. For updating and
|
||||
creating new products, see Bugzilla::Product.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head2 Creating
|
||||
|
||||
$build = Bugzilla::Testopia::Product->new($product_id);
|
||||
$build = Bugzilla::Testopia::Product->new({name => $name});
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<builds($active, $current)>
|
||||
|
||||
Description: Get the list of builds associated with this product.
|
||||
|
||||
Params: $active - Boolean (optional): True to only include builds with isactive set.
|
||||
Defaults to False.
|
||||
$current - Integer (optional): Must be used in conjuntion with $active.
|
||||
If $active is true then $current should be the current build
|
||||
selected to prevent it from being excluded if isactive is false.
|
||||
|
||||
Returns: Array: Returns an array of Build objects.
|
||||
|
||||
=item C<get_cases($product)>
|
||||
|
||||
Description: Get the list of cases associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of TestCase objects.
|
||||
|
||||
=item C<get_categories($product)>
|
||||
|
||||
Description: Get the list of categories associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Case Category objects.
|
||||
|
||||
=item C<get_components($product)>
|
||||
|
||||
Description: Get the list of components associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Component objects.
|
||||
|
||||
=item C<get_environments($product)>
|
||||
|
||||
Description: Get the list of environments associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Environment objects.
|
||||
|
||||
=item C<get_milestones($product)>
|
||||
|
||||
Description: Get the list of milestones associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Milestone objects.
|
||||
|
||||
=item C<get_plans($product)>
|
||||
|
||||
Description: Get the list of plans associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Test Plan objects.
|
||||
|
||||
=item C<get_runs($product)>
|
||||
|
||||
Description: Get the list of runs associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Test Run objects.
|
||||
|
||||
=item C<get_tags($product)>
|
||||
|
||||
Description: Get the list of tags associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Tags objects.
|
||||
|
||||
=item C<get_versions($product)>
|
||||
|
||||
Description: Get the list of versions associated with this product.
|
||||
|
||||
Params: $product - Integer/String/Object
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
Object: Blessed Bugzilla::Product object
|
||||
|
||||
Returns: Array: Returns an array of Version objects.
|
||||
|
||||
=item C<lookup_name_by_id> B<DEPRECATED> Use Product::get instead
|
||||
|
||||
=item C<lookup_id_by_name> B<DEPRECATED - CONSIDERED HARMFUL> Use Product::check_product instead
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
L<Bugzilla::Testopia::Product>
|
||||
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,368 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Large portions lifted from bugzilla's report.cgi written by
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Report - Generates report data.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Reports
|
||||
|
||||
=over
|
||||
|
||||
=back
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Report;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Testopia::Util;
|
||||
use Bugzilla::Testopia::Search;
|
||||
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new report object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
|
||||
$self->init(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
=head2 init
|
||||
|
||||
Private constructor for this class
|
||||
|
||||
=cut
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
my ($type, $url, $cgi) = @_;
|
||||
$self->{'type'} = $type || ThrowCodeError('bad_arg',
|
||||
{argument => 'type',
|
||||
function => 'Testopia::Table::_init'});
|
||||
$self->{'url_loc'} = $url;
|
||||
$self->{'cgi'} = $cgi;
|
||||
my $debug = $cgi->param('debug') if $cgi;
|
||||
|
||||
my $col_field = $cgi->param('x_axis_field') || '';
|
||||
my $row_field = $cgi->param('y_axis_field') || '';
|
||||
my $tbl_field = $cgi->param('z_axis_field') || '';
|
||||
|
||||
if (!($col_field || $row_field || $tbl_field)) {
|
||||
ThrowUserError("no_axes_defined");
|
||||
}
|
||||
|
||||
my $width = $cgi->param('width');
|
||||
my $height = $cgi->param('height');
|
||||
|
||||
if (defined($width)) {
|
||||
(detaint_natural($width) && $width > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
$width <= 2000 || ThrowUserError("chart_too_large");
|
||||
}
|
||||
|
||||
if (defined($height)) {
|
||||
(detaint_natural($height) && $height > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
$height <= 2000 || ThrowUserError("chart_too_large");
|
||||
}
|
||||
|
||||
# These shenanigans are necessary to make sure that both vertical and
|
||||
# horizontal 1D tables convert to the correct dimension when you ask to
|
||||
# display them as some sort of chart.
|
||||
if (defined $cgi->param('format') && $cgi->param('format') eq "table") {
|
||||
if ($col_field && !$row_field) {
|
||||
# 1D *tables* should be displayed vertically (with a row_field only)
|
||||
$row_field = $col_field;
|
||||
$col_field = '';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($row_field && !$col_field) {
|
||||
# 1D *charts* should be displayed horizontally (with an col_field only)
|
||||
$col_field = $row_field;
|
||||
$row_field = '';
|
||||
}
|
||||
}
|
||||
|
||||
my %columns;
|
||||
if ($type eq 'case'){
|
||||
$columns{'case_status'} = "map_case_status.name";
|
||||
$columns{'priority'} = "map_priority.value";
|
||||
$columns{'product'} = "map_case_product.name";
|
||||
$columns{'component'} = "map_case_components.name";
|
||||
$columns{'category'} = "map_categories.name";
|
||||
$columns{'isautomated'} = "test_cases.isautomated";
|
||||
$columns{'tags'} = "map_case_tags.tag_name";
|
||||
$columns{'requirement'} = "test_cases.requirement";
|
||||
$columns{'author'} = "map_case_author.login_name";
|
||||
$columns{'default_tester'} = "map_default_tester.login_name";
|
||||
}
|
||||
elsif ($type eq 'run'){
|
||||
$columns{'run_status'} = "test_runs.stop_date";
|
||||
$columns{'product'} = "map_run_product.name";
|
||||
$columns{'build'} = "map_run_build.name";
|
||||
$columns{'milestone'} = "map_run_milestone.milestone";
|
||||
$columns{'environment'} = "map_run_environment.name";
|
||||
$columns{'tags'} = "map_run_tags.tag_name";
|
||||
$columns{'manager'} = "map_run_manager.login_name";
|
||||
$columns{'default_product_version'} = "test_runs.product_version";
|
||||
}
|
||||
elsif ($type eq 'plan'){
|
||||
$columns{'plan_type'} = "map_plan_type.name";
|
||||
$columns{'product'} = "map_plan_product.name";
|
||||
$columns{'archived'} = "test_plans.isactive";
|
||||
$columns{'tags'} = "map_plan_tags.tag_name";
|
||||
$columns{'author'} = "map_plan_author.login_name";
|
||||
$columns{'default_product_version'} = "test_plans.default_product_version";
|
||||
}
|
||||
elsif ($type eq 'caserun'){
|
||||
$columns{'build'} = "map_caserun_build.name";
|
||||
$columns{'case'} = "map_caserun_case.summary";
|
||||
$columns{'run'} = "map_caserun_run.summary";
|
||||
$columns{'environment'} = "map_caserun_environment.name";
|
||||
$columns{'assignee'} = "map_caserun_assignee.login_name";
|
||||
$columns{'testedby'} = "map_caserun_testedby.login_name";
|
||||
$columns{'case_run_status'} = "map_caserun_status.name";
|
||||
$columns{'milestone'} = "map_caserun_milestone.milestone";
|
||||
$columns{'case_tags'} = "map_caserun_case_tags.tag_name";
|
||||
$columns{'run_tags'} = "map_caserun_run_tags.tag_name";
|
||||
$columns{'requirement'} = "map_caserun_cases.requirement";
|
||||
$columns{'priority'} = "map_caserun_priority.value";
|
||||
$columns{'default_tester'} = "map_caserun_default_tester.login_name";
|
||||
$columns{'category'} = "map_caserun_category.name";
|
||||
$columns{'component'} = "map_caserun_components.name";
|
||||
}
|
||||
# One which means "nothing". Any number would do, really. It just gets SELECTed
|
||||
# so that we always select 3 items in the query.
|
||||
$columns{''} = "42217354";
|
||||
|
||||
# Validate the values in the axis fields or throw an error.
|
||||
!$row_field
|
||||
|| ($columns{$row_field} && trick_taint($row_field))
|
||||
|| ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field});
|
||||
!$col_field
|
||||
|| ($columns{$col_field} && trick_taint($col_field))
|
||||
|| ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field});
|
||||
!$tbl_field
|
||||
|| ($columns{$tbl_field} && trick_taint($tbl_field))
|
||||
|| ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field});
|
||||
|
||||
my @axis_fields = ($row_field, $col_field, $tbl_field);
|
||||
my @selectnames = map($columns{$_}, @axis_fields);
|
||||
$self->{'axis_fields'} = \@axis_fields;
|
||||
$self->{'selectnames'} = \@selectnames;
|
||||
$cgi->param('viewall', 1);
|
||||
|
||||
my $dbh = Bugzilla->switch_to_shadow_db;
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi, \@selectnames);
|
||||
my $results = $dbh->selectall_arrayref($search->query);
|
||||
$dbh = Bugzilla->switch_to_main_db;
|
||||
|
||||
# We have a hash of hashes for the data itself, and a hash to hold the
|
||||
# row/col/table names.
|
||||
my %data;
|
||||
my %names;
|
||||
|
||||
# Read the bug data and count the bugs for each possible value of row, column
|
||||
# and table.
|
||||
#
|
||||
# We detect a numerical field, and sort appropriately, if all the values are
|
||||
# numeric.
|
||||
my $col_isnumeric = 1;
|
||||
my $row_isnumeric = 1;
|
||||
my $tbl_isnumeric = 1;
|
||||
|
||||
foreach my $result (@$results) {
|
||||
my ($row, $col, $tbl) = @$result;
|
||||
|
||||
# handle empty dimension member names
|
||||
$row = ' ' if ($row eq '');
|
||||
$col = ' ' if ($col eq '');
|
||||
$tbl = ' ' if ($tbl eq '');
|
||||
|
||||
$row = "" if ($row eq $columns{''});
|
||||
$col = "" if ($col eq $columns{''});
|
||||
$tbl = "" if ($tbl eq $columns{''});
|
||||
|
||||
# account for the fact that names may start with '_' or '.'. Change this
|
||||
# so the template doesn't hide hash elements with those keys
|
||||
$row =~ s/^([._])/ $1/;
|
||||
$col =~ s/^([._])/ $1/;
|
||||
$tbl =~ s/^([._])/ $1/;
|
||||
|
||||
$data{$tbl}{$col}{$row}++;
|
||||
$names{"col"}{$col}++;
|
||||
$names{"row"}{$row}++;
|
||||
$names{"tbl"}{$tbl}++;
|
||||
|
||||
$col_isnumeric &&= ($col =~ /^-?\d+(\.\d+)?$/o);
|
||||
$row_isnumeric &&= ($row =~ /^-?\d+(\.\d+)?$/o);
|
||||
$tbl_isnumeric &&= ($tbl =~ /^-?\d+(\.\d+)?$/o);
|
||||
}
|
||||
|
||||
my @col_names = @{get_names($names{"col"}, $col_isnumeric, $col_field)};
|
||||
my @row_names = @{get_names($names{"row"}, $row_isnumeric, $row_field)};
|
||||
my @tbl_names = @{get_names($names{"tbl"}, $tbl_isnumeric, $tbl_field)};
|
||||
|
||||
# The GD::Graph package requires a particular format of data, so once we've
|
||||
# gathered everything into the hashes and made sure we know the size of the
|
||||
# data, we reformat it into an array of arrays of arrays of data.
|
||||
push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1);
|
||||
|
||||
my @image_data;
|
||||
foreach my $tbl (@tbl_names) {
|
||||
my @tbl_data;
|
||||
push(@tbl_data, \@col_names);
|
||||
foreach my $row (@row_names) {
|
||||
my @col_data;
|
||||
foreach my $col (@col_names) {
|
||||
$data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0;
|
||||
push(@col_data, $data{$tbl}{$col}{$row});
|
||||
if ($tbl ne "-total-") {
|
||||
# This is a bit sneaky. We spend every loop except the last
|
||||
# building up the -total- data, and then last time round,
|
||||
# we process it as another tbl, and push() the total values
|
||||
# into the image_data array.
|
||||
$data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row};
|
||||
}
|
||||
}
|
||||
|
||||
push(@tbl_data, \@col_data);
|
||||
}
|
||||
|
||||
unshift(@image_data, \@tbl_data);
|
||||
}
|
||||
$self->{'col_field'} = $col_field;
|
||||
$self->{'row_field'} = $row_field;
|
||||
$self->{'tbl_field'} = $tbl_field;
|
||||
|
||||
my @time = localtime(time());
|
||||
my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3];
|
||||
$self->{'date'} = $date;
|
||||
$self->{'format'} = $cgi->param('format');
|
||||
|
||||
$self->{'col_names'} = \@col_names;
|
||||
$self->{'row_names'} = \@row_names;
|
||||
$self->{'tbl_names'} = \@tbl_names;
|
||||
|
||||
# Below a certain width, we don't see any bars, so there needs to be a minimum.
|
||||
if ($width && $cgi->param('format') eq "bar") {
|
||||
my $min_width = (scalar(@col_names) || 1) * 20;
|
||||
|
||||
if (!$cgi->param('cumulate')) {
|
||||
$min_width *= (scalar(@row_names) || 1);
|
||||
}
|
||||
|
||||
$self->{'min_width'} = $min_width;
|
||||
}
|
||||
|
||||
$self->{'width'} = $width if $width;
|
||||
$self->{'height'} = $height if $height;
|
||||
|
||||
$self->{'query'} = $search->query;
|
||||
$self->{'debug'} = $cgi->param('debug');
|
||||
|
||||
$self->{'data'} = \%data;
|
||||
$self->{'image_data'} = \@image_data;
|
||||
$self->{'report_loc'} = "tr_" . $type . "_reports.cgi";
|
||||
if ($cgi->param('debug')) {
|
||||
print $cgi->header;
|
||||
require Data::Dumper;
|
||||
print "<pre>data hash:\n";
|
||||
print Data::Dumper::Dumper(%data) . "\n\n";
|
||||
print "data array:\n";
|
||||
print Data::Dumper::Dumper(@image_data) . "\n\n</pre>";
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_names {
|
||||
my ($names, $isnumeric, $field) = @_;
|
||||
|
||||
my @sorted;
|
||||
|
||||
if ($isnumeric) {
|
||||
# It's not a field we are preserving the order of, so sort it
|
||||
# numerically...
|
||||
sub numerically { $a <=> $b }
|
||||
@sorted = sort numerically keys(%{$names});
|
||||
} else {
|
||||
# ...or alphabetically, as appropriate.
|
||||
@sorted = sort(keys(%{$names}));
|
||||
}
|
||||
|
||||
return \@sorted;
|
||||
}
|
||||
sub listbase{
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
$self->{'listbase'} = $cgi->canonicalise_query(
|
||||
"x_axis_field", "y_axis_field", "z_axis_field",
|
||||
"ctype", "format", "query_format", "report_action", @{$self->{'axis_fields'}});
|
||||
return $self->{'listbase'};
|
||||
}
|
||||
|
||||
sub imagebase {
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
$self->{'imagebase'} = $cgi->canonicalise_query(
|
||||
$self->{'tbl_field'}, "report_action", "ctype", "format", "width", "height");
|
||||
return $self->{'imagebase'};
|
||||
}
|
||||
|
||||
sub switchbase {
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
$self->{'switchbase'} = $cgi->canonicalise_query(
|
||||
"query_format", "report_action", "ctype", "format", "width", "height");
|
||||
return $self->{'switchbase'};
|
||||
}
|
||||
|
||||
1;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,515 @@
|
|||
# -*- 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::Table - Produces display tables for Testopia lists
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
A table is generated as a result of a query. This module returns
|
||||
a list of Testopia objects that were queried for. It supports
|
||||
pagination of data as well as sorting. It takes the following
|
||||
arguments:
|
||||
|
||||
=over
|
||||
|
||||
=item type - one of 'case', 'plan', 'run', 'caserun', or 'environment'
|
||||
|
||||
=item url - the cgi file that is calling this
|
||||
|
||||
=item cgi - a CGI object
|
||||
|
||||
=item list - A reference to a list
|
||||
|
||||
=item query - An SQL query string, usually generated by Search.pm
|
||||
|
||||
=back
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$table = Bugzilla::Testopia::Table->new($type, $url, $cgi, $list, $query);
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Table;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Testopia::Util;
|
||||
use Bugzilla::Testopia::TestCase;
|
||||
use Bugzilla::Testopia::TestPlan;
|
||||
use Bugzilla::Testopia::TestRun;
|
||||
use Bugzilla::Testopia::TestCaseRun;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
# For use in sorting functions which do not allow arguments
|
||||
our $field;
|
||||
our $reverse;
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new table object
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
|
||||
$self->init(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
=head2 init
|
||||
|
||||
Private constructor for this class
|
||||
|
||||
=cut
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
my ($type, $url, $cgi, $list, $query) = @_;
|
||||
$self->{'user'} = Bugzilla->user;
|
||||
$self->{'type'} = $type || ThrowCodeError('bad_arg',
|
||||
{argument => 'type',
|
||||
function => 'Testopia::Table::_init'});
|
||||
$self->{'url_loc'} = $url;
|
||||
$self->{'cgi'} = $cgi;
|
||||
my $debug = $cgi->param('debug') if $cgi;
|
||||
my @list;
|
||||
if ($query){
|
||||
# For paging we need to know the total number of items
|
||||
# but Search.pm returns a query with a subset
|
||||
my $countquery = $query;
|
||||
$countquery =~ s/ LIMIT.*$//;
|
||||
|
||||
my $dbh = Bugzilla->switch_to_shadow_db();
|
||||
|
||||
my $count_res = $dbh->selectcol_arrayref($countquery);
|
||||
my $count = scalar @$count_res;
|
||||
print "<p> Total rows: $count</p>" if $debug;
|
||||
|
||||
$self->{'list_count'} = $count;
|
||||
my @ids;
|
||||
my $list = $dbh->selectcol_arrayref($query);
|
||||
|
||||
$dbh = Bugzilla->switch_to_main_db();
|
||||
|
||||
foreach my $id (@$list){
|
||||
my $o;
|
||||
if ($type eq 'case'){
|
||||
$o = Bugzilla::Testopia::TestCase->new($id);
|
||||
}
|
||||
elsif ($type eq 'plan'){
|
||||
$o = Bugzilla::Testopia::TestPlan->new($id);
|
||||
}
|
||||
elsif ($type eq 'run'){
|
||||
$o = Bugzilla::Testopia::TestRun->new($id);
|
||||
}
|
||||
elsif ($type eq 'case_run'){
|
||||
$o = Bugzilla::Testopia::TestCaseRun->new($id);
|
||||
}
|
||||
elsif ($type eq 'environment'){
|
||||
$o = Bugzilla::Testopia::Environment->new($id);
|
||||
}
|
||||
push (@ids, $id);
|
||||
push (@list, $o);
|
||||
}
|
||||
$self->{'list'} = \@list;
|
||||
$self->{'view_count'} = scalar @list;
|
||||
$self->{'id_list'} = join(",", @$count_res);
|
||||
}
|
||||
if ($cgi){
|
||||
$self->{'viewall'} = $cgi->param('viewall');
|
||||
$self->{'page'} = $cgi->param('page') || 0;
|
||||
}
|
||||
|
||||
print $query if $debug;
|
||||
exit if $debug;
|
||||
# elsif (!$query && !$list){
|
||||
# my @list;
|
||||
# foreach my $id (split(",", $self->get_saved_list())){
|
||||
# if ($self->{'type'} eq 'case'){
|
||||
# my $o = Bugzilla::Testopia::TestCase->new($id);
|
||||
# push @list, $o;
|
||||
# $o->category;
|
||||
# $o->status;
|
||||
# $o->priority;
|
||||
# }
|
||||
# elsif ($self->{'type'} eq 'plan'){
|
||||
# my $o = Bugzilla::Testopia::TestPlan->new($id);
|
||||
# push @list, $o;
|
||||
# $o->test_case_count;
|
||||
# $o->test_run_count;
|
||||
# }
|
||||
# elsif ($self->{'type'} eq 'run'){
|
||||
# my $o = Bugzilla::Testopia::TestCase->new($id);
|
||||
# push @list, $o;
|
||||
## $o->category;
|
||||
## $o->status;
|
||||
## $o->priority;
|
||||
# }
|
||||
# elsif ($self->{'type'} eq 'caserun'){
|
||||
# my $o = Bugzilla::Testopia::TestCase->new($id);
|
||||
# push @list, $o;
|
||||
## $o->category;
|
||||
## $o->status;
|
||||
## $o->priority;
|
||||
# }
|
||||
# elsif ($self->{'type'} eq 'attachment'){
|
||||
# my $o = Bugzilla::Testopia::TestCase->new($id);
|
||||
# push @list, $o;
|
||||
## $o->category;
|
||||
## $o->status;
|
||||
## $o->priority;
|
||||
# }
|
||||
# else {
|
||||
# ThrowUserError('unknown-type');
|
||||
# }
|
||||
# }
|
||||
# $self->{'list'} = \@list;
|
||||
# }
|
||||
# else {
|
||||
# $self->{'list'} = $list;
|
||||
# }
|
||||
# my @params = split(":", $cgi->cookie('TESTORDER'));
|
||||
# $self->{'last_sort'} = shift @params || undef;
|
||||
# $self->{'reverse_sort'} = shift @params || undef;
|
||||
#
|
||||
# #### SORT ####
|
||||
# # This is very inefficient. It would be much better to have
|
||||
# # the database do this.
|
||||
# my $order = $cgi->param('order');
|
||||
# if ($order){
|
||||
# $self->sort_fields($order);
|
||||
# $self->{'reverse_sort'} = ($self->{'reverse_sort'} ? 1 : 0);
|
||||
# }
|
||||
#
|
||||
#### 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
|
||||
# $self->get_page($self->{'page'});
|
||||
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head2 save_list
|
||||
|
||||
Saves the last list to the database as a hidden saved search
|
||||
Used only in list context
|
||||
|
||||
=cut
|
||||
|
||||
sub save_list {
|
||||
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;
|
||||
if ($self->{'id_list'}){
|
||||
$dbh->bz_start_transaction();
|
||||
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 (userid, name, isvisible, query) VALUES(?,?,?,?)", undef,
|
||||
($self->{'user'}->id, "__". $self->{'type'} ."__", 0, join(",", $self->{'id_list'})));
|
||||
}
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
# $self->{'list_count'} = scalar @ids unless $self->{'query'};
|
||||
}
|
||||
|
||||
=head2 get_saved_list
|
||||
|
||||
Retrieves a saved list from the database
|
||||
Used only in list context
|
||||
|
||||
=cut
|
||||
|
||||
sub get_saved_list {
|
||||
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, "__". $type ."__"));
|
||||
my @list = split(',', $list);
|
||||
return \@list;
|
||||
}
|
||||
|
||||
# used by the sort function to check which field to sort on
|
||||
# we turn off warnings to supress the nonnumeric vs numeric junk
|
||||
sub sort_fields {
|
||||
no warnings;
|
||||
if ($field eq 'category'){
|
||||
if ($reverse){
|
||||
$a->category->name cmp $b->category->name;
|
||||
}
|
||||
else {
|
||||
$b->category->name cmp $a->category->name;
|
||||
}
|
||||
}
|
||||
elsif ($field eq 'plans'){
|
||||
if ($reverse){
|
||||
scalar @{$a->plans} <=> scalar @{$b->plans};
|
||||
}
|
||||
else {
|
||||
scalar @{$b->plans} <=> scalar @{$a->plans};
|
||||
}
|
||||
}
|
||||
else{
|
||||
if ($reverse){
|
||||
$a->{$field} cmp $b->{$field};
|
||||
}
|
||||
else {
|
||||
$b->{$field} cmp $a->{$field};
|
||||
}
|
||||
}
|
||||
use warnings;
|
||||
}
|
||||
|
||||
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'};
|
||||
}
|
||||
|
||||
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'}};
|
||||
@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 ####
|
||||
###############################
|
||||
|
||||
sub list { return $_[0]->{'list'}; }
|
||||
sub id_list { return $_[0]->{'id_list'}; }
|
||||
sub list_count { return $_[0]->{'list_count'}; }
|
||||
sub view_count { return $_[0]->{'view_count'}; }
|
||||
sub page { return $_[0]->{'page'}; }
|
||||
sub url_loc { return $_[0]->{'url_loc'}; }
|
||||
sub type { return $_[0]->{'type'}; }
|
||||
|
||||
=head2 page_size
|
||||
|
||||
Returns an ineger representing how many items should appear on a page
|
||||
|
||||
=cut
|
||||
|
||||
sub page_size {
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
my $size = $cgi->param('pagesize') || 25;
|
||||
return $size;
|
||||
}
|
||||
|
||||
=head2 get_order_url
|
||||
|
||||
Returns a URL query string from a CGI object which is used by
|
||||
column headers to produce a sort order
|
||||
|
||||
=cut
|
||||
|
||||
sub get_order_url {
|
||||
my $self = shift;
|
||||
return $self->get_url('page','order');
|
||||
}
|
||||
|
||||
=head2 get_page_url
|
||||
|
||||
Retrns a URL query string from a CGI object which is used by
|
||||
the page navigation links to move from page to page.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_page_url {
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
return $self->{'url_loc'} ."?". $cgi->canonicalise_query('page', 'pagesize', 'viewall');
|
||||
}
|
||||
|
||||
sub get_url {
|
||||
my ($self, @drops) = @_;
|
||||
my $cgi = $self->{'cgi'};
|
||||
$self->{'url'} = $self->{'url_loc'} ."?". $cgi->canonicalise_query(@drops);
|
||||
return $self->{'url'};
|
||||
}
|
||||
|
||||
sub get_query_part {
|
||||
my $self = shift;
|
||||
my $cgi = $self->{'cgi'};
|
||||
my @keys = $cgi->param;
|
||||
my $qstring;
|
||||
foreach my $key (@keys){
|
||||
my @vals = $cgi->param($key);
|
||||
foreach my $val (@vals){
|
||||
$qstring .= $key ."=". url_quote($val) ."&";
|
||||
}
|
||||
}
|
||||
chop $qstring;
|
||||
return $qstring
|
||||
}
|
||||
|
||||
=head2 page_count
|
||||
|
||||
Returns a total count of the number of pages returned by a query.
|
||||
Determined in part by page_size
|
||||
|
||||
=cut
|
||||
|
||||
sub page_count {
|
||||
my $self = shift;
|
||||
if ($self->list_count % $self->page_size){
|
||||
use integer;
|
||||
return ($self->list_count / $self->page_size) + 1;
|
||||
}
|
||||
return $self->list_count/$self->page_size;
|
||||
}
|
||||
|
||||
sub reverse_sort {
|
||||
my $self = shift;
|
||||
return $self->{'reverse_sort'};
|
||||
}
|
||||
|
||||
sub ajax {
|
||||
my $self = shift;
|
||||
return $self->{'ajax'} ? 1 : 0;
|
||||
}
|
||||
|
||||
sub arrow {
|
||||
my $self = shift;
|
||||
if ($self->{'reverse_sort'}) {
|
||||
$self->{'arrow'} = '<img src="images/arrow_desc.png" border="0">';
|
||||
}
|
||||
else {
|
||||
$self->{'arrow'} = '<img src="images/arrow_asc.png" border="0">';
|
||||
}
|
||||
return $self->{'arrow'};
|
||||
}
|
||||
|
||||
sub to_yui_json {
|
||||
my $self = shift;
|
||||
my $out = '';
|
||||
$out .= '{"ResultSet":{';
|
||||
$out .= '"totalResultsAvailable":' . $self->list_count .',';
|
||||
$out .= '"totalResultsReturned":' . $self->page_size .',';
|
||||
$out .= '"firstResultPosition":' . $self->page * $self->page_size .',';
|
||||
$out .= '"Result":[';
|
||||
foreach my $i (@{$self->list}){
|
||||
$out .= '{';
|
||||
$out .= '"ID":"' . $i->id . '",';
|
||||
$out .= '"Name":"' . $i->name . '",';
|
||||
$out .= '"Author":"' . $i->author->login . '",';
|
||||
$out .= '"Created":"' . $i->creation_date . '",';
|
||||
$out .= '"Product":"' . $i->product->name . '",';
|
||||
$out .= '"Version":"' . $i->product_version . '",';
|
||||
$out .= '"Type":"' . $i->type . '",';
|
||||
$out .= '"Cases":"' . $i->test_case_count . '",';
|
||||
$out .= '"Runs":"' . $i->test_run_count . '",';
|
||||
$out .= '"ClickUrl":"tr_show_plan.cgi?plan_id=' . $i->id;
|
||||
$out .= '"},';
|
||||
}
|
||||
chomp($out);
|
||||
$out .= ']}}';
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
sub to_ext_json {
|
||||
my $self = shift;
|
||||
|
||||
my $out = '';
|
||||
$out .= '{';
|
||||
$out .= '"totalResultsAvailable":' . $self->list_count .',';
|
||||
$out .= '"Result":[';
|
||||
foreach my $i (@{$self->list}){
|
||||
$out .= $i->TO_JSON . ',';
|
||||
}
|
||||
chop($out) if scalar @{$self->list};
|
||||
$out .= ']}';
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Testopia::Search
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,374 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::TestTag - A Testopia tag
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Tags in Testopia are used to classify objects in an intuitive manner.
|
||||
Tags are user defined strings that can be attached to Test Plans, test
|
||||
Cases, and Test Runs. They are similar to keywords in Bugzilla but do
|
||||
not require administrator privileges to create.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Testopia::TestTag;
|
||||
|
||||
$tag = Bugzilla::Testopia::TestTag->new($tag_id);
|
||||
$tag = Bugzilla::Testopia::TestTag->new($tag_hash);
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::TestTag;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use JSON;
|
||||
|
||||
use base qw(Exporter Bugzilla::Object);
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 FIELDS
|
||||
|
||||
tag_id
|
||||
tag_name
|
||||
|
||||
=cut
|
||||
use constant DB_TABLE => "test_tags";
|
||||
use constant NAME_FIELD => "tag_name";
|
||||
use constant ID_FIELD => "tag_id";
|
||||
use constant DB_COLUMNS => qw(
|
||||
tag_id
|
||||
tag_name
|
||||
);
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(tag_name);
|
||||
use constant UPDATE_COLUMNS => qw();
|
||||
use constant VALIDATORS => {
|
||||
tag_name => \&_check_name,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Validators ####
|
||||
###############################
|
||||
sub _check_name {
|
||||
my ($invocant, $name) = @_;
|
||||
$name = trim($name);
|
||||
trick_taint($name);
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
||||
Instantiates a new TestTag
|
||||
|
||||
=cut
|
||||
###############################
|
||||
#### Mutators ####
|
||||
###############################
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $param = shift;
|
||||
|
||||
if (ref $param eq 'HASH'){
|
||||
ThrowCodeError('non-empty-hash');
|
||||
}
|
||||
|
||||
unshift @_, $param;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub create {
|
||||
my ($class, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$class->SUPER::check_required_create_fields($params);
|
||||
my $field_values = $class->SUPER::run_create_validators($params);
|
||||
|
||||
my ($key) = $dbh->selectrow_array("SELECT tag_id FROM test_tags
|
||||
WHERE LOWER(tag_name) = ?",
|
||||
undef, lc($field_values->{'tag_name'}));
|
||||
|
||||
return $class->new($key) if $key;
|
||||
return $class->insert_create_data($field_values);
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head2 check_name
|
||||
|
||||
Checks the supplied name to see if a tag of that name exists.
|
||||
If it does it returns a TestTag object. Otherwise it returns
|
||||
undef.
|
||||
|
||||
=cut
|
||||
|
||||
sub check_tag {
|
||||
my $self = shift;
|
||||
my ($name) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($id) = $dbh->selectrow_array(
|
||||
"SELECT tag_id FROM test_tags
|
||||
WHERE tag_name = ?",
|
||||
undef, lc($name));
|
||||
if ($id){
|
||||
return Bugzilla::Testopia::TestTag->new($id);
|
||||
}
|
||||
else{
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub attach {
|
||||
my $self = shift;
|
||||
my ($obj) = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->bz_start_transaction();
|
||||
my $tagged = $dbh->selectrow_array(
|
||||
"SELECT 1 FROM test_". $obj->type ."_tags
|
||||
WHERE tag_id = ? AND ". $obj->type ."_id = ?",
|
||||
undef, $self->id, $obj->id);
|
||||
if ($tagged) {
|
||||
$dbh->bz_commit_transaction();
|
||||
return;
|
||||
}
|
||||
$dbh->do("INSERT INTO test_". $obj->type ."_tags(tag_id, ". $obj->type ."_id, userid)
|
||||
VALUES(?,?,?)",
|
||||
undef, $self->id, $obj->id, Bugzilla->user->id);
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
=head2 store
|
||||
|
||||
Checks if the given tag exists in the database. If so, it returns
|
||||
that ID. Otherwise it submits the tag to the database and returns
|
||||
the newly created ID.
|
||||
|
||||
=cut
|
||||
|
||||
sub store {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $key;
|
||||
$self->{'tag_name'} = trim($self->{'tag_name'});
|
||||
$dbh->bz_start_transaction();
|
||||
($key) = $dbh->selectrow_array("SELECT tag_id FROM test_tags
|
||||
WHERE LOWER(tag_name) = ?",
|
||||
undef, lc($self->{'tag_name'}));
|
||||
if ($key) {
|
||||
$dbh->bz_commit_transaction();
|
||||
return $key;
|
||||
}
|
||||
$dbh->do("INSERT INTO test_tags (tag_name) VALUES (?)",
|
||||
undef, ($self->{'tag_name'}));
|
||||
$key = $dbh->bz_last_key( 'test_tags', 'tag_id' );
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
$self->{'tag_id'} = $key;
|
||||
return $key;
|
||||
}
|
||||
|
||||
=head2 obliterate
|
||||
|
||||
Completely removes a tag from the database. This is the only safe
|
||||
way to do this.
|
||||
|
||||
=cut
|
||||
|
||||
sub obliterate {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("DELETE FROM test_case_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
$dbh->do("DELETE FROM test_plan_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
$dbh->do("DELETE FROM test_run_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
$dbh->do("DELETE FROM test_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
}
|
||||
|
||||
=head2 case_list
|
||||
|
||||
Returns a comma separated list of case ids associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub case_list {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $list = $dbh->selectcol_arrayref(
|
||||
"SELECT case_id FROM test_case_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
return join(",", @{$list});
|
||||
}
|
||||
|
||||
=head2 plan_list
|
||||
|
||||
Returns a comma separated list of plan ids associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub plan_list {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $list = $dbh->selectcol_arrayref(
|
||||
"SELECT plan_id FROM test_plan_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
return join(",", @{$list});
|
||||
}
|
||||
|
||||
=head2 run_list
|
||||
|
||||
Returns a comma separated list of run ids associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub run_list {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $list = $dbh->selectcol_arrayref(
|
||||
"SELECT run_id FROM test_run_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
return join(",", @{$list});
|
||||
}
|
||||
|
||||
sub candelete {
|
||||
my $self = shift;
|
||||
return 0 unless Bugzilla->user->in_group("admin");
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ####
|
||||
###############################
|
||||
|
||||
=head1 ACCCESSOR METHODS
|
||||
|
||||
=head2 id
|
||||
|
||||
Returns the tag ID
|
||||
|
||||
=head2 name
|
||||
|
||||
Returns the tag name
|
||||
|
||||
=cut
|
||||
|
||||
sub id { return $_[0]->{'tag_id'}; }
|
||||
sub name { return $_[0]->{'tag_name'}; }
|
||||
|
||||
=head2 case_count
|
||||
|
||||
Returns a count of the test cases associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub case_count {
|
||||
my $self = shift;
|
||||
return $self->{'case_count'} if exists $self->{'case_count'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(tag_id)
|
||||
FROM test_case_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
$self->{'case_count'} = $count;
|
||||
return $self->{'case_count'};
|
||||
}
|
||||
|
||||
=head2 plan_count
|
||||
|
||||
Returns a count of the test plans associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub plan_count {
|
||||
my $self = shift;
|
||||
return $self->{'plan_count'} if exists $self->{'plan_count'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(tag_id)
|
||||
FROM test_plan_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
|
||||
$self->{'plan_count'} = $count;
|
||||
return $self->{'plan_count'};
|
||||
|
||||
}
|
||||
|
||||
=head2 run_count
|
||||
|
||||
Returns a count of the test runs associated with this tag
|
||||
|
||||
=cut
|
||||
|
||||
sub run_count {
|
||||
my $self = shift;
|
||||
return $self->{'run_count'} if exists $self->{'run_count'};
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($count) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(tag_id)
|
||||
FROM test_run_tags
|
||||
WHERE tag_id = ?", undef, $self->{'tag_id'});
|
||||
$self->{'run_count'} = $count;
|
||||
return $self->{'run_count'}
|
||||
}
|
||||
|
||||
sub TO_JSON {
|
||||
my $self = shift;
|
||||
my $obj;
|
||||
my $json = new JSON;
|
||||
|
||||
foreach my $field ($self->DB_COLUMNS){
|
||||
$obj->{$field} = $self->{$field};
|
||||
}
|
||||
$obj->{'run_count'} = $self->run_count;
|
||||
$obj->{'plan_count'} = $self->plan_count;
|
||||
$obj->{'case_count'} = $self->case_count;
|
||||
|
||||
return $json->encode($obj);
|
||||
}
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
TestPlan TestRun TestCase
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
|
@ -0,0 +1,228 @@
|
|||
# -*- 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): Maciej Maczynski <macmac@xdsnet.pl>
|
||||
# Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Util
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module contains miscillaneous functions used by Testopia.
|
||||
It exports all of these functions using Exporter.
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Util;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Exporter);
|
||||
@Bugzilla::Testopia::Util::EXPORT = qw(get_test_field_id get_time_stamp
|
||||
validate_test_id validate_selection
|
||||
support_server_push process_list
|
||||
percentage);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
#use Bugzilla::Testopia::TestPlan;
|
||||
|
||||
### Methods ###
|
||||
|
||||
=head2 get_test_field_id
|
||||
|
||||
Takes a field name and table and returns the fieldid from the
|
||||
test_fielddefs table.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_test_field_id {
|
||||
my ($field, $table) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my ($field_id) = $dbh->selectrow_array(
|
||||
"SELECT fieldid
|
||||
FROM test_fielddefs
|
||||
WHERE name = ? AND table_name = ?",
|
||||
undef, $field, $table);
|
||||
ThrowCodeError('testopia_undefined_field', {fieldname => $field, table => $table}) unless $field_id;
|
||||
return $field_id;
|
||||
}
|
||||
|
||||
=head2 get_time_stamp
|
||||
|
||||
Returns an SQL timestamp
|
||||
|
||||
=cut
|
||||
|
||||
sub get_time_stamp {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
=head2 tc_alias_to_id
|
||||
|
||||
Takes a test case alias and returns the corresponding ID
|
||||
|
||||
=cut
|
||||
|
||||
sub tc_alias_to_id {
|
||||
my ($alias) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
trick_taint($alias);
|
||||
return $dbh->selectrow_array(
|
||||
"SELECT case_id FROM test_cases WHERE alias = ?", undef, $alias);
|
||||
}
|
||||
|
||||
=head2 validate_test_id
|
||||
|
||||
Takes an ID and a Testopia type and validates it against the database.
|
||||
In the case of cases it will validate and return an ID from an alias.
|
||||
Much of this was taken from Bugzilla. This function assumes all tables
|
||||
in the database are named test_<object>s
|
||||
|
||||
=cut
|
||||
|
||||
sub validate_test_id {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($id, $type) = @_;
|
||||
$id = trim($id);
|
||||
|
||||
# If the ID isn't a number, it might be an alias, so try to convert it.
|
||||
my $alias = $id;
|
||||
if (!detaint_natural($id) && $type eq 'case') {
|
||||
$id = tc_alias_to_id($alias);
|
||||
$id || ThrowUserError("testopia-invalid-test-id-or-alias",
|
||||
{'case_id' => $alias});
|
||||
}
|
||||
|
||||
# Modify the calling code's original variable to contain the trimmed,
|
||||
# converted-from-alias ID.
|
||||
$_[0] = $id;
|
||||
|
||||
# First check that the object exists
|
||||
my ($res) = $dbh->selectrow_array("SELECT ". $type. "_id FROM test_". $type."s
|
||||
WHERE ". $type ."_id=?",undef, $id);
|
||||
|
||||
$res
|
||||
|| ThrowUserError("invalid-test-id-non-existent",
|
||||
{'id' => $alias, 'type' => $type});
|
||||
return $res;
|
||||
}
|
||||
|
||||
=head2 validate_selection
|
||||
|
||||
Checks that the selected option is a valid one
|
||||
|
||||
=cut
|
||||
|
||||
sub validate_selection {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($id, $field, $table) = @_;
|
||||
$id = trim($id);
|
||||
trick_taint($id);
|
||||
|
||||
my ($res) = $dbh->selectrow_array(
|
||||
"SELECT $field
|
||||
FROM $table
|
||||
WHERE $field = ?",
|
||||
undef, $id);
|
||||
|
||||
$res
|
||||
|| ThrowUserError("invalid-test-id-non-existent",
|
||||
{'id' => $id, 'type' => $table});
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub support_server_push {
|
||||
my ($cgi) = @_;
|
||||
my $serverpush =
|
||||
exists $ENV{'HTTP_USER_AGENT'}
|
||||
&& $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/
|
||||
&& $ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/
|
||||
&& $ENV{'HTTP_USER_AGENT'} !~ /WebKit/
|
||||
&& !defined($cgi->param('serverpush'))
|
||||
|| $cgi->param('serverpush');
|
||||
|
||||
return $serverpush;
|
||||
}
|
||||
|
||||
sub percentage {
|
||||
my ($total, $count) = (@_);
|
||||
return $total == 0 ? 0 : int($count*100/$total);
|
||||
}
|
||||
|
||||
|
||||
=head2 update
|
||||
|
||||
Updates this test plan with new values supplied by the user.
|
||||
Accepts a reference to a hash with keys identical to a test plan's
|
||||
fields and values representing the new values entered.
|
||||
Validation tests should be performed on the values
|
||||
before calling this method. If a field is changed, a history
|
||||
of that change is logged in the test_plan_activity table.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
sub log_activity {
|
||||
my ($type, $obj_id, $field, $timestamp, $oldvalue, $newvalue) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$timestamp ||= get_time_stamp();
|
||||
|
||||
trick_taint($newvalue) if defined $newvalue;
|
||||
my $field_id = Bugzilla::Testopia::Util::get_test_field_id($field, "test_". $type ."s");
|
||||
$dbh->do("INSERT INTO test_". $type ."_activity
|
||||
(". $type . "_id, fieldid, who, changed, oldvalue, newvalue)
|
||||
VALUES(?,?,?,?,?,?)",
|
||||
undef, ($obj_id, $field_id, Bugzilla->user->id,
|
||||
$timestamp, $oldvalue, $newvalue));
|
||||
|
||||
}
|
||||
|
||||
sub process_list {
|
||||
my ($ids) = @_;
|
||||
|
||||
my @ids;
|
||||
if (ref $ids eq 'ARRAY'){
|
||||
@ids = @$ids;
|
||||
}
|
||||
elsif ($ids =~ /,/){
|
||||
@ids = split(/[\s,]+/, $ids);
|
||||
}
|
||||
else {
|
||||
push @ids, $ids if $ids;
|
||||
}
|
||||
|
||||
return @ids;
|
||||
}
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
|
@ -0,0 +1,678 @@
|
|||
# -*- 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): David Koenig <dkoenig@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
# Jeff Dayley <jedayley@novell.com>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Xml - Testopia Xml object
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module parsers a XML representation of a Testopia Test Plans,
|
||||
Test Cases, or Categories and stores them in Testopia if not errors
|
||||
are detected.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Testopia::Xml;
|
||||
|
||||
=cut
|
||||
|
||||
package Bugzilla::Testopia::Xml;
|
||||
#use fields qw(testplans testcases tags categories builds);
|
||||
|
||||
use strict;
|
||||
#use base qw(Exporter);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::Testopia::Attachment;
|
||||
use Bugzilla::Testopia::Build;
|
||||
use Bugzilla::Testopia::Category;
|
||||
use Bugzilla::Testopia::TestCase;
|
||||
use Bugzilla::Testopia::TestPlan;
|
||||
use Bugzilla::Testopia::TestRun;
|
||||
use Bugzilla::Testopia::TestTag;
|
||||
use Bugzilla::Testopia::Util;
|
||||
use Bugzilla::Testopia::XmlTestCase;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
=head1 CONSTANTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item STRIP_NONE - Do not strip white space from string.
|
||||
|
||||
=item STRIP_LEFT - Strip white space from left side of string.
|
||||
|
||||
=item STRIP_RIGHT - Strip white space from right side of string.
|
||||
|
||||
=item STRIP_BOTH - Strinp white space from left and right side of string.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
use constant AUTOMATIC => "AUTOMATIC";
|
||||
use constant BLOCKS => "blocks";
|
||||
our $DATABASE_DESCRIPTION = "Database_description";
|
||||
our $DATABASE_ID = "Database_id";
|
||||
use constant IGNORECASE => 1;
|
||||
use constant DEPENDSON => "dependson";
|
||||
use constant PCDATA => "#PCDATA";
|
||||
use constant REQUIRE => "REQUIRE";
|
||||
use constant STRIP_NONE => 0;
|
||||
use constant STRIP_LEFT => 1;
|
||||
use constant STRIP_RIGHT => 2;
|
||||
use constant STRIP_BOTH => 3;
|
||||
our $TESTOPIA_GT = "&testopia_gt;";
|
||||
our $TESTOPIA_LT = "&testopia_lt;";
|
||||
use constant TESTPLAN_REFERENCE => "testplan_reference";
|
||||
our $XML_DESCRIPTION = "Xml_description";
|
||||
our $XML_AMP = "&[Aa][Mm][Pp];";
|
||||
our $XML_APOS = "&[Aa][Pp][Oo][Ss];";
|
||||
our $XML_GT = "&[Gg][Tt];";
|
||||
our $XML_LT = "&[Ll][Tt];";
|
||||
our $XML_QUOT = "&[Qq][Uu][Oo][Tt];";
|
||||
|
||||
use constant XMLREFERENCES_FIELDS => "Database_description Database_id Xml_description";
|
||||
@Bugzilla::Testopia::Xml::EXPORT = qw($DATABASE_DESCRIPTION $DATABASE_ID $XML_DESCRIPTION);
|
||||
|
||||
use Class::Struct;
|
||||
#
|
||||
# The Xml structure is used to keep track of all new Testopia objects being created.
|
||||
#
|
||||
struct
|
||||
(
|
||||
'Bugzilla::Testopia::Xml',
|
||||
{
|
||||
# Array of attachments read for xml source.
|
||||
attachments => '@',
|
||||
#TODO builds => '@',
|
||||
# Array of categories read from xml source. Categories
|
||||
categories => '@',
|
||||
tags => '@',
|
||||
#TODO testenvironments => '@',
|
||||
# Array of testcases read from xml source.
|
||||
testcases => '@',
|
||||
# Hash of testcase aliases indexed by testcase summary. Used during verfication to
|
||||
# insure that alias does not exist and that new aliases are used in only one testcase.
|
||||
testcase_aliases => '%',
|
||||
# Array of testplans read from xml source.
|
||||
testplans => '@',
|
||||
# If true indicates some type of error has occurred processing the XML. Used to prevent
|
||||
# updating Testopia with contents of current XML.
|
||||
parse_error => '$',
|
||||
}
|
||||
);
|
||||
|
||||
#TODO: Add this to checksetup
|
||||
use Text::Diff;
|
||||
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<entity_replace_testopia($string,$strip)>
|
||||
|
||||
Description: This method is called for any field that is stored in HTML format. The Testopia
|
||||
entities are provided to allow users to pass HTML tags. For example, if you want to
|
||||
store a < in Bold font, the following XML
|
||||
&testopia_lt;B&testopia_gt;<&testopia_lt;/B&testopia_gt;
|
||||
is passed to the HTML field as
|
||||
<B><</B>
|
||||
Params: $string - String to convert.
|
||||
Returns: converted string.
|
||||
|
||||
=cut
|
||||
|
||||
sub entity_replace_testopia
|
||||
{
|
||||
my ($string) = @_;
|
||||
|
||||
return undef if ( ! defined $string );
|
||||
|
||||
$string =~ s/$TESTOPIA_GT/>/g;
|
||||
$string =~ s/$TESTOPIA_LT/</g;
|
||||
$string =~ s/\n/<br>/g;
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=item C<entity_replace_xml($string,$strip)>
|
||||
|
||||
Description: This method is called for any field that is not stored in HTML format. The source
|
||||
is XML so any XML entity will be in the &<name>; format. These entities need to be
|
||||
converted back to their character representation.
|
||||
Params: $string - String to convert.
|
||||
$strip - STRIP_NONE, STRIP_LEFT, STRIP_RIGHT, or STRIP_BOTH. White space stripping
|
||||
is included because MySQL 5.0.3 retains trailing spaces when values are
|
||||
stored and retrieved while prior versions stripped the trailing spaces.
|
||||
Any non HTML field should use STRIP_BOTH to prevent searching issues if the
|
||||
database was orginally pre MySQL 5.0.3.
|
||||
Returns: converted string.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub entity_replace_xml
|
||||
{
|
||||
my ($string,$strip) = @_;
|
||||
|
||||
return undef if ( ! defined $string );
|
||||
|
||||
$string =~ s/^\s+// if ( $strip & STRIP_LEFT );
|
||||
$string =~ s/\s+$// if ( $strip & STRIP_RIGHT );
|
||||
$string =~ s/$XML_GT/>/g;
|
||||
$string =~ s/$XML_LT/</g;
|
||||
$string =~ s/$XML_AMP/&/g;
|
||||
$string =~ s/$XML_APOS/'/g;
|
||||
$string =~ s/$XML_QUOT/"/g;
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub error()
|
||||
{
|
||||
my ($self, $message) = @_;
|
||||
|
||||
print STDERR $message . "\n";
|
||||
|
||||
$self->parse_error("TRUE");
|
||||
}
|
||||
|
||||
sub parse()
|
||||
{
|
||||
my ($self, $xml, $filename) = @_;
|
||||
my @case_ids;
|
||||
my $twig = XML::Twig->new( load_DTD => 1, keep_encoding => 1 );
|
||||
|
||||
if ( defined($xml) )
|
||||
{
|
||||
$twig->parse($xml);
|
||||
}
|
||||
elsif ( defined($filename) )
|
||||
{
|
||||
$twig->parsefile($filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
$self->error("Bugzilla::Testopia::Xml::parse has no XML input source")
|
||||
}
|
||||
|
||||
my $root = $twig->root;
|
||||
|
||||
# Check for unimplemented tags.
|
||||
my @twig_builds = $root->children('build');
|
||||
$self->error("Support for <build> tags has not been implemented.") if ( $#twig_builds != -1 );
|
||||
my @twig_testenvironments = $root->children('testenvironment');
|
||||
$self->error("Support for <testenvironment> tags has not been implemented.") if ( $#twig_testenvironments != -1 );
|
||||
my @twig_testruns = $root->children('testrun');
|
||||
$self->error("Support for <testrun> tags has not been implemented.") if ( $#twig_testruns != -1 );
|
||||
my @twig_testrunlogs = $root->children('testrunlog');
|
||||
$self->error("Support for <testrunlog> tags has not been implemented.") if ( $#twig_testrunlogs != -1 );
|
||||
|
||||
foreach my $twig_category ($root->children('category'))
|
||||
{
|
||||
my $category_name = entity_replace_xml($twig_category->field('name'),STRIP_BOTH);
|
||||
my $product_name = entity_replace_xml($twig_category->att('product'),STRIP_BOTH);
|
||||
my $description = entity_replace_xml($twig_category->field('description'),STRIP_BOTH);
|
||||
if ( $category_name eq "" )
|
||||
{
|
||||
$self->error("Category name cannot be empty, product='" . $product_name . "', description='" . $description . "'.");
|
||||
next;
|
||||
}
|
||||
|
||||
$description = "FIX ME. Created during category import with no description supplied." if ( $description eq "" );
|
||||
|
||||
if ( $product_name eq REQUIRE )
|
||||
{
|
||||
$self->error("Must supply a product for category '" . $category_name . "'." );
|
||||
next;
|
||||
}
|
||||
|
||||
my $product = new Bugzilla::Product({name => $product_name});
|
||||
if ( ! $product )
|
||||
{
|
||||
$self->error("Cannot find product '" . $product_name . "' for category '" . $category_name . "'.");
|
||||
$self->{"parser_error"} = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
my $category = new Bugzilla::Testopia::Category
|
||||
({
|
||||
name => $category_name,
|
||||
product_id => $product->id(),
|
||||
description => $description,
|
||||
});
|
||||
|
||||
# Only create the category if it does not exist.
|
||||
push @{$self->categories}, $category if ( ! $category->check_name($category_name) );
|
||||
}
|
||||
|
||||
my $testplan = Bugzilla::Testopia::TestPlan->new({});
|
||||
my %plantype_ids;
|
||||
my @temparray = @{$testplan->get_plan_types()};
|
||||
foreach my $arrayelement (@temparray)
|
||||
{
|
||||
my %temphash = %{$arrayelement};
|
||||
$plantype_ids{$temphash{"name"}} = $temphash{"id"};
|
||||
}
|
||||
|
||||
foreach my $twig_testplan ($root->children('testplan'))
|
||||
{
|
||||
my $author = $twig_testplan->att('author');
|
||||
# Bugzilla::User::match returns a array with a user hash. Fields of the hash needed
|
||||
# are 'id' and 'login'.
|
||||
my $author_ref = Bugzilla::User::match($author, 1, 0);
|
||||
my $author_id = -1;
|
||||
if ( ! $author_ref->[0] )
|
||||
{
|
||||
$self->error("Cannot find author '" . $author . "' in test plan '" . $twig_testplan->field('name') . "'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
my $author_user = $author_ref->[0];
|
||||
bless($author_user,"Bugzilla::User");
|
||||
$author_id = $author_user->id();
|
||||
}
|
||||
|
||||
my $product_id = Bugzilla::Testopia::TestPlan::lookup_product_by_name($twig_testplan->field('product'));
|
||||
if ( ! defined($product_id) )
|
||||
{
|
||||
$self->error("Cannot find product '" . $twig_testplan->field('product') . "' in test plan '" . $twig_testplan->field('name') . "'.");
|
||||
}
|
||||
|
||||
my $name = entity_replace_xml($twig_testplan->field('name'),STRIP_BOTH) || undef;
|
||||
$self->error("Found empty Test Plan name.") if ( ! defined($name) );
|
||||
$self->error("Length of Test Plan name '" . $name . "' must be " . Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH . " characters or less.") if ( defined($name) && ( length($name) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) );
|
||||
|
||||
$testplan = Bugzilla::Testopia::TestPlan->create({
|
||||
'name' => $name,
|
||||
'product_id' => $product_id,
|
||||
'default_product_version' => entity_replace_xml($twig_testplan->field('productversion'),STRIP_BOTH),
|
||||
'type_id' => $plantype_ids{$twig_testplan->att('type')},
|
||||
'text' => entity_replace_testopia($twig_testplan->field('document')),
|
||||
'author_id' => $author_id,
|
||||
'isactive' => entity_replace_xml($twig_testplan->att('archived'),STRIP_BOTH) =~/(true|1)/i ? 1 : 0,
|
||||
'creation_date' => entity_replace_xml($twig_testplan->field('created'),STRIP_BOTH)
|
||||
});
|
||||
push @{$self->testplans}, $testplan;
|
||||
|
||||
my @tags = $twig_testplan->children('tag');
|
||||
foreach my $twig_tag (@tags)
|
||||
{
|
||||
push @{$self->tags}, entity_replace_xml($twig_tag->text(),STRIP_BOTH);
|
||||
}
|
||||
|
||||
my @attachments = $twig_testplan->children('attachment');
|
||||
foreach my $twig_attachments (@attachments)
|
||||
{
|
||||
my $submitter = $twig_attachments->field('submitter');
|
||||
# Bugzilla::User::match returns a array with a user hash. Fields of the hash needed
|
||||
# are 'id' and 'login'.
|
||||
my $submitter_ref = Bugzilla::User::match($submitter, 1, 0);
|
||||
my $submitter_id = -1;
|
||||
if ( ! $submitter_ref->[0] )
|
||||
{
|
||||
$self->error("Cannot find submitter '" . $submitter . "' in test plan '" . $twig_testplan->field('name') . "' attachment '" . $twig_attachments->field('description') . "'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
my $submitter_user = $submitter_ref->[0];
|
||||
bless($submitter_user,"Bugzilla::User");
|
||||
$submitter_id = $submitter_user->id();
|
||||
}
|
||||
my $attachment = Bugzilla::Testopia::Attachment->new({
|
||||
'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH),
|
||||
'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH),
|
||||
'submitter_id' => $submitter_id,
|
||||
'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH),
|
||||
'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH)
|
||||
});
|
||||
push @{$self->attachments}, $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
my $testcase = Bugzilla::Testopia::TestCase->new({ 'name' => 'dummy' });
|
||||
my %priority_ids;
|
||||
@temparray = @{$testcase->get_priority_list()};
|
||||
foreach my $arrayelement (@temparray)
|
||||
{
|
||||
my %temphash = %{$arrayelement};
|
||||
my $longname = $temphash{"name"};
|
||||
# The long name. "P1 - Urgent"
|
||||
$priority_ids{$longname} = $temphash{"id"};
|
||||
# The short name. "P1"
|
||||
my $shortname = $longname;
|
||||
$shortname =~ s/ - .*//;
|
||||
$priority_ids{$shortname} = $temphash{"id"} if ( $longname ne $shortname );
|
||||
}
|
||||
foreach my $twig_testcase ($root->children('testcase'))
|
||||
{
|
||||
my $summary = entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH) || undef;
|
||||
$self->error("Found empty Test Case summary.") if ( ! defined($summary) );
|
||||
$self->error("Length of summary '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH . " characters or less.") if ( defined($summary) && ( length($summary) > Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH ) );
|
||||
my $author = $twig_testcase->att('author');
|
||||
# Bugzilla::User::match returns a array with a user hash. Fields of the hash needed
|
||||
# are 'id' and 'login'.
|
||||
my $author_ref = Bugzilla::User::match($author, 1, 0);
|
||||
my $author_id = -1;
|
||||
if ( ! $author_ref->[0] )
|
||||
{
|
||||
$self->error("Cannot find author '" . $author . "' in test case '" . $summary . "'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
my $author_user = $author_ref->[0];
|
||||
bless($author_user,"Bugzilla::User");
|
||||
$author_id = $author_user->id();
|
||||
}
|
||||
my $tester = entity_replace_xml($twig_testcase->field('defaulttester'),STRIP_BOTH);
|
||||
# Bugzilla::User::match returns a array with a user hash. Fields of the hash needed
|
||||
# are 'id' and 'login'.
|
||||
my $tester_ref = Bugzilla::User::match($tester, 1, 0);
|
||||
my $tester_id = -1;
|
||||
if ( ! $tester_ref->[0] )
|
||||
{
|
||||
$self->error("Cannot find default tester '" . $tester . "' in test case '" . $summary . "'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
my $tester_user = $tester_ref->[0];
|
||||
bless($tester_user,"Bugzilla::User");
|
||||
$tester_id = $tester_user->id();
|
||||
}
|
||||
my $status_id = Bugzilla::Testopia::TestCase::lookup_status_by_name($twig_testcase->att('status'));
|
||||
$self->error("Cannot find status '" . $twig_testcase->att('status') . "' in test case '" . $summary . "'.") if ( ! defined($status_id) );
|
||||
|
||||
my $xml_testcase = new Bugzilla::Testopia::XmlTestCase;
|
||||
$xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
$xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
$xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
push @{$self->testcases}, $xml_testcase;
|
||||
my $alias = entity_replace_xml($twig_testcase->field('alias'),STRIP_BOTH) || undef;
|
||||
$self->error("Length of alias '" . $alias . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH . " characters or less.") if ( defined($alias) && ( length($alias) > Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH ) );
|
||||
my $requirement = entity_replace_xml($twig_testcase->field('requirement'),STRIP_BOTH) || undef;
|
||||
$self->error("Length of requirement '" . $requirement . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less.") if ( defined($requirement) && ( length($requirement) > Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH ) );
|
||||
|
||||
$xml_testcase->testcase(Bugzilla::Testopia::TestCase->new({
|
||||
'action' => entity_replace_testopia($twig_testcase->field('action')),
|
||||
'alias' => $alias,
|
||||
'arguments' => entity_replace_xml($twig_testcase->field('arguments'),STRIP_NONE),
|
||||
'author_id' => $author_id,
|
||||
'blocks' => undef,
|
||||
'breakdown' => entity_replace_testopia($twig_testcase->field('breakdown')),
|
||||
'case_status_id' => $status_id,
|
||||
'category_id' => undef,
|
||||
'default_tester_id' => $tester_id,
|
||||
'dependson' => undef,
|
||||
'effect' => entity_replace_testopia($twig_testcase->field('expectedresults')),
|
||||
'isautomated' => ( uc $twig_testcase->att('automated') ) eq AUTOMATIC ? 1 : 0,
|
||||
'plans' => undef,
|
||||
'priority_id' => $priority_ids{$twig_testcase->att('priority')},
|
||||
'requirement' => $requirement,
|
||||
'setup' => entity_replace_testopia($twig_testcase->field('setup')),
|
||||
'script' => entity_replace_xml($twig_testcase->field('script'),STRIP_NONE),
|
||||
'summary' => $summary,
|
||||
}));
|
||||
|
||||
foreach my $twig_testplan_reference ( $twig_testcase->children(TESTPLAN_REFERENCE) )
|
||||
{
|
||||
my $testplan_reference = $twig_testplan_reference->children_text(PCDATA);
|
||||
if ( $testplan_reference eq "" )
|
||||
{
|
||||
$self->error("No test plan included for type '"
|
||||
. $twig_testplan_reference->att('type')
|
||||
. "' in test case '" . $twig_testcase->field('summary') . "'." );
|
||||
}
|
||||
elsif ( length($testplan_reference) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH )
|
||||
{
|
||||
$self->error("Length of Test Plan name '" . $testplan_reference . "' for test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less.");
|
||||
}
|
||||
elsif ( ! $xml_testcase->testplan->add($twig_testplan_reference->att('type'),entity_replace_xml($testplan_reference,STRIP_BOTH)) )
|
||||
{
|
||||
$self->error("Do not know how to handle test plan of type '"
|
||||
. $twig_testplan_reference->att('type')
|
||||
. "' in test case '" . $twig_testcase->field('summary') . "'."
|
||||
. "\nKnown types are: (" . uc XMLREFERENCES_FIELDS . ").");
|
||||
}
|
||||
}
|
||||
# Keep track of this testcase's alias. Used during verification to insure aliases are unique.
|
||||
$self->testcase_aliases(entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH),$alias) if ( defined $alias );
|
||||
# Keep track of this testcase's category. To create a category at this time would require
|
||||
# getting the product from the Test Plan that this Test Case is associated with. The category
|
||||
# will created when each Test Case is stored.
|
||||
my $categoryname = entity_replace_xml($twig_testcase->field('categoryname'),STRIP_BOTH);
|
||||
if ( $categoryname ne "" )
|
||||
{
|
||||
$xml_testcase->category($categoryname);
|
||||
}
|
||||
else
|
||||
{
|
||||
$self->error("Empty category name for test case '" . $summary . "'.");
|
||||
}
|
||||
|
||||
my @attachments = $twig_testcase->children('attachment');
|
||||
foreach my $twig_attachments (@attachments)
|
||||
{
|
||||
my $submitter = $twig_attachments->field('submitter');
|
||||
# Bugzilla::User::match returns a array with a user hash. Fields of the hash needed
|
||||
# are 'id' and 'login'.
|
||||
my $submitter_ref = Bugzilla::User::match($submitter, 1, 0);
|
||||
my $submitter_id = -1;
|
||||
if ( ! $submitter_ref->[0] )
|
||||
{
|
||||
$self->error("Cannot find submitter '" . $submitter . "' in test case '" . $twig_testcase->field('summary') . "' attachment '" . $twig_attachments->field('description') . "'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
my $submitter_user = $submitter_ref->[0];
|
||||
bless($submitter_user,"Bugzilla::User");
|
||||
$submitter_id = $submitter_user->id();
|
||||
}
|
||||
my $attachment = {
|
||||
'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH),
|
||||
'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH),
|
||||
'submitter_id' => $submitter_id,
|
||||
'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH),
|
||||
'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH)
|
||||
};
|
||||
$xml_testcase->add_attachment($attachment);
|
||||
}
|
||||
|
||||
my @tags = $twig_testcase->children('tag');
|
||||
foreach my $twig_tag ( @tags )
|
||||
{
|
||||
my $tag = entity_replace_xml($twig_tag->text(),STRIP_BOTH);
|
||||
$self->error("Length of tag '" . $tag . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH . " characters or less.") if ( defined($tag) && ( length($tag) > Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH ) );
|
||||
$xml_testcase->add_tag($tag);
|
||||
}
|
||||
|
||||
my @components = $twig_testcase->children('component');
|
||||
foreach my $twig_component ( @components )
|
||||
{
|
||||
my $results = $xml_testcase->add_component(entity_replace_xml($twig_component->children_text(PCDATA),STRIP_BOTH),$twig_component->att('product'));
|
||||
$self->error($results) if ( $results ne "" );
|
||||
}
|
||||
foreach my $twig_blocks ( $twig_testcase->children(BLOCKS) )
|
||||
{
|
||||
if ( ! $xml_testcase->blocks->add($twig_blocks->att('type'),entity_replace_xml($twig_blocks->children_text(PCDATA),STRIP_BOTH)) )
|
||||
{
|
||||
$self->error("Do not know how to handle a blocking test case of type '" . $twig_blocks->att('type') . "' in test case '" . $xml_testcase->testcase->summary() . "'.")
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $twig_dependson ( $twig_testcase->children(DEPENDSON) )
|
||||
{
|
||||
if ( ! $xml_testcase->dependson->add($twig_dependson->att('type'),entity_replace_xml($twig_dependson->children_text(PCDATA),STRIP_BOTH)) )
|
||||
{
|
||||
$self->error("Do not know how to handle dependency of type '" . $twig_dependson->att('type') . "' in test case '" . entity_replace_xml($xml_testcase->testcase->summary(),STRIP_BOTH) . "'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Start of data integrity check.
|
||||
#
|
||||
# Run through the Test Plans and Test Cases looking for integrity errors.
|
||||
#
|
||||
|
||||
# Check for duplicate aliases. Loop though all testcases that have a alias.
|
||||
my %used_alias;
|
||||
my %duplicate_alias;
|
||||
foreach my $summary ( keys %{$self->testcase_aliases} )
|
||||
{
|
||||
# Get the alias.
|
||||
my $alias = $self->testcase_aliases($summary);
|
||||
# Is the alias used by a testcase in the database already? If so add it to the duplicate list
|
||||
# and move onto next testcase.
|
||||
my $alias_testcase_id = Bugzilla::Testopia::TestCase::class_check_alias($alias);
|
||||
if ( $alias_testcase_id )
|
||||
{
|
||||
$duplicate_alias{$alias} = $alias_testcase_id;
|
||||
next;
|
||||
}
|
||||
# Is the alias in the used_alias array?
|
||||
if ( defined( $used_alias{$alias} ) )
|
||||
{
|
||||
# If so then another testcase being created also used the alias. Add the alias to the
|
||||
# duplicate list.
|
||||
$duplicate_alias{$alias} = "import";
|
||||
}
|
||||
else
|
||||
{
|
||||
# Alias has not been seen yet. Add it to the used_alias list to keep track of it.
|
||||
$used_alias{$alias} = "";
|
||||
}
|
||||
}
|
||||
# The @duplicate_alias list contains aliases used by more that one test case. Display them and set
|
||||
# error condition
|
||||
foreach my $summary ( keys %{$self->testcase_aliases} )
|
||||
{
|
||||
my $alias = $self->testcase_aliases($summary);
|
||||
if ( exists $duplicate_alias{$alias} )
|
||||
{
|
||||
my $error_message = "Test Case '" . $summary . "' has a non-unique alias '" . $alias . "'.";
|
||||
if ( $duplicate_alias{$alias} ne "import" )
|
||||
{
|
||||
$error_message .= " Test Case " . $duplicate_alias{$alias} . " already uses the alias '" . $alias . "'.";
|
||||
}
|
||||
else
|
||||
{
|
||||
$error_message .= " Additional test cases being imported are using the alias '" . $alias . "'.";
|
||||
}
|
||||
$self->error($error_message);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Start of data store.
|
||||
#
|
||||
# No data has been written prior to this point. If parse_error has not been set the XML is valid
|
||||
# and no integrity errors were found. It's time to start storing the Test Plans and Test Cases.
|
||||
#
|
||||
|
||||
if ( ! defined $self->parse_error )
|
||||
{
|
||||
# Store new categories.
|
||||
foreach my $category ( @{$self->categories} )
|
||||
{
|
||||
# Make sure category still does not exist. We don't check for uniqueness above so
|
||||
# the same category could be defined multiple times.
|
||||
if ( ! $category->check_name($category->name()) )
|
||||
{
|
||||
$category->store();
|
||||
my $product_name = Bugzilla::Testopia::Product->new($category->product_id())->name();
|
||||
print "Created category '" . $category->name() . "': " . $category->description() . " for product " . $product_name . ".\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Store new testplans.
|
||||
foreach my $testplan ( @{$self->testplans} )
|
||||
{
|
||||
foreach my $asciitag ( @{$self->tags} )
|
||||
{
|
||||
my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag});
|
||||
my $tagid = $classtag->store;
|
||||
$testplan->{'tag_id'} = $tagid;
|
||||
$testplan->add_tag($tagid);
|
||||
}
|
||||
foreach my $attachment ( @{$self->attachments} )
|
||||
{
|
||||
$attachment->{'plan_id'} = $testplan->id;
|
||||
Bugzilla::Testopia::Attachment->create($attachment);
|
||||
}
|
||||
print "Created Test Plan ". $testplan->id . ": " . $testplan->name() . "\n";
|
||||
}
|
||||
|
||||
# Store new testcases.
|
||||
foreach my $testcase ( @{$self->testcases} )
|
||||
{
|
||||
bless($testcase,"Bugzilla::Testopia::XmlTestCase");
|
||||
my $result = $testcase->store(@{$self->testplans});
|
||||
if ( $result ne "" )
|
||||
{
|
||||
$self->error($result);
|
||||
}
|
||||
else
|
||||
{
|
||||
push @case_ids, $testcase->testcase->id();
|
||||
print "Created Test Case " . $testcase->testcase->id() . ": " . $testcase->testcase->summary() . "\n" if $filename;
|
||||
}
|
||||
}
|
||||
|
||||
# Now that each testcase has been stored we loop though them again and create
|
||||
# relationships like blocks or dependson.
|
||||
foreach my $testcase ( @{$self->testcases} )
|
||||
{
|
||||
$testcase->store_relationships(@{$self->testcases});
|
||||
}
|
||||
}
|
||||
|
||||
$twig->purge;
|
||||
return \@case_ids;
|
||||
}
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Testopia::(TestPlan, TestCase, TestRun, Category, Build, Environment)
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
David Koenig <dkoenig@novell.com>
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
|
@ -0,0 +1,144 @@
|
|||
# -*- 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): David Koenig <dkoenig@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::XmlReferences;
|
||||
|
||||
use constant IGNORECASE => "ignorecase";
|
||||
|
||||
use strict;
|
||||
|
||||
sub new {
|
||||
my ($invocant,$ignorecase,$fields) = @_;
|
||||
|
||||
my $class = ref($invocant) || $invocant;
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
$self->{IGNORECASE} = $ignorecase;
|
||||
for my $field ( split(/ /, $fields) ) {
|
||||
$field = uc $field if ( $self->{IGNORECASE} );
|
||||
$self->{$field} = [];
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub add {
|
||||
my ($self, $type, $object) = @_;
|
||||
|
||||
$type = uc $type if ( $self->{IGNORECASE} );
|
||||
|
||||
return 0 if ( ! exists $self->{$type} );
|
||||
|
||||
push @{$self->{$type}}, $object;
|
||||
}
|
||||
|
||||
sub display {
|
||||
my ($self) = @_;
|
||||
|
||||
print "display() self=" . $self . "\n";
|
||||
foreach my $key (keys %$self) {
|
||||
if ( defined $self->{$key} ) {
|
||||
print "display() key=$key value=" . $self->{$key} . "\n";
|
||||
}
|
||||
else {
|
||||
print "display() key=$key value=undefined\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub get {
|
||||
my ($self, $type) = @_;
|
||||
|
||||
$type = uc $type if ( $self->{IGNORECASE} );
|
||||
|
||||
return 0 if ( ! exists $self->{$type} );
|
||||
|
||||
return $self->{$type};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::XmlReferences - Testopia XmlReferences object
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module maintains references to objects while the XML data is
|
||||
being imported. Test plans and Test cases can be referenced by
|
||||
database id, database description or XML description. The references
|
||||
are stored here and processed as needed.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Testopia::XmlReferences;
|
||||
|
||||
$xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
$xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
$xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS));
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<new(IGNORECASE, XMLREFERENCES_FIELDS)>
|
||||
|
||||
Description: Creates a new reference.
|
||||
|
||||
Params: Constants - IGNORECASE
|
||||
|
||||
Returns: Blessed XmlReferences object.
|
||||
|
||||
=item C<add($type, $object)>
|
||||
|
||||
Description: Add a new object reeference of the supplied type.
|
||||
|
||||
Params: type - dependson, blocks, plan
|
||||
object - case or plan
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<display()>
|
||||
|
||||
Description: displays the cureent references on the screen.
|
||||
|
||||
Params: none.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<get($type)>
|
||||
|
||||
Description: Returns the references of the specified type.
|
||||
|
||||
Params: type
|
||||
|
||||
Returns: references.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Testopia::(TestPlan, TestCase, XmlTestCase, Xml)
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
David Koenig <dkoenig@novell.com>
|
|
@ -0,0 +1,485 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): David Koenig <dkoenig@novell.com>
|
||||
# Jeff Dayley <jedayley@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::Testopia::XmlTestCase;
|
||||
#use fields qw(testplans testcases tags categories builds);
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::Testopia::Attachment;
|
||||
use Bugzilla::Testopia::Build;
|
||||
use Bugzilla::Testopia::Category;
|
||||
use Bugzilla::Testopia::TestCase;
|
||||
use Bugzilla::Testopia::TestPlan;
|
||||
use Bugzilla::Testopia::TestRun;
|
||||
use Bugzilla::Testopia::TestTag;
|
||||
use Bugzilla::Testopia::Util;
|
||||
use Bugzilla::Testopia::XmlReferences;
|
||||
use Bugzilla::Testopia::Product;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use Class::Struct;
|
||||
|
||||
struct(
|
||||
'Bugzilla::Testopia::XmlTestCase',
|
||||
{
|
||||
attachments => '@',
|
||||
blocks => 'Bugzilla::Testopia::XmlReferences',
|
||||
category => '$',
|
||||
component_ids => '@',
|
||||
dependson => 'Bugzilla::Testopia::XmlReferences',
|
||||
tags => '@',
|
||||
testcase => '$',
|
||||
testplan => 'Bugzilla::Testopia::XmlReferences',
|
||||
}
|
||||
);
|
||||
|
||||
sub add_attachment(){
|
||||
my ($self,$attachment) = @_;
|
||||
|
||||
push @{$self->attachments}, $attachment;
|
||||
}
|
||||
|
||||
sub add_tag(){
|
||||
my ($self,$tag) = @_;
|
||||
|
||||
push @{$self->tags}, $tag;
|
||||
}
|
||||
|
||||
sub get_available_products {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $products = $dbh->selectall_arrayref(
|
||||
"SELECT id, name
|
||||
FROM products
|
||||
ORDER BY name",
|
||||
{"Slice"=>{}});
|
||||
return $products;
|
||||
}
|
||||
|
||||
sub add_component {
|
||||
my ($self,$component,$component_product) = @_;
|
||||
my $component_id = "";
|
||||
my $product_id = "";
|
||||
|
||||
return "Component $component needs to provide a product." if ( $component_product eq "" );
|
||||
|
||||
# Find the product identifier.
|
||||
my $products_ref = get_available_products();
|
||||
foreach my $product (@$products_ref){
|
||||
if ( $component_product eq $product->{name} ){
|
||||
$product_id = $product->{id};
|
||||
last;
|
||||
}
|
||||
}
|
||||
return "Cannot find product $component_product for component $component." if ( $product_id eq "" );
|
||||
|
||||
# Find the component identifier for the product's componet
|
||||
my $product = Bugzilla::Testopia::Product->new($product_id);
|
||||
my $components_ref = $product->components;
|
||||
foreach my $product_component ( @$components_ref ) {
|
||||
if ( $component eq $product_component->name ) {
|
||||
$component_id = $product_component->id;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return "Product $component_product does not have a component named $component." if ( $component_id eq "" );
|
||||
|
||||
# Save the component identifier for this Test Case.
|
||||
push @{$self->component_ids}, $component_id;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
sub debug_display {
|
||||
my ($self) = @_;
|
||||
my $display_variable = "";
|
||||
|
||||
my %text = %{$self->testcase->text()} if ( defined $self->testcase->text() );
|
||||
print STDERR "Testcase: " . $self->testcase->summary() . "\n";
|
||||
my $testcase_id = "null";
|
||||
$testcase_id = $self->testcase->id() if ( $self->testcase->id() );
|
||||
print STDERR " ID " . $testcase_id . "\n";
|
||||
print STDERR " Action\n";
|
||||
if ( defined $text{'action'} ){
|
||||
my @results = split(/\n/,$text{'action'});
|
||||
foreach my $result (@results){
|
||||
print STDERR " $result\n";
|
||||
}
|
||||
}
|
||||
my $alias = "null";
|
||||
$alias = $self->testcase-alias() if ( $self->testcase->alias() );
|
||||
print STDERR " Alias " . $alias . "\n";
|
||||
my %author = %{$self->testcase->author()};
|
||||
my $author_id = $author{"id"};
|
||||
my $author_login = $author{"login"};
|
||||
print STDERR " Author " . $author_login . " (id=" . $author_id . ", hash=" . $self->testcase->author() . ")\n";
|
||||
print STDERR " Blocks " . $self->blocks->display() . "\n";
|
||||
print STDERR " Category " . $self->category . "\n";
|
||||
print STDERR " Depends On " . $self->dependson->display() . "\n";
|
||||
print STDERR " Expected Results\n";
|
||||
if ( defined $text{'effect'} ){
|
||||
my @results = split(/\n/,$text{'effect'});
|
||||
foreach my $result (@results){
|
||||
print STDERR " $result\n";
|
||||
}
|
||||
}
|
||||
print STDERR " Summary " . $self->testcase->summary() . "\n";
|
||||
#TODO:print STDERR " Default Product Version " . $self->testcase->product_version() . "\n";
|
||||
#TODO:print STDERR " Document " . $self->testcase->text() . "\n";
|
||||
my %tester = %{$self->testcase->default_tester()};
|
||||
my $tester_id = $tester{"id"};
|
||||
my $tester_login = $tester{"login"};
|
||||
print STDERR " Tester " . $tester_login . " (id=" . $tester_id . ", hash=" . $self->testcase->default_tester() . ")\n";
|
||||
print STDERR " Is Automated " . $self->testcase->isautomated() . "\n";
|
||||
#TODO:print STDERR " Plans " . $self->testcase->plans() . "\n";
|
||||
#TODO:print STDERR " Priority " . $self->testcase->priority_id() . "\n";
|
||||
#TODO:print STDERR " Product " . $self->testcase->product_id() . "\n";
|
||||
print STDERR " Requirement " . $self->testcase->requirement() . "\n";
|
||||
|
||||
print STDERR " Script " . $self->testcase->script() . "\n";
|
||||
print STDERR " Script Arguments " . $self->testcase->arguments() . "\n";
|
||||
print STDERR " Status " . $self->testcase->status() . "\n";
|
||||
|
||||
foreach my $tag (@{$self->tags}){
|
||||
print STDERR " Tag " . $tag . "\n";
|
||||
}
|
||||
|
||||
my @attachments = @{$self->testcase->attachments()};
|
||||
foreach my $attachment (@attachments) {
|
||||
my %submitter = %{$self->testcase->submitter()};
|
||||
$author_login = $author{"login"};
|
||||
print STDERR " Attachment " . $attachment->description() . "\n";
|
||||
print STDERR " Filename " . $attachment->filename() . "\n";
|
||||
print STDERR " Mime Type " . $attachment->mime_type(). "\n";
|
||||
print STDERR " Submitter " . $author_login . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_testcase_ids {
|
||||
my ($self, $field, @new_testcases) = @_;
|
||||
my $error_message = "";
|
||||
|
||||
my @testcase_id = @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)};
|
||||
|
||||
foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) {
|
||||
foreach my $testcase (@new_testcases) {
|
||||
push @testcase_id, $testcase->testcase->{'case_id'} if ( $testcase->testcase->{'summary'} eq $testcase_summary );
|
||||
}
|
||||
}
|
||||
|
||||
#TODO Testplans using Database_Description
|
||||
foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) {
|
||||
$error_message .= "\n" if ( $error_message ne "" );
|
||||
$error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup for blocking test case " . $testcase_summary . "' for Test Case '". $self->testcase->summary . "'.";
|
||||
}
|
||||
return $error_message if ( $error_message ne "" );
|
||||
|
||||
my @return_testcase_id;
|
||||
foreach my $testcase_id (@testcase_id) {
|
||||
my $testcase = Bugzilla::Testopia::TestCase->new($testcase_id);
|
||||
if ( ! defined($testcase) ) {
|
||||
$error_message .= "\n" if ( $error_message ne "" );
|
||||
$error_message .= "Could not find blocking Test Case '" . $testcase_id . "' for Test Case '". $self->testcase->summary . "'.";
|
||||
}
|
||||
else {
|
||||
push @return_testcase_id, $testcase->id();
|
||||
}
|
||||
}
|
||||
return $error_message if ( $error_message ne "" );
|
||||
|
||||
return @return_testcase_id;
|
||||
}
|
||||
|
||||
sub store {
|
||||
my ($self, @new_testplans) = @_;
|
||||
my $error_message = "";
|
||||
my @testplan_id = @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)};
|
||||
|
||||
# If we have any references to test plans from the XML data we need to search the @new_testplans
|
||||
# array to find the actual test plan id. The new_testplans array contains all test plans created
|
||||
# from the XML.
|
||||
# Order of looping does not matter, number of test plans associated to each test case should be small.
|
||||
foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) {
|
||||
foreach my $testplan (@new_testplans) {
|
||||
push @testplan_id, $testplan->id() if ( $testplan->name() eq $testplan_name );
|
||||
}
|
||||
}
|
||||
|
||||
#TODO Testplans using Database_Description
|
||||
foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) {
|
||||
$error_message .= "\n" if ( $error_message ne "" );
|
||||
$error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup of test plan " . $testplan_name . "' for Test Case '". $self->testcase->{'summary'} . "'.";
|
||||
}
|
||||
return $error_message if ( $error_message ne "" );
|
||||
|
||||
# Have to have a testplan to determine valid categories for testcase.
|
||||
return "Test Case '" . $self->testcase->{'summary'} . "' needs a Test Plan." if ( $#testplan_id == -1 );
|
||||
|
||||
# Verify that each testplan exists.
|
||||
my @testplan;
|
||||
foreach my $testplan_id (@testplan_id) {
|
||||
my $testplan = Bugzilla::Testopia::TestPlan->new($testplan_id);
|
||||
if ( ! defined($testplan) ) {
|
||||
$error_message .= "\n" if ( $error_message ne "" );
|
||||
$error_message .= "Could not find Test Plan '" . $testplan_id . "' for Test Case '". $self->testcase->{'summary'} . "'.";
|
||||
}
|
||||
else {
|
||||
push @testplan, $testplan;
|
||||
}
|
||||
}
|
||||
return $error_message if ( $error_message ne "" );
|
||||
|
||||
# Verify that each testplan has the testcase's category associated to it. If the category does not
|
||||
# exist it will be created.
|
||||
foreach my $testplan (@testplan) {
|
||||
my $category = $testplan->product->categories->[0];
|
||||
|
||||
my $categoryid = check_case_category($self->category, new Bugzilla::Testopia::Product($testplan->product_id)) if ( defined($category) );
|
||||
if ( ! defined($categoryid) ) {
|
||||
my $new_category = Bugzilla::Testopia::Category->create({
|
||||
product_id => $testplan->product_id,
|
||||
name => $self->category,
|
||||
description => "FIX ME. Created during Test Plan import."
|
||||
});
|
||||
$categoryid = $new_category->id;
|
||||
}
|
||||
$self->testcase->{'category_id'} = $categoryid if ( ! defined($self->testcase->{'category_id'}) );
|
||||
}
|
||||
$self->testcase->{plans} = \@testplan;
|
||||
my $case = Bugzilla::Testopia::TestCase->create($self->testcase);
|
||||
$self->testcase->{'case_id'} = $case->id;
|
||||
foreach my $attachment ( @{$self->attachments} ) {
|
||||
$attachment->{'case_id'} = $case->id;
|
||||
$attachment = Bugzilla::Testopia::Attachment->create($attachment);
|
||||
}
|
||||
foreach my $asciitag ( @{$self->tags} ) {
|
||||
$case->add_tag($asciitag);
|
||||
}
|
||||
|
||||
# Link the testcase to each of it's testplans.
|
||||
foreach my $testplan ( @testplan ) {
|
||||
$case->link_plan($testplan->id(),$case->id)
|
||||
}
|
||||
|
||||
# Code below requires the testplans to be linked into testcases before being run.
|
||||
$case->add_component($self->component_ids);
|
||||
|
||||
return $error_message;
|
||||
}
|
||||
|
||||
sub store_relationships {
|
||||
my ($self, @new_testcases) = @_;
|
||||
return unless $self->testcase->{'case_id'};
|
||||
my $testcase = Bugzilla::Testopia::TestCase->new($self->testcase->{'case_id'});
|
||||
|
||||
# Hashes are used because the entires in blocks and dependson must be unique.
|
||||
my %blocks = ();
|
||||
foreach my $block ( $self->get_testcase_ids("blocks",@new_testcases) ) {
|
||||
$blocks{$block}++;
|
||||
}
|
||||
|
||||
my $blocks_size = keys( %blocks );
|
||||
my %dependson = ();
|
||||
foreach my $dependson ( $self->get_testcase_ids("dependson",@new_testcases) ) {
|
||||
$dependson{$dependson}++;
|
||||
}
|
||||
|
||||
my $dependson_size = keys( %dependson );
|
||||
if ( ( $blocks_size > 0 ) || ( $dependson_size > 0 ) ) {
|
||||
# Need to add the current blocks and dependson from the Test Case; otherwise, they will
|
||||
# be removed.
|
||||
foreach my $block ( split(/ /,$testcase->blocked_list_uncached()) ) {
|
||||
$blocks{$block}++;
|
||||
}
|
||||
|
||||
foreach my $dependson ( split(/ /,$testcase->dependson_list_uncached()) ) {
|
||||
$dependson{$dependson}++;
|
||||
}
|
||||
|
||||
my @blocks = keys(%blocks);
|
||||
my @dependson = keys(%dependson);
|
||||
$testcase->update_deps( join(' ',@dependson ),join(' ',@blocks) );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::XmlTestCase
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The XmlTestCase structure stores data for the verfication processing. The database is not updated
|
||||
until a verfication pass is made through the XML data. Some of the TestCase class references are
|
||||
database references that will not be valid until the class has been stored in the database. This
|
||||
structure stores these references to be used during verfication and writting to the database.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$testcase = Bugzilla::Testopia::XMLTestcase->new($case_hash_ref);
|
||||
|
||||
$testcase->store();
|
||||
|
||||
=head1 FILEDS
|
||||
|
||||
=over
|
||||
|
||||
=item C<attachments>
|
||||
|
||||
Array - List of attachment hashes that will be used to create Testopia::Attachment objects
|
||||
for this test case.
|
||||
|
||||
=item C<blocks>
|
||||
|
||||
Testopia::XMLReferences Object representing the test cases that this tests case blocks.
|
||||
|
||||
=item C<category>
|
||||
|
||||
Testopia::Category object representing the category this case will belong to.
|
||||
|
||||
=item C<component_ids>
|
||||
|
||||
List of Bugzilla::Component ids that will be attached to this test case.
|
||||
|
||||
=item C<dependson>
|
||||
|
||||
Testopia::XMLReferences Object representing the test cases that this tests case depends on.
|
||||
|
||||
=item C<tags>
|
||||
|
||||
Array of strings - tags to apply to this test case.
|
||||
|
||||
=item C<testcase>
|
||||
|
||||
Hash representation of the test case that will be created.
|
||||
|
||||
=item C<testplan>
|
||||
|
||||
Bugzilla::Testopia::XmlReferences of plans to link to this test case
|
||||
|
||||
=back
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<add_attachment()>
|
||||
|
||||
Description: Adds an attachment to the list for later inclusion
|
||||
|
||||
Params: attachment - hash representing the Testopia::Attachment to add.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<add_component()>
|
||||
|
||||
Description: Adds a component to the list for later inclusion
|
||||
|
||||
Params: component_id - id of component to add.
|
||||
product_id - id of the product this component belongs to.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<add_tag()>
|
||||
|
||||
Description: Adds a tag for later inclusion.
|
||||
|
||||
Params: string - tag name
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<debug_display()>
|
||||
|
||||
Description: Prints debug information to STDERR
|
||||
|
||||
Params: None.
|
||||
|
||||
Returns: Nothing.
|
||||
|
||||
=item C<get_test_case_ids()>
|
||||
|
||||
Description: Gets a list of the newly created testcases for use in setting up relationships
|
||||
|
||||
Params: field - dependson or blocks.
|
||||
cases - array of test cases newly created by store.
|
||||
|
||||
Returns: A list of case ids.
|
||||
|
||||
=item C<store()>
|
||||
|
||||
Description: Saves a imported Test Case. This method insures that all Test Case attributes not stored
|
||||
in the Test Case object are created. The attributes include the Test Plan, tags, compoents,
|
||||
attachments and categories.
|
||||
|
||||
Params: test_plans - list of plans to link the newly created cases to.
|
||||
|
||||
Returns: error - if the store was not successful, an error message is returned.
|
||||
|
||||
=item C<store_relationships>
|
||||
|
||||
Description: Save the dependson and blocks relationships between Test Cases. This method can only be
|
||||
called after the Test Cases being imported have been stored. The dependson and blocks
|
||||
relationships use the Test Case identifier which is created only after the Test Case has
|
||||
been stored.
|
||||
|
||||
Params: test_cases - array of newly created case objects.
|
||||
|
||||
Returns: nothing.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item C<get_available_products()>
|
||||
|
||||
Description: Returns a list of products. This is the same code as Bugzilla::Testopia::TestPlan->get_available_products
|
||||
without view restrictions.
|
||||
|
||||
Params: None.
|
||||
|
||||
Returns: A array of product information.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
L<Bugzilla::Testopia::TestCase>
|
||||
|
||||
L<Bugzilla::Testopia::Xml>
|
||||
|
||||
L<Bugzilla::Testopia::XmlReferences>
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
David Koenig <dkoenig@novell.com>
|
|
@ -335,6 +335,16 @@ sub queries_available {
|
|||
return $self->{queries_available};
|
||||
}
|
||||
|
||||
sub testopia_queries {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $ref = $dbh->selectall_arrayref(
|
||||
"SELECT name, query FROM test_named_queries
|
||||
WHERE userid = ? AND isvisible = 1",
|
||||
{'Slice' =>{}}, $self->id);
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub settings {
|
||||
my ($self) = @_;
|
||||
|
||||
|
@ -632,7 +642,7 @@ sub get_selectable_products {
|
|||
my $class_restricted = Bugzilla->params->{'useclassification'} && $class_id;
|
||||
|
||||
if (!defined $self->{selectable_products}) {
|
||||
my $query = "SELECT id " .
|
||||
my $query = "(SELECT id, name AS pname " .
|
||||
" FROM products " .
|
||||
"LEFT JOIN group_control_map " .
|
||||
" ON group_control_map.product_id = products.id ";
|
||||
|
@ -642,10 +652,17 @@ sub get_selectable_products {
|
|||
$query .= " AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY;
|
||||
}
|
||||
$query .= " AND group_id NOT IN(" . $self->groups_as_string . ") " .
|
||||
" WHERE group_id IS NULL " .
|
||||
"ORDER BY name";
|
||||
" WHERE group_id IS NULL) " ;
|
||||
|
||||
|
||||
my $prod_ids = Bugzilla->dbh->selectcol_arrayref($query);
|
||||
$query .= "UNION (SELECT id, tr_products.name AS pname FROM products AS tr_products ".
|
||||
"INNER JOIN test_plans ON tr_products.id = test_plans.product_id ".
|
||||
"INNER JOIN test_plan_permissions ON test_plan_permissions.plan_id = test_plans.plan_id ".
|
||||
"WHERE test_plan_permissions.userid = ?)";
|
||||
|
||||
$query .= "ORDER BY pname ";
|
||||
|
||||
my $prod_ids = Bugzilla->dbh->selectcol_arrayref($query,undef,$self->id);
|
||||
$self->{selectable_products} = Bugzilla::Product->new_from_list($prod_ids);
|
||||
}
|
||||
|
||||
|
@ -925,6 +942,33 @@ sub derive_regexp_groups {
|
|||
$group_delete->execute($id, $group, GRANT_REGEXP) if $present;
|
||||
}
|
||||
}
|
||||
# Now do the same for Testopia test plans.
|
||||
$sth = $dbh->prepare("SELECT test_plan_permissions_regexp.plan_id,
|
||||
user_regexp, test_plan_permissions_regexp.permissions,
|
||||
test_plan_permissions.plan_id
|
||||
FROM test_plan_permissions_regexp
|
||||
LEFT JOIN test_plan_permissions
|
||||
ON test_plan_permissions_regexp.plan_id = test_plan_permissions.plan_id
|
||||
AND test_plan_permissions.userid = ?
|
||||
AND test_plan_permissions.grant_type = ?");
|
||||
|
||||
$sth->execute($id, GRANT_REGEXP);
|
||||
my $plan_insert = $dbh->prepare(q{INSERT INTO test_plan_permissions
|
||||
(userid, plan_id, permissions, grant_type)
|
||||
VALUES (?, ?, ?, ?)});
|
||||
my $plan_delete = $dbh->prepare(q{DELETE FROM test_plan_permissions
|
||||
WHERE userid = ?
|
||||
AND plan_id = ?
|
||||
AND grant_type = ?});
|
||||
|
||||
while (my ($planid, $regexp, $perms, $present) = $sth->fetchrow_array()) {
|
||||
if (($regexp ne '') && ($self->{login} =~ m/$regexp/i)) {
|
||||
$plan_insert->execute($id, $planid, $perms, GRANT_REGEXP) unless $present;
|
||||
} else {
|
||||
$plan_delete->execute($id, $planid, GRANT_REGEXP) if $present;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub product_responsibilities {
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::Build;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Testopia::Build;
|
||||
use Bugzilla::Testopia::Product;
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($build_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a build object hash
|
||||
my $build = new Bugzilla::Testopia::Build($build_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build;
|
||||
ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit;
|
||||
|
||||
$build->run_count();
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
sub check_build {
|
||||
my $self = shift;
|
||||
my ($name, $product) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($product =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($product);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($product);
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
return Bugzilla::Testopia::Build::check_build($name, $product, "THROWERROR");
|
||||
}
|
||||
|
||||
sub create{
|
||||
my $self = shift;
|
||||
my ($new_values) = @_; # Required: name, product_id
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
$new_values->{'product_id'} ||= $new_values->{'product'};
|
||||
delete $new_values->{'product'};
|
||||
|
||||
my $product;
|
||||
if ($new_values->{'product_id'} =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($new_values->{'product_id'});
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($new_values->{'product_id'});
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
$new_values->{'milestone'} ||= $product->default_milestone;
|
||||
if (! defined $new_values->{'isactive'}){
|
||||
$new_values->{'isactive'} = 1;
|
||||
}
|
||||
|
||||
my $build = Bugzilla::Testopia::Build->create($new_values);
|
||||
|
||||
# Result is new build
|
||||
return $build;
|
||||
}
|
||||
|
||||
sub update{
|
||||
my $self = shift;
|
||||
my ($id, $new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $build = new Bugzilla::Testopia::Build($id);
|
||||
ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Build'}) unless $build;
|
||||
ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit;
|
||||
|
||||
$build->set_name($new_values->{'name'}) if $new_values->{'name'};
|
||||
$build->set_description($new_values->{'description'}) if defined $new_values->{'description'};
|
||||
$build->set_milestone($new_values->{'milestone'}) if $new_values->{'milestone'};
|
||||
$build->set_isactive($new_values->{'isactive'} =~ /(true|1|yes)/i ? 1 : 0) if defined $new_values->{'isactive'};
|
||||
|
||||
$build->update;
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
# DEPRECATED use Build::get instead
|
||||
sub lookup_name_by_id {
|
||||
my $self = shift;
|
||||
my ($build_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
die "Invalid Build ID"
|
||||
unless defined $build_id && length($build_id) > 0 && $build_id > 0;
|
||||
|
||||
my $build = new Bugzilla::Testopia::Build($build_id);
|
||||
ThrowUserError('testopia-read-only', {'object' => $build->product}) unless $build->product->canedit;
|
||||
|
||||
my $result = defined $build ? $build->name : '';
|
||||
|
||||
# Result is build name string or empty string if ERROR
|
||||
return $result;
|
||||
}
|
||||
|
||||
# DEPRECATED use Build::check_build($name, $product) instead
|
||||
sub lookup_id_by_name {
|
||||
return { ERROR => 'This method is considered harmful and has been deprecated. Please use Build::check_build instead'};
|
||||
}
|
||||
|
||||
sub get_runs {
|
||||
my $self = shift;
|
||||
my ($build_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $build = new Bugzilla::Testopia::Build($build_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build;
|
||||
ThrowUserError('testopia-read-only', {'object' => $build}) unless $build->product->canview;
|
||||
|
||||
# Result is list of test runs for the given build
|
||||
return $build->runs();
|
||||
}
|
||||
|
||||
sub get_caseruns {
|
||||
my $self = shift;
|
||||
my ($build_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $build = new Bugzilla::Testopia::Build($build_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $build_id}) unless $build;
|
||||
ThrowUserError('testopia-read-only', {'object' => $build}) unless $build->product->canview;
|
||||
|
||||
# Result is list of test runs for the given build
|
||||
return $build->caseruns();
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::Build
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to manipulate Testopia Builds
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_build($name, $product)>
|
||||
|
||||
Description: Looks up and returns a build by name.
|
||||
|
||||
Params: $name - String: name of the build.
|
||||
$product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Hash: Matching Build object hash or error if not found.
|
||||
|
||||
=item C<create($values)>
|
||||
|
||||
Description: Creates a new build object and stores it in the database
|
||||
|
||||
Params: $values - Hash: A reference to a hash with keys and values
|
||||
matching the fields of the build to be created.
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
| Field | Type | Null | Description |
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
| product | Integer/String | Required | ID or Name of product |
|
||||
| name | String | Required | |
|
||||
| milestone | String | Optional | Defaults to product's default MS |
|
||||
| description | String | Optional | |
|
||||
| isactive | Boolean | Optional | Defaults to True (1) |
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
|
||||
Returns: The newly created object hash.
|
||||
|
||||
=item C<get($id)>
|
||||
|
||||
Description: Used to load an existing build from the database.
|
||||
|
||||
Params: $id - An integer representing the ID in the database
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Build object hash
|
||||
|
||||
=item C<get_caseruns($id)>
|
||||
|
||||
Description: Returns the list of case-runs that this Build is used in.
|
||||
|
||||
Params: $id - Integer: Build ID.
|
||||
|
||||
Returns: Array: List of case-run object hashes.
|
||||
|
||||
=item C<get_runs($id)>
|
||||
|
||||
Description: Returns the list of runs that this Build is used in.
|
||||
|
||||
Params: $id - Integer: Build ID.
|
||||
|
||||
Returns: Array: List of run object hashes.
|
||||
|
||||
=item C<lookup_id_by_name> B<DEPRECATED - CONSIDERED HARMFUL> Use Build::check_build instead
|
||||
|
||||
=item C<lookup_name_by_id> B<DEPRECATED> Use Build::get instead
|
||||
|
||||
=item C<update($id, $values)>
|
||||
|
||||
Description: Updates the fields of the selected build or builds.
|
||||
|
||||
Params: $id - Integer: A single build ID.
|
||||
|
||||
$values - Hash of keys matching Build fields and the new values
|
||||
to set each field to.
|
||||
+-------------+----------------+
|
||||
| Field | Type |
|
||||
+-------------+----------------+
|
||||
| name | String |
|
||||
| milestone | String |
|
||||
| description | String |
|
||||
| isactive | Boolean |
|
||||
+-------------+----------------+
|
||||
|
||||
Returns: Hash: The updated Build object hash.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::Build>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,386 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::Environment;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Testopia::Environment;
|
||||
use Bugzilla::Testopia::Search;
|
||||
use Bugzilla::Testopia::Table;
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($environment_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $environment = new Bugzilla::Testopia::Environment($environment_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment;
|
||||
ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview;
|
||||
|
||||
#Result is a environment hash map
|
||||
return $environment;
|
||||
}
|
||||
|
||||
sub check_environment {
|
||||
my $self = shift;
|
||||
my ($name, $product) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($product =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($product);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($product);
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
return Bugzilla::Testopia::Environment::check_environment($name, $product, 'THROWERROR');
|
||||
}
|
||||
|
||||
sub list {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$cgi->param("current_tab", "environment");
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $query->{$_});
|
||||
}
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
|
||||
# Result is an array of environment hash maps
|
||||
return Bugzilla::Testopia::Table->new('environment', 'tr_xmlrpc.cgi',$cgi,undef, $search->query())->list();
|
||||
|
||||
}
|
||||
|
||||
sub create {
|
||||
my $self = shift;
|
||||
my ($new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Setup field name aliasing
|
||||
$new_values->{'product_id'} ||= $new_values->{'product'};
|
||||
delete $new_values->{'product'};
|
||||
|
||||
my $product;
|
||||
if ($new_values->{'product_id'} =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($new_values->{'product_id'});
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($new_values->{'product_id'});
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
if (! defined $new_values->{'isactive'}){
|
||||
$new_values->{'isactive'} = 1;
|
||||
}
|
||||
|
||||
my $environment = Bugzilla::Testopia::Environment->create($new_values);
|
||||
|
||||
# Result is new environment
|
||||
return $environment;
|
||||
}
|
||||
|
||||
sub create_full {
|
||||
my $self = shift;
|
||||
my ($env_basename, $product, $environment) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($product =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($product);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($product);
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
my $env_id = Bugzilla::Testopia::Environment->create_full($env_basename, $product->id, $environment);
|
||||
|
||||
return $env_id;
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my ($environment_id, $new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
my $environment = new Bugzilla::Testopia::Environment($environment_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment;
|
||||
ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canedit;
|
||||
|
||||
$environment->set_name($new_values->{'name'}) if $new_values->{'name'};
|
||||
$environment->set_isactive($new_values->{'isactive'} =~ /(true|1|yes)/i ? 1 : 0) if defined $new_values->{'isactive'};
|
||||
|
||||
$environment->update();
|
||||
|
||||
# Result is modified environment, otherwise an exception will be thrown
|
||||
return $environment;
|
||||
}
|
||||
|
||||
sub get_runs {
|
||||
my $self = shift;
|
||||
my ($environment_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $environment = new Bugzilla::Testopia::Environment($environment_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment;
|
||||
ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview;
|
||||
|
||||
# Result is list of test runs for the given environment
|
||||
return $environment->runs();
|
||||
}
|
||||
|
||||
sub get_caseruns {
|
||||
my $self = shift;
|
||||
my ($environment_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $environment = new Bugzilla::Testopia::Environment($environment_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Environment', id => $environment_id}) unless $environment;
|
||||
ThrowUserError('testopia-read-only', {'object' => $environment}) unless $environment->canview;
|
||||
|
||||
# Result is list of test runs for the given environment
|
||||
return $environment->caseruns();
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::Environment
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to manipulate Testopia Environments
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_environment($name, $product)>
|
||||
|
||||
Description: Looks up and returns an environment by name.
|
||||
|
||||
Params: $name - String: name of the environment.
|
||||
$product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Hash: Matching Environment object hash or error if not found.
|
||||
|
||||
=item C<create($values)>
|
||||
|
||||
Description: Creates a new environment object and stores it in the database
|
||||
|
||||
Params: $values - Hash: A reference to a hash with keys and values
|
||||
matching the fields of the environment to be created.
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
| Field | Type | Null | Description |
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
| product_id | Integer/String | Required | ID or Name of product |
|
||||
| name | String | Required | |
|
||||
| isactive | Boolean | Optional | Defaults to True (1) |
|
||||
+-------------+----------------+-----------+------------------------------------+
|
||||
|
||||
Returns: The newly created object hash.
|
||||
|
||||
=item C<create_full($basename, $product, $envhash)>
|
||||
|
||||
Description: When an environment starting with $basename does not exist yet
|
||||
exactly matching $envhash, creates a new environment object, and any new
|
||||
elements, properties, values, and the mapping between them and stores it in
|
||||
the database. The full environment name that is created will be a conjunction
|
||||
of the $basename and a date and time stamp. Else returns id of existing env.
|
||||
|
||||
Params: $basename - String: starting name of the environment (remainder
|
||||
will be a date time conjunction added by this function)
|
||||
$product - Integer/String: product name or id of the product in the Database
|
||||
$envhash - Hash ref: Multilevel hash following a format: Top level hash
|
||||
keys are assumed to be categories. Bottom level
|
||||
values (leaves) and their immediate keys are assumed
|
||||
to be the values and properties in the database,
|
||||
respectively. Between the top level and bottom level
|
||||
keys are the elements. Everything will be created
|
||||
if it does not yet exist in the database except for
|
||||
the categories which must exist beforehand.
|
||||
|
||||
Here is an example of an $envhash (quotes removed):
|
||||
|
||||
{
|
||||
|
||||
Hardware => {
|
||||
|
||||
System Board => {
|
||||
Type => i386,
|
||||
Manufacturer => Ssystem manufacturer,
|
||||
Model => System product name
|
||||
},
|
||||
|
||||
Memory => {
|
||||
Total Physical => 2,015.33 MB
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
Software => {
|
||||
|
||||
BIOS => {
|
||||
Version/Date => American Megatrends Inc. 1.00, 11/25/2003
|
||||
},
|
||||
|
||||
Operating System => {
|
||||
|
||||
Version => 5.2.3790 Service Pack 2 Build 3790,
|
||||
Manufacturer => Microsoft Corporation,
|
||||
Name => Microsoft(R) Windows(R) Server 2003, Standard Edition
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
Harddrives => {
|
||||
|
||||
1 => {
|
||||
Card => 0,
|
||||
Firmware Revision => 34.06J34,
|
||||
Channel => 6,
|
||||
Model => WDC WD360GD-00FNA0
|
||||
},
|
||||
|
||||
0 => {
|
||||
Card => 0,
|
||||
Firmware Revision => 35.06K35,
|
||||
Channel => 7,
|
||||
Model => WDC WD360GD-00FNA0
|
||||
},
|
||||
|
||||
2 => {
|
||||
Card => 0,
|
||||
Firmware Revision => 27.08D27,
|
||||
Channel => 5,
|
||||
Model => WDC WD740GD-00FLA1
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
Returns: The environment id of the newly created or matching environment.
|
||||
|
||||
=item C<get($id)>
|
||||
|
||||
Description: Used to load an existing Environment from the database.
|
||||
|
||||
Params: $id - An integer representing the ID in the database
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Environment object hash
|
||||
|
||||
=item C<get_caseruns($id)>
|
||||
|
||||
Description: Returns the list of case-runs that this Environment is used in.
|
||||
|
||||
Params: $id - Integer: Environment ID.
|
||||
|
||||
Returns: Array: List of case-run object hashes.
|
||||
|
||||
=item C<get_runs($id)>
|
||||
|
||||
Description: Returns the list of runs that this Environment is used in.
|
||||
|
||||
Params: $id - Integer: Environment ID.
|
||||
|
||||
Returns: Array: List of run object hashes.
|
||||
|
||||
=item C<list($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting list of Environments
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields.
|
||||
|
||||
+--------------------------+
|
||||
| classification |
|
||||
| env_products |
|
||||
| env_categories |
|
||||
| env_elements |
|
||||
| env_properties |
|
||||
| env_expressions |
|
||||
| name |
|
||||
| env_value_selected_type |
|
||||
+--------------------------+
|
||||
|
||||
Returns: Array: Matching Environments are retuned in a list of hashes.
|
||||
|
||||
=item C<update($ids, $values)>
|
||||
|
||||
Description: Updates the fields of the selected environment or environments.
|
||||
|
||||
Params: $ids - Integer A single environment ID.
|
||||
|
||||
$values - Hash of keys matching Environment fields and the new values
|
||||
to set each field to.
|
||||
+-------------+----------------+
|
||||
| Field | Type |
|
||||
+-------------+----------------+
|
||||
| name | String |
|
||||
| isactive | Boolean |
|
||||
+-------------+----------------+
|
||||
|
||||
Returns: Hash: The updated environment object hash.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::Environment>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,423 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::Product;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Testopia::Product;
|
||||
|
||||
sub _validate {
|
||||
my ($product) = @_;
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($product =~ /^\d+$/){
|
||||
$product = Bugzilla::Testopia::Product->new($product);
|
||||
}
|
||||
else {
|
||||
$product = Bugzilla::Product::check_product($product);
|
||||
$product = Bugzilla::Testopia::Product->new($product->id);
|
||||
}
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $product}) unless $product;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $product}) if $product && !$product->canedit;
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a product object hash
|
||||
my $product = new Bugzilla::Testopia::Product($id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $id}) unless $product;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub check_product {
|
||||
my $self = shift;
|
||||
my ($name) = @_;
|
||||
|
||||
my $product = _validate($name);
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub check_category {
|
||||
my $self = shift;
|
||||
my ($name, $product) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
require Bugzilla::Testopia::Category;
|
||||
return Bugzilla::Testopia::Category->new(Bugzilla::Testopia::Category::check_case_category($name, $product));
|
||||
}
|
||||
|
||||
sub check_component {
|
||||
my $self = shift;
|
||||
my ($name, $product) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
ThrowUserError('testopia-read-only', {'object' => $product}) unless $product->canedit;
|
||||
require Bugzilla::Component;
|
||||
return Bugzilla::Component->check({product => $product, name => $name});
|
||||
}
|
||||
|
||||
sub get_builds {
|
||||
my $self = shift;
|
||||
my ($product, $active) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->builds($active);
|
||||
|
||||
}
|
||||
|
||||
sub get_category {
|
||||
my $self = shift;
|
||||
my ($id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
require Bugzilla::Testopia::Category;
|
||||
|
||||
my $category = Bugzilla::Testopia::Category->new($id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Category', id => $id}) unless $category;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $category->product}) unless $category->product->canedit;
|
||||
|
||||
delete $category->{'product'};
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
sub get_component {
|
||||
my $self = shift;
|
||||
my ($id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
require Bugzilla::Component;
|
||||
my $component = Bugzilla::Component->new($id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Component', id => $id}) unless $component;
|
||||
|
||||
my $product = Bugzilla::Testopia::Product->new($component->product_id);
|
||||
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $product}) unless $product->canedit;
|
||||
|
||||
return $component;
|
||||
}
|
||||
|
||||
sub get_cases {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->cases;
|
||||
}
|
||||
|
||||
sub get_categories {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->categories;
|
||||
}
|
||||
|
||||
sub get_components {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->components;
|
||||
}
|
||||
|
||||
sub get_environments {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->environments;
|
||||
}
|
||||
|
||||
sub get_milestones {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->milestones;
|
||||
}
|
||||
|
||||
sub get_plans {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->plans;
|
||||
}
|
||||
|
||||
sub get_runs {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->runs;
|
||||
}
|
||||
|
||||
sub get_tags {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->tags;
|
||||
}
|
||||
|
||||
sub get_versions {
|
||||
my $self = shift;
|
||||
my ($product) = @_;
|
||||
|
||||
$product = _validate($product);
|
||||
|
||||
return $product->versions;
|
||||
|
||||
}
|
||||
|
||||
sub lookup_name_by_id {
|
||||
return {ERROR=> 'This method id deprecated. Use Product::get instead.'};
|
||||
}
|
||||
sub lookup_id_by_name {
|
||||
return {ERROR=> 'This method id deprecated. Use Product::check_product instead.'};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::Product
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to expose Testopia Product data.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<check_category($name, $product)>
|
||||
|
||||
Description: Looks up and returns a category by name.
|
||||
|
||||
Params: $name - String: name of the category.
|
||||
$product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Hash: Matching Category object hash or error if not found.
|
||||
|
||||
=item C<check_component($name, $product)>
|
||||
|
||||
Description: Looks up and returns a component by name.
|
||||
|
||||
Params: $name - String: name of the category.
|
||||
$product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Hash: Matching component object hash or error if not found.
|
||||
|
||||
=item C<check_product($name, $product)>
|
||||
|
||||
Description: Looks up and returns a validated product.
|
||||
|
||||
Params: $name - String: name of the product.
|
||||
|
||||
Returns: Hash: Matching Product object hash or error if not found.
|
||||
|
||||
=item C<get($id)>
|
||||
|
||||
Description: Used to load an existing product from the database.
|
||||
|
||||
Params: $id - An integer representing the ID in the database
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::Product object hash
|
||||
|
||||
=item C<get_builds($product, $active)>
|
||||
|
||||
Description: Get the list of builds associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
$active - Boolean: True to only include builds where isactive is true.
|
||||
|
||||
Returns: Array: Returns an array of Build objects.
|
||||
|
||||
=item C<get_cases($product)>
|
||||
|
||||
Description: Get the list of cases associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of TestCase objects.
|
||||
|
||||
=item C<get_categories($product)>
|
||||
|
||||
Description: Get the list of categories associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Case Category objects.
|
||||
|
||||
=item C<get_category($id)>
|
||||
|
||||
Description: Get the category matching the given id.
|
||||
|
||||
Params: $id - Integer: ID of the category in the database.
|
||||
|
||||
Returns: Hash: Category object hash.
|
||||
|
||||
=item C<get_component($id)>
|
||||
|
||||
Description: Get the component matching the given id.
|
||||
|
||||
Params: $id - Integer: ID of the component in the database.
|
||||
|
||||
Returns: Hash: Component object hash.
|
||||
|
||||
=item C<get_components($product)>
|
||||
|
||||
Description: Get the list of components associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Component objects.
|
||||
|
||||
=item C<get_environments($product)>
|
||||
|
||||
Description: Get the list of environments associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Environment objects.
|
||||
|
||||
=item C<get_milestones($product)>
|
||||
|
||||
Description: Get the list of milestones associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Milestone objects.
|
||||
|
||||
=item C<get_plans($product)>
|
||||
|
||||
Description: Get the list of plans associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Test Plan objects.
|
||||
|
||||
=item C<get_runs($product)>
|
||||
|
||||
Description: Get the list of runs associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Test Run objects.
|
||||
|
||||
=item C<get_tags($product)>
|
||||
|
||||
Description: Get the list of tags associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Tags objects.
|
||||
|
||||
=item C<get_versions($product)>
|
||||
|
||||
Description: Get the list of versions associated with this product.
|
||||
|
||||
Params: $product - Integer/String
|
||||
Integer: product_id of the product in the Database
|
||||
String: Product name
|
||||
|
||||
Returns: Array: Returns an array of Version objects.
|
||||
|
||||
=item C<lookup_name_by_id> B<DEPRECATED> Use Product::get instead
|
||||
|
||||
=item C<lookup_id_by_name> B<DEPRECATED - CONSIDERED HARMFUL> Use Product::check_product instead
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::Product>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,716 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::TestCaseRun;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Testopia::Constants;
|
||||
use Bugzilla::Testopia::Search;
|
||||
use Bugzilla::Testopia::Table;
|
||||
use Bugzilla::Testopia::TestCaseRun;
|
||||
use Bugzilla::Testopia::Util;
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($build_id && $build_id !~ /^\d+$/){
|
||||
my $run = Bugzilla::Testopia::TestRun->new($run_id);
|
||||
ThrowUserError('invalid-test-id-non-existent') unless $run;
|
||||
my $build = Bugzilla::Testopia::Build::check_build($build_id, $run->product, "THROW");
|
||||
$build_id = $build->id;
|
||||
}
|
||||
if ($env_id && $env_id !~ /^\d+$/){
|
||||
my $run = Bugzilla::Testopia::TestRun->new($run_id);
|
||||
ThrowUserError('invalid-test-id-non-existent') unless $run;
|
||||
my $environment = Bugzilla::Testopia::Build::check_environment($env_id, $run->product, "THROW");
|
||||
$env_id = $environment->id;
|
||||
}
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview;
|
||||
|
||||
return $caserun;
|
||||
}
|
||||
|
||||
sub list {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "case_run");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
|
||||
return Bugzilla::Testopia::Table->new('case_run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list();
|
||||
}
|
||||
|
||||
sub list_count {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "case_run");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
return Bugzilla::Testopia::Table->new('case_run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count();
|
||||
}
|
||||
|
||||
sub create {
|
||||
my $self = shift;
|
||||
my ($new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $run = Bugzilla::Testopia::TestRun->new($new_values->{'run_id'});
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $new_values->{'run_id'}}) unless $run;
|
||||
ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit;
|
||||
|
||||
$new_values->{'build_id'} ||= $new_values->{'build'};
|
||||
$new_values->{'environment_id'} ||= $new_values->{'environment'};
|
||||
|
||||
delete $new_values->{'build'};
|
||||
delete $new_values->{'environment'};
|
||||
|
||||
if (trim($new_values->{'build_id'}) !~ /^\d+$/ ){
|
||||
my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $run->plan->product, "THROWERROR");
|
||||
$new_values->{'build_id'} = $build->id;
|
||||
}
|
||||
if (trim($new_values->{'environment_id'}) !~ /^\d+$/ ){
|
||||
my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $run->plan->product, "THROWERROR");
|
||||
$new_values->{'environment_id'} = $environment->id;
|
||||
}
|
||||
|
||||
$new_values->{'case_run_status_id'} ||= $new_values->{'status'};
|
||||
$new_values->{'case_run_status_id'} ||= IDLE;
|
||||
|
||||
delete $new_values->{'status'};
|
||||
|
||||
my $caserun = Bugzilla::Testopia::TestCaseRun->create($new_values);
|
||||
|
||||
# Result is new test case run object hash
|
||||
return $caserun;
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id, $new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my @caseruns;
|
||||
my @ids = Bugzilla::Testopia::Util::process_list($run_id);
|
||||
if (ref $case_id eq 'HASH' && !$build_id){
|
||||
$new_values = $case_id;
|
||||
foreach my $id (@ids){
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($id);
|
||||
if ($caserun){
|
||||
push @caseruns, $caserun;
|
||||
}
|
||||
else {
|
||||
push @caseruns, {ERROR => 'Case-run does not exist'};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach my $id (@ids){
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id,$case_id,$build_id,$env_id);
|
||||
if ($caserun){
|
||||
push @caseruns, $caserun;
|
||||
}
|
||||
else {
|
||||
push @caseruns, {ERROR => 'Case-run does not exist'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$new_values->{'case_run_status_id'} ||= $new_values->{'status'};
|
||||
$new_values->{'build_id'} ||= $new_values->{'build'};
|
||||
$new_values->{'environment_id'} ||= $new_values->{'environment'};
|
||||
|
||||
my @results;
|
||||
|
||||
foreach my $caserun (@caseruns){
|
||||
if ($caserun->{'ERROR'}){
|
||||
push @results, $caserun;
|
||||
next;
|
||||
}
|
||||
unless ($caserun->canedit){
|
||||
push @results, {ERROR => "You do not have rights to edit this test case"};
|
||||
next;
|
||||
}
|
||||
|
||||
$run_id = $caserun->run_id;
|
||||
$case_id = $caserun->case_id;
|
||||
$build_id ||= $caserun->build->id;
|
||||
$env_id ||= $caserun->environment->id;
|
||||
|
||||
# Check to see what has changed then use set methods
|
||||
# The order is important. We need to check if the build or environment has
|
||||
# Changed so that we can switch to the right record first.
|
||||
if ($new_values->{'build_id'} && $new_values->{'environment_id'}){
|
||||
$caserun = $caserun->switch($new_values->{'build_id'}, $new_values->{'environment_id'}, $run_id, $case_id);
|
||||
}
|
||||
elsif ($new_values->{'build_id'}){
|
||||
$caserun = $caserun->switch($new_values->{'build_id'}, $env_id, $run_id, $case_id);
|
||||
}
|
||||
elsif ($new_values->{'environment_id'}){
|
||||
$caserun = $caserun->switch($build_id, $new_values->{'environment_id'}, $run_id, $case_id);
|
||||
}
|
||||
|
||||
# Now that we know we are working with the right record, update it.
|
||||
if ($new_values->{'assignee'}){
|
||||
print STDERR "THE ASSSIGNEEE IS ". $new_values->{'assignee'};
|
||||
$caserun->set_assignee($new_values->{'assignee'});
|
||||
print STDERR "NOW IT IS ". $caserun->assignee->{'login_name'};
|
||||
}
|
||||
|
||||
if ($new_values->{'case_run_status_id'}){
|
||||
$caserun->set_status($new_values->{'case_run_status_id'}, $new_values->{'update_bugs'});
|
||||
}
|
||||
|
||||
if ($new_values->{'sortkey'}){
|
||||
$caserun->set_sortkey($new_values->{'sortkey'});
|
||||
}
|
||||
|
||||
if ($new_values->{'notes'}){
|
||||
$caserun->append_note($new_values->{'notes'});
|
||||
}
|
||||
|
||||
# Remove assignee user object and replace with just assignee id
|
||||
if (ref $caserun->{'assignee'} eq 'Bugzilla::User'){
|
||||
$caserun->{assignee} = $caserun->{assignee}->id();
|
||||
}
|
||||
|
||||
# Result is modified test case run on success, otherwise an exception will be thrown
|
||||
return $caserun if scalar @caseruns == 1;
|
||||
push @results, $caserun;
|
||||
}
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub lookup_status_id_by_name {
|
||||
my $self = shift;
|
||||
my ($name) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is test case run status id for the given test case run status name
|
||||
return Bugzilla::Testopia::TestCaseRun::lookup_status_by_name($name);
|
||||
}
|
||||
|
||||
sub lookup_status_name_by_id {
|
||||
my $self = shift;
|
||||
my ($id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is test case run status name for the given test case run status id
|
||||
return Bugzilla::Testopia::TestCaseRun::lookup_status($id);
|
||||
}
|
||||
|
||||
sub get_history {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview;
|
||||
|
||||
return $caserun->get_case_run_list;
|
||||
|
||||
}
|
||||
|
||||
sub attach_bug {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id, $bug_ids) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
# If we have just the id, the third arg will not be set.
|
||||
$bug_ids = $case_id unless $build_id;
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-read-only', {'object' => $caserun}) unless $caserun->canedit;
|
||||
|
||||
$caserun->attach_bug($bug_ids);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub detach_bug {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id, $bug_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
# If we have just the id, the third arg will not be set.
|
||||
$bug_id = $case_id unless $build_id;
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-read-only', {'object' => $caserun}) unless $caserun->canedit;
|
||||
|
||||
$caserun->detach_bug($bug_id);
|
||||
|
||||
# Result undef on success, otherwise an exception will be thrown
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_bugs {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview;
|
||||
|
||||
return $caserun->bugs;
|
||||
}
|
||||
|
||||
sub get_completion_time {
|
||||
my $self = shift;
|
||||
my ($run_id, $case_id, $build_id, $env_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
#Result is a test case run hash map
|
||||
my $caserun = new Bugzilla::Testopia::TestCaseRun($run_id, $case_id, $build_id, $env_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Case Run', id => $run_id}) unless $caserun;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $caserun}) unless $caserun->canview;
|
||||
|
||||
return $caserun->completion_time;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::TestCaseRun
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to manipulate Testopia TestCaseRuns.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Test case-runs are the mapping of a test case in a given run for a particular
|
||||
build and environment. There are therefore two ways to refer to a given
|
||||
case-run:
|
||||
|
||||
By ID: The unique case_run_id
|
||||
By Combination: $run_id, $case_id, $build_id, $environment_id
|
||||
|
||||
TestCaseRun methods are overloaded to support either of these two
|
||||
methods of looking up the case-run you are interested in.
|
||||
|
||||
B<EXAMPLE:>
|
||||
|
||||
TestCaseRun->get($caserun_id)
|
||||
TestCaseRun->get($run_id, $case_id, $build_id, $environment_id)
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<attach_bug($caserun_id, $bug_ids)>
|
||||
|
||||
Description: Add one or more bugs to the selected test case-runs.
|
||||
|
||||
Params: $case_run_id - Integer: An integer representing the ID in the database.
|
||||
|
||||
$bug_ids - Integer/Array/String: An integer or alias representing the ID in the database,
|
||||
an array of bug_ids or aliases, or a string of comma separated bug_ids.
|
||||
|
||||
Returns: undef.
|
||||
|
||||
=item C<attach_bug($run_id, $case_id, $build_id, $environment_id, $bug_ids)>
|
||||
|
||||
Description: Add one or more bugs to the selected test case-runs.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
$bug_ids - Integer/Array/String: An integer or alias representing the ID in the database,
|
||||
an array of bug_ids or aliases, or a string of comma separated bug_ids.
|
||||
|
||||
Returns: undef.
|
||||
|
||||
=item C<create($values)>
|
||||
|
||||
Description: Creates a new Test Case Run object and stores it in the database.
|
||||
|
||||
Params: $values - Hash: A reference to a hash with keys and values
|
||||
matching the fields of the test case to be created.
|
||||
+--------------------+----------------+-----------+------------------------------------------------+
|
||||
| Field | Type | Null | Description |
|
||||
+--------------------+----------------+-----------+------------------------------------------------+
|
||||
| run_id | Integer | Required | Test Run Number |
|
||||
| case_id | Integer/String | Required | ID or alias of test case |
|
||||
| build | Integer/String | Required | ID or name of a Build in plan's product |
|
||||
| environment | Integer/String | Required | ID or name of an Environment in plan's product |
|
||||
| assignee | Integer/String | Optional | Defaults to test case default tester |
|
||||
| status | String | Optional | Defaults to "IDLE" |
|
||||
| case_text_version | Integer | Optional | |
|
||||
| notes | String | Optional | |
|
||||
| sortkey | Integer | Optional | a.k.a. Index |
|
||||
+--------------------+----------------+-----------+------------------------------------------------+
|
||||
Valid statuses include: IDLE, PASSED, FAILED, RUNNING, PAUSED, BLOCKED, ERROR
|
||||
|
||||
Returns: The newly created object hash.
|
||||
|
||||
=item C<detach_bug($caserun_id, $bug_id)>
|
||||
|
||||
Description: Remove a bug from a test case-run.
|
||||
|
||||
Params: $caserun_id - Integer: An integer representing the ID in the database.
|
||||
|
||||
$bug_ids - Integer/Array/String: An integer or alias representing the ID in the database,
|
||||
an array of bug_ids or aliases, or a string of comma separated bug_ids.
|
||||
|
||||
Returns: undef.
|
||||
|
||||
=item C<detach_bug($run_id, $case_id, $build_id, $environment_id, $bug_id)>
|
||||
|
||||
Description: Remove a bug from a test case-run.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
$bug_id - Integer: An integer or alias representing the ID of
|
||||
the bug in the database,
|
||||
|
||||
Returns: undef.
|
||||
|
||||
=item C<get($caserun_id)>
|
||||
|
||||
Description: Used to load an existing test case-run from the database.
|
||||
|
||||
Params: $caserun_id - Integer: An integer representing the ID in
|
||||
the database for this case-run.
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::TestCaseRun object hash
|
||||
|
||||
=item C<get($run_id, $case_id, $build_id, $environment_id)>
|
||||
|
||||
Description: Used to load an existing test case from the database.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
Returns: A blessed Bugzilla::Testopia::TestCaseRun object hash
|
||||
|
||||
=item C<get_bugs($caserun_id)>
|
||||
|
||||
Description: Get the list of bugs that are associated with this test case.
|
||||
|
||||
Params: $caserun_id - Integer: An integer representing the ID in
|
||||
the database for this case-run.
|
||||
|
||||
Returns: Array: An array of bug object hashes.
|
||||
|
||||
=item C<get_bugs($run_id, $case_id, $build_id, $environment_id)>
|
||||
|
||||
Description: Get the list of bugs that are associated with this test case.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
Returns: Array: An array of bug object hashes.
|
||||
|
||||
=item C<get_completion_time($caserun_id)>
|
||||
|
||||
Description: Returns the time in seconds that it took for this case to complete.
|
||||
|
||||
Params: $caserun_id - Integer: An integer representing the ID in
|
||||
the database for this case-run.
|
||||
|
||||
Returns: Integer: Seconds since run was started till this case was completed.
|
||||
|
||||
=item C<get_completion_time($run_id, $case_id, $build_id, $environment_id)>
|
||||
|
||||
Description: Returns the time in seconds that it took for this case to complete.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
Returns: Integer: Seconds since run was started till this case was completed.
|
||||
|
||||
=item C<get_history($caserun_id)>
|
||||
|
||||
Description: Get the list of case-runs for all runs this case appears in.
|
||||
To limit this list by build or other attribute, see TestCaseRun::list.
|
||||
|
||||
Params: $caserun_id - Integer: An integer representing the ID in
|
||||
the database for this case-run.
|
||||
|
||||
Returns: Array: An array of case-run object hashes.
|
||||
|
||||
=item C<get_history($run_id, $case_id, $build_id, $environment_id)>
|
||||
|
||||
Description: Get the list of case-runs for all runs this case appears in.
|
||||
To limit this list by build or other attribute, see TestCaseRun::list.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
Returns: Array: An array of case-run object hashes.
|
||||
|
||||
=item C<list($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting list of test cases.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields.
|
||||
|
||||
+--------------------------------------------------------+
|
||||
| Case-Run Search Parameters |
|
||||
+--------------------------------------------------------+
|
||||
| Key | Valid Values |
|
||||
| andor | 1: Author AND tester, 0: OR |
|
||||
| assignee | A bugzilla login (email address) |
|
||||
| assignee_type | (select from email_variants) |
|
||||
| build | String |
|
||||
| build_id | Integer |
|
||||
| case_id | comma separated integers |
|
||||
| case_run_status | String: Status |
|
||||
| case_run_status_id | Integer: Status |
|
||||
| case_summary | String: Requirement |
|
||||
| case_summary_type | (select from query_variants) |
|
||||
| category | String: Category Name |
|
||||
| category_id | Integer |
|
||||
| component | String: Component Name |
|
||||
| environment | String |
|
||||
| environment_id | Integer |
|
||||
| isactive | 0: Only show current 1: show all |
|
||||
| isautomated | 1: true 0: false |
|
||||
| milestone | String |
|
||||
| notes | String |
|
||||
| notes_type | (select from query_variants) |
|
||||
| plan_id | comma separated integers |
|
||||
| priority | String: Priority |
|
||||
| priority_id | Integer |
|
||||
| product | String |
|
||||
| product_id | Integer |
|
||||
| requirement | String: Requirement |
|
||||
| requirement_type | (select from query_variants) |
|
||||
| run_id | comma separated integers |
|
||||
| run_product_version | String |
|
||||
| run_status | 1: RUNNING 0: STOPPED |
|
||||
| tags | String |
|
||||
| tags_type | (select from tag_variants) |
|
||||
| testedby | A bugzilla login (email address) |
|
||||
| testedby_type | (select from email_variants) |
|
||||
+--------------------------------------------------------+
|
||||
|
||||
+---------------------------------------------------+
|
||||
| Paging and Sorting |
|
||||
+---------------------------------------------------+
|
||||
| Key | Description |
|
||||
| dir | "ASC" or "DESC" |
|
||||
| order | field to sort by |
|
||||
+---------------------------------------------------+
|
||||
| page_size | integer: how many per page |
|
||||
| page | integer: page number |
|
||||
| +++++++ OR +++++++ |
|
||||
| start | integer: Start with which record |
|
||||
| limit | integer: limit to how many |
|
||||
+---------------------------------------------------+
|
||||
| viewall | 1: returns all records |
|
||||
+---------------------------------------------------+
|
||||
* The default is to only return 25 records at a time
|
||||
|
||||
+----------------------------------------------------+
|
||||
| query_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
+----------------+-----------------------------------+
|
||||
|
||||
+-------------------------------------+
|
||||
| email_variants |
|
||||
+--------------+----------------------+
|
||||
| Key | Description |
|
||||
| substring | contains |
|
||||
| exact | is |
|
||||
| regexp | matches regexp |
|
||||
| notregexp | doesn't match regexp |
|
||||
+--------------+----------------------+
|
||||
|
||||
+----------------------------------------------------+
|
||||
| tag_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| anyexact | is tagged with |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| nowords | contains none of the words |
|
||||
+----------------------------------------------------+
|
||||
|
||||
Returns: Array: Matching test cases are retuned in a list of hashes.
|
||||
|
||||
=item C<list_count($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting count of cases.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields (see list).
|
||||
|
||||
Returns: Integer - total matching cases.
|
||||
|
||||
=item C<lookup_status_name_by_id>
|
||||
|
||||
Params: $id - Integer: ID of the status to return
|
||||
|
||||
Returns: String: the status name.
|
||||
|
||||
=item C<lookup_status_id_by_name>
|
||||
|
||||
Params: $name - String: the status name.
|
||||
|
||||
Returns: Integer: ID of the status.
|
||||
|
||||
=item C<update($caserun_ids, $values)>
|
||||
|
||||
Description: Updates the fields of the selected case-runs.
|
||||
|
||||
Params: $caserun_ids - Integer/String/Array
|
||||
Integer: A single TestCaseRun ID.
|
||||
String: A comma separates string of TestCaseRun IDs for batch
|
||||
processing.
|
||||
Array: An array of TestCaseRun IDs for batch mode processing
|
||||
|
||||
$values - Hash of keys matching TestCaseRun fields and the new values
|
||||
to set each field to.
|
||||
+--------------------+----------------+
|
||||
| Field | Type |
|
||||
+--------------------+----------------+
|
||||
| build | Integer/String |
|
||||
| environment | Integer/String |
|
||||
| assignee | Integer/String |
|
||||
| status | String |
|
||||
| notes | String |
|
||||
| sortkey | Integer |
|
||||
| update_bugs | Boolean | 1: Reopen bugs on FAILED 0: Don't change bug status
|
||||
+--------------------+----------------+
|
||||
|
||||
Returns: Hash/Array: In the case of a single object, it is returned. If a
|
||||
list was passed, it returns an array of object hashes. If the
|
||||
update on any particular object failed, the hash will contain a
|
||||
ERROR key and the message as to why it failed.
|
||||
|
||||
=item C<update($run_id, $case_id, $build_id, $environment_id, $values)>
|
||||
|
||||
Description: Updates the fields of the selected case-run.
|
||||
|
||||
Params: $case_id - Integer: An integer representing the ID of the test case in the database.
|
||||
$run_id - Integer: An integer representing the ID of the test run in the database.
|
||||
$build_id - Integer: An integer representing the ID of the test build in the database.
|
||||
$environment_id - Integer: An integer representing the ID of the environment in the database.
|
||||
|
||||
$values - Hash of keys matching TestCaseRun fields and the new values
|
||||
to set each field to. See above.
|
||||
|
||||
Returns: Hash/Array: In the case of a single object, it is returned. If a
|
||||
list was passed, it returns an array of object hashes. If the
|
||||
update on any particular object failed, the hash will contain a
|
||||
ERROR key and the message as to why it failed.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::TestCaseRun>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,584 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::TestPlan;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use Bugzilla::Testopia::TestPlan;
|
||||
use Bugzilla::Testopia::Search;
|
||||
use Bugzilla::Testopia::Table;
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a plan object hash
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
$plan->test_run_count();
|
||||
$plan->test_case_count();
|
||||
|
||||
return $plan;
|
||||
}
|
||||
|
||||
sub list {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "plan");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
|
||||
return Bugzilla::Testopia::Table->new('plan','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list();
|
||||
}
|
||||
|
||||
sub list_count {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "plan");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
return Bugzilla::Testopia::Table->new('plan','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count();
|
||||
}
|
||||
|
||||
sub create {
|
||||
my $self =shift;
|
||||
my ($new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
$new_values->{'product_id'} ||= $new_values->{'product'};
|
||||
$new_values->{'type_id'} ||= $new_values->{'type'};
|
||||
delete $new_values->{'product'};
|
||||
delete $new_values->{'type'};
|
||||
|
||||
$new_values->{'author_id'} ||= Bugzilla->user->id;
|
||||
|
||||
# Canedit check is performed in TestPlan::_check_product
|
||||
my $plan = Bugzilla::Testopia::TestPlan->create($new_values);
|
||||
|
||||
return $plan;
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self =shift;
|
||||
my ($plan_id, $new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit;
|
||||
|
||||
$new_values->{'type_id'} ||= $new_values->{'type'};
|
||||
|
||||
$plan->set_name(trim($new_values->{'name'}));
|
||||
$plan->set_default_product_version($new_values->{'default_product_version'});
|
||||
$plan->set_type($new_values->{'type_id'});
|
||||
$plan->set_isactive($new_values->{'isactive'});
|
||||
|
||||
$plan->update();
|
||||
|
||||
# Result is modified test plan, otherwise an exception will be thrown
|
||||
return $plan;
|
||||
}
|
||||
|
||||
sub get_text {
|
||||
my $self = shift;
|
||||
my ($plan_id, $version) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
#Result is the latest test plan doc hash map
|
||||
return $plan->text($version);
|
||||
}
|
||||
|
||||
sub store_text {
|
||||
my $self = shift;
|
||||
my ($plan_id, $text, $author_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
$author_id ||= Bugzilla->user->id;
|
||||
if ($author_id !~ /^\d+$/){
|
||||
$author_id = Bugzilla::User::login_to_id($author_id, "THROWERROR");
|
||||
}
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit;
|
||||
|
||||
my $version = $plan->store_text($plan_id, $author_id, $text);
|
||||
|
||||
# Result is new test plan doc version on success, otherwise an exception will be thrown
|
||||
return $version;
|
||||
}
|
||||
|
||||
sub get_test_cases {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a plan object hash
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
# Result is list of test cases for the given test plan
|
||||
return $plan->test_cases;
|
||||
}
|
||||
|
||||
sub get_test_runs {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a plan object hash
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
# Result is list of test runs for the given test plan
|
||||
return $plan->test_runs;
|
||||
}
|
||||
|
||||
sub get_change_history {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
# Result list of changes otherwise an exception will be thrown
|
||||
return $plan->history;
|
||||
}
|
||||
|
||||
sub get_product {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a plan object hash
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Product', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
# Result is list of test cases for the given test plan
|
||||
return $plan->product;
|
||||
}
|
||||
|
||||
sub lookup_type_name_by_id {
|
||||
my $self =shift;
|
||||
my ($id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is test plan type name for the given test plan type id
|
||||
return lookup_type($id);
|
||||
}
|
||||
|
||||
sub lookup_type_id_by_name {
|
||||
my $self =shift;
|
||||
my ($name) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is test plan type id for the given test plan type name
|
||||
return Bugzilla::Testopia::TestPlan::lookup_type_by_name($name);
|
||||
}
|
||||
|
||||
sub add_tag {
|
||||
my $self = shift;
|
||||
my ($plan_ids, $tags) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my @ids = Bugzilla::Testopia::Util::process_list($plan_ids);
|
||||
my @results;
|
||||
foreach my $id (@ids){
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($id);
|
||||
unless ($plan){
|
||||
push @results, {ERROR => "TestPlan $id does not exist"};
|
||||
next;
|
||||
}
|
||||
unless ($plan->canedit){
|
||||
push @results, {ERROR => "You do not have rights to edit this test plan"};
|
||||
next;
|
||||
}
|
||||
eval {
|
||||
$plan->add_tag($tags);
|
||||
};
|
||||
if ($@){
|
||||
push @results, {ERROR => $@};
|
||||
}
|
||||
}
|
||||
# @results will be empty if successful
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub remove_tag {
|
||||
my $self = shift;
|
||||
my ($plan_id, $tag_name) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-read-only', {'object' => $plan}) unless $plan->canedit;
|
||||
|
||||
$plan->remove_tag($tag_name);
|
||||
|
||||
# Result 0 on success, otherwise an exception will be thrown
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub get_tags {
|
||||
my $self = shift;
|
||||
my ($plan_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = new Bugzilla::Testopia::TestPlan($plan_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Plan', id => $plan_id}) unless $plan;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $plan}) unless $plan->canview;
|
||||
|
||||
my @results;
|
||||
foreach my $tag (@{$plan->tags}){
|
||||
push @results, $tag->name;
|
||||
}
|
||||
# Result list of tags otherwise an exception will be thrown
|
||||
return \@results;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::TestPlan
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to manipulate Testopia TestPlans
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<add_tag($plan_ids, $tags)>
|
||||
|
||||
Description: Add one or more tags to the selected test plans.
|
||||
|
||||
Params: $plan_ids - Integer/Array/String: An integer representing the ID of the plan in the database,
|
||||
an arry of plan_ids, or a string of comma separated plan_ids.
|
||||
|
||||
$tags - String/Array - A single tag, an array of tags,
|
||||
or a comma separated list of tags.
|
||||
|
||||
Returns: Array: empty on success or an array of hashes with failure
|
||||
codes if a failure occured.
|
||||
|
||||
=item C<create($values)>
|
||||
|
||||
Description: Creates a new Test Plan object and stores it in the database.
|
||||
|
||||
Params: $values - Hash: A reference to a hash with keys and values
|
||||
matching the fields of the test plan to be created.
|
||||
+-------------------------+----------------+-----------+------------------------------------+
|
||||
| Field | Type | Null | Description |
|
||||
+-------------------------+----------------+-----------+------------------------------------+
|
||||
| product | Integer/String | Required | ID or Name of product |
|
||||
| name | String | Required | |
|
||||
| type | Integer/String | Required | ID or name of plan type |
|
||||
| default_product_version | String | Required | |
|
||||
| isactive | Boolean | Optional | 0: Archived 1: Active (Default 1) |
|
||||
+-------------------------+----------------+-----------+------------------------------------+
|
||||
|
||||
Returns: The newly created object hash.
|
||||
|
||||
=item C<get($plan_id)>
|
||||
|
||||
Description: Used to load an existing test plan from the database.
|
||||
|
||||
Params: $id - Integer/String: An integer representing the ID of this plan in the database
|
||||
|
||||
Returns: Hash: A blessed Bugzilla::Testopia::TestPlan object hash
|
||||
|
||||
=item C<get_change_history($plan_id)>
|
||||
|
||||
Description: Get the list of changes to the fields of this plan.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of this plan in the database
|
||||
|
||||
Returns: Array: An array of hashes with changed fields and their details.
|
||||
|
||||
=item C<get_product($plan_id)>
|
||||
|
||||
Description: Get the Product the plan is assiciated with.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of the plan in the database.
|
||||
|
||||
Returns: Hash: A blessed Bugzilla::Testopia::Product hash.
|
||||
|
||||
=item C<get_tags($plan_id)>
|
||||
|
||||
Description: Get the list of tags attached to this plan.
|
||||
|
||||
Params: $plan_id - Integer An integer representing the ID of this plan in the database
|
||||
|
||||
Returns: Array: An array of tag object hashes.
|
||||
|
||||
=item C<get_test_cases($plan_id)>
|
||||
|
||||
Description: Get the list of cases that this plan is linked to.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of the plan in the database
|
||||
|
||||
Returns: Array: An array of test case object hashes.
|
||||
|
||||
=item C<get_test_runs($plan_id)>
|
||||
|
||||
Description: Get the list of runs in this plan.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of this plan in the database
|
||||
|
||||
Returns: Array: An array of test run object hashes.
|
||||
|
||||
=item C<get_text($plan_id, $version)>
|
||||
|
||||
Description: The plan document for a given test plan.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of this plan in the database
|
||||
|
||||
$version - Integer: (OPTIONAL) The version of the text you want returned.
|
||||
Defaults to the latest.
|
||||
|
||||
Returns: Hash: Text and author information.
|
||||
|
||||
=item C<list($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting list of test plans.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields.
|
||||
|
||||
+--------------------------------------------------------+
|
||||
| Plan Search Parameters |
|
||||
+--------------------------------------------------------+
|
||||
| Key | Valid Values |
|
||||
| author | A bugzilla login (email address) |
|
||||
| author_type | (select from email_variants) |
|
||||
| plan_id | comma separated integers |
|
||||
| plan_text | String |
|
||||
| plan_text_type | (select from query_variants) |
|
||||
| plan_type | String: Product Name |
|
||||
| product | String: Product Name |
|
||||
| product_id | Integer |
|
||||
| tags | String |
|
||||
| tags_type | (select from tag_variants) |
|
||||
| type_id | Integer |
|
||||
| version | String: Product version |
|
||||
+--------------------------------------------------------+
|
||||
|
||||
+--------------------------------------------------------+
|
||||
| Paging and Sorting |
|
||||
+--------------------------------------------------------+
|
||||
| Key | Description |
|
||||
| dir | "ASC" or "DESC" |
|
||||
| order | field to sort by |
|
||||
+--------------------------------------------------------+
|
||||
| page_size | integer: how many per page |
|
||||
| page | integer: page number |
|
||||
| +++++++ OR +++++++ |
|
||||
| start | integer: Start with which record |
|
||||
| limit | integer: limit to how many |
|
||||
+--------------------------------------------------------+
|
||||
| viewall | 1: returns all records 0: first 25 |
|
||||
+--------------------------------------------------------+
|
||||
* The default is to only return 25 records at a time
|
||||
|
||||
+----------------------------------------------------+
|
||||
| query_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
+----------------+-----------------------------------+
|
||||
|
||||
+-------------------------------------+
|
||||
| email_variants |
|
||||
+--------------+----------------------+
|
||||
| Key | Description |
|
||||
| substring | contains |
|
||||
| exact | is |
|
||||
| regexp | matches regexp |
|
||||
| notregexp | doesn't match regexp |
|
||||
+--------------+----------------------+
|
||||
|
||||
+----------------------------------------------------+
|
||||
| tag_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| anyexact | is tagged with |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| nowords | contains none of the words |
|
||||
+----------------------------------------------------+
|
||||
|
||||
Returns: Array: Matching test plans are retuned in a list of plan object hashes.
|
||||
|
||||
=item C<list_count($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting count of plans.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields (see list).
|
||||
|
||||
Returns: Integer - total matching plans.
|
||||
|
||||
=item C<lookup_type_id_by_name>
|
||||
|
||||
Params: $name - String: the plan type.
|
||||
|
||||
Returns: Integer: ID of the plan type.
|
||||
|
||||
=item C<lookup_type_name_by_id>
|
||||
|
||||
Params: $id - Integer: ID of the plan type to return
|
||||
|
||||
Returns: String: the type name.
|
||||
|
||||
=item C<remove_tag($plan_id, $tag)>
|
||||
|
||||
Description: Remove a tag from a plan.
|
||||
|
||||
Params: $plan_id - Integer: An integer or alias representing the ID of this plan in the database.
|
||||
|
||||
$tag - String - A single tag to be removed.
|
||||
|
||||
Returns: 0 on success.
|
||||
|
||||
=item C<store_text($plan_id, $text, [$author_id])>
|
||||
|
||||
Description: Update the document field of a plan.
|
||||
|
||||
Params: $plan_id - Integer: An integer representing the ID of this plan in the database.
|
||||
$text - String: Text for the document. Can contain HTML.
|
||||
[$author_id] = Integer/String: (OPTIONAL) The numeric ID or the login of the author.
|
||||
Defaults to logged in user.
|
||||
|
||||
Returns: Integer: The new text version
|
||||
|
||||
=item C<update($ids, $values)>
|
||||
|
||||
Description: Updates the fields of the selected test plan.
|
||||
|
||||
Params: $ids - Integer: A single TestPlan ID.
|
||||
|
||||
$values - Hash of keys matching TestPlan fields and the new values
|
||||
to set each field to.
|
||||
+-------------------------+----------------+
|
||||
| Field | Type |
|
||||
+-------------------------+----------------+
|
||||
| name | String |
|
||||
| type | Integer/String |
|
||||
| default_product_version | String |
|
||||
| isactive | Boolean |
|
||||
+-------------------------+----------------+
|
||||
|
||||
Returns: Hash: The updated test plan object.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::TestPlan>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,726 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::TestRun;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Product;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use Bugzilla::Testopia::Constants;
|
||||
use Bugzilla::Testopia::TestRun;
|
||||
use Bugzilla::Testopia::Search;
|
||||
use Bugzilla::Testopia::Table;
|
||||
|
||||
# Utility method called by the list method
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my ($run_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a run object hash
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
$run->{'case_count'} = $run->case_count();
|
||||
|
||||
return $run;
|
||||
}
|
||||
|
||||
sub list {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "run");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
|
||||
return Bugzilla::Testopia::Table->new('run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list();
|
||||
}
|
||||
|
||||
sub list_count {
|
||||
my $self = shift;
|
||||
my ($query) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$cgi->param("current_tab", "run");
|
||||
|
||||
foreach (keys(%$query)){
|
||||
$cgi->param($_, $$query{$_});
|
||||
}
|
||||
$cgi->param('distinct', 1);
|
||||
|
||||
my $search = Bugzilla::Testopia::Search->new($cgi);
|
||||
return Bugzilla::Testopia::Table->new('run','tr_xmlrpc.cgi',$cgi,undef,$search->query())->list_count();
|
||||
}
|
||||
|
||||
sub create {
|
||||
my $self =shift;
|
||||
my ($new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $plan = Bugzilla::Testopia::TestPlan->new($new_values->{'plan_id'});
|
||||
ThrowUserError("testopia-create-denied", {'object' => 'Test Run', 'plan' => $plan}) unless ($plan->canedit);
|
||||
|
||||
my @cases = Bugzilla::Testopia::Util::process_list($new_values->{'cases'});
|
||||
print STDERR Data::Dumper::Dumper(\@cases);
|
||||
delete $new_values->{'cases'};
|
||||
|
||||
$new_values->{'manager_id'} ||= $new_values->{'manager'};
|
||||
$new_values->{'build_id'} ||= $new_values->{'build'};
|
||||
$new_values->{'environment_id'} ||= $new_values->{'environment'};
|
||||
|
||||
delete $new_values->{'manager'};
|
||||
delete $new_values->{'build'};
|
||||
delete $new_values->{'environment'};
|
||||
|
||||
$new_values->{'plan_text_version'} ||= $plan->version;
|
||||
$new_values->{'product_version'} ||= $plan->product_version;
|
||||
$new_values->{'status'} = 1 unless defined $new_values->{'status'} && $new_values->{'status'} == 0;
|
||||
|
||||
if (trim($new_values->{'build_id'}) !~ /^\d+$/ ){
|
||||
my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $plan->product, "THROWERROR");
|
||||
$new_values->{'build_id'} = $build->id;
|
||||
}
|
||||
if (trim($new_values->{'environment_id'}) !~ /^\d+$/ ){
|
||||
my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $plan->product, "THROWERROR");
|
||||
$new_values->{'environment_id'} = $environment->id;
|
||||
}
|
||||
|
||||
my $run = Bugzilla::Testopia::TestRun->create($new_values);
|
||||
|
||||
foreach my $c (@cases){
|
||||
my $case = Bugzilla::Testopia::TestCase->new($c);
|
||||
$run->add_case_run($case->id, $case->sortkey) if $case;
|
||||
}
|
||||
|
||||
return $run;
|
||||
}
|
||||
|
||||
sub add_cases {
|
||||
my $self = shift;
|
||||
my ($case_ids, $run_ids) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my @ids = Bugzilla::Testopia::Util::process_list($case_ids);
|
||||
my @results;
|
||||
foreach my $id (@ids){
|
||||
my $case = new Bugzilla::Testopia::TestCase($id);
|
||||
unless ($case){
|
||||
push @results, {ERROR => "TestCase $id does not exist"};
|
||||
next;
|
||||
}
|
||||
unless ($case->canedit){
|
||||
push @results, {ERROR => "You do not have rights to edit this test case"};
|
||||
next;
|
||||
}
|
||||
eval {
|
||||
$case->add_to_run($run_ids);
|
||||
};
|
||||
if ($@){
|
||||
push @results, {ERROR => $@};
|
||||
}
|
||||
}
|
||||
# @results will be empty if successful
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub update {
|
||||
my $self =shift;
|
||||
my ($run_id, $new_values) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit;
|
||||
|
||||
$new_values->{'manager_id'} ||= $new_values->{'manager'};
|
||||
$new_values->{'build_id'} ||= $new_values->{'build'};
|
||||
$new_values->{'environment_id'} ||= $new_values->{'environment'};
|
||||
|
||||
ThrowUserError("testopia-no-status", {field => 'status'}) if exists $new_values->{'status'} && !$run->canstatus;
|
||||
ThrowUserError("testopia-no-status", {field => 'manager'}) if $new_values->{'manager'} && !$run->canstatus;
|
||||
ThrowUserError("testopia-no-status", {field => 'target'}) if exists $new_values->{'target_pass'} && !$run->canstatus;
|
||||
ThrowUserError("testopia-no-status", {field => 'target'}) if exists $new_values->{'target_completion'} && !$run->canstatus;
|
||||
|
||||
|
||||
if ($new_values->{'build_id'} && trim($new_values->{'build_id'}) !~ /^\d+$/ ){
|
||||
my $build = Bugzilla::Testopia::Build::check_build($new_values->{'build_id'}, $run->plan->product, "THROWERROR");
|
||||
$new_values->{'build_id'} = $build->id;
|
||||
}
|
||||
if ($new_values->{'environment_id'} && trim($new_values->{'environment_id'}) !~ /^\d+$/ ){
|
||||
my $environment = Bugzilla::Testopia::Environment::check_environment($new_values->{'environment_id'}, $run->plan->product, "THROWERROR");
|
||||
$new_values->{'environment_id'} = $environment->id;
|
||||
}
|
||||
|
||||
my $timestamp;
|
||||
$timestamp = $run->stop_date;
|
||||
$timestamp = undef if $new_values->{'status'} == 1;
|
||||
$timestamp = Bugzilla::Testopia::Util::get_time_stamp() if $new_values->{'status'} == 0 && !$run->stop_date;
|
||||
|
||||
$run->set_summary(trim($new_values->{'summary'})) if defined $new_values->{'summary'};
|
||||
$run->set_product_version($new_values->{'product_version'}) if $new_values->{'product_version'};
|
||||
$run->set_plan_text_version($new_values->{'plan_text_version'}) if $new_values->{'plan_text_version'};
|
||||
$run->set_build($new_values->{'build_id'}) if $new_values->{'build_id'};
|
||||
$run->set_environment($new_values->{'environment_id'}) if $new_values->{'environment_id'};
|
||||
$run->set_manager($new_values->{'manager_id'}) if $new_values->{'manager_id'};
|
||||
$run->set_notes($new_values->{'notes'}) if defined $new_values->{'notes'};
|
||||
$run->set_stop_date($timestamp) if exists $new_values->{'status'};
|
||||
$run->set_target_pass($new_values->{'target_pass'}) if defined $new_values->{'target_pass'};
|
||||
$run->set_target_completion($new_values->{'target_completion'}) if defined $new_values->{'target_completion'};
|
||||
|
||||
$run->update();
|
||||
|
||||
# Result is modified test run, otherwise an exception will be thrown
|
||||
return $run;
|
||||
}
|
||||
|
||||
sub get_change_history {
|
||||
my $self = shift;
|
||||
my ($run_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
# Result list of changes otherwise an exception will be thrown
|
||||
return $run->history;
|
||||
}
|
||||
|
||||
sub get_test_cases {
|
||||
my $self = shift;
|
||||
my ($run_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a run object hash
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
# Result is list of test cases for the given test run
|
||||
return $run->cases;
|
||||
}
|
||||
|
||||
sub get_test_case_runs {
|
||||
my $self = shift;
|
||||
my ($run_id, $current) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a run object hash
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
# Result is list of test cases for the given test run
|
||||
return $run->current_caseruns if $current;
|
||||
return $run->caseruns;
|
||||
}
|
||||
|
||||
sub get_test_plan {
|
||||
my $self = shift;
|
||||
my ($run_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
# Result is a run object hash
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Build', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
# Result is list of test cases for the given test run
|
||||
return $run->plan;
|
||||
}
|
||||
|
||||
sub lookup_environment_id_by_name {
|
||||
return { ERROR => 'This method is considered harmful and has been deprecated. Please use Environment::check_environment instead'};
|
||||
}
|
||||
|
||||
sub lookup_environment_name_by_id {
|
||||
return { ERROR => 'This method has been deprecated. Please use Environment::get instead'};
|
||||
}
|
||||
|
||||
sub add_tag {
|
||||
my $self = shift;
|
||||
my ($run_ids, $tags) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my @ids = Bugzilla::Testopia::Util::process_list($run_ids);
|
||||
my @results;
|
||||
foreach my $id (@ids){
|
||||
my $run = new Bugzilla::Testopia::TestRun($id);
|
||||
unless ($run){
|
||||
push @results, {ERROR => "TestRun $id does not exist"};
|
||||
next;
|
||||
}
|
||||
unless ($run->canedit){
|
||||
push @results, {ERROR => "You do not have rights to edit this test run"};
|
||||
next;
|
||||
}
|
||||
eval {
|
||||
$run->add_tag($tags);
|
||||
};
|
||||
if ($@){
|
||||
push @results, {ERROR => $@};
|
||||
}
|
||||
}
|
||||
# @results will be empty if successful
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub remove_tag {
|
||||
my $self = shift;
|
||||
my ($run_id, $tag_name) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-read-only', {'object' => $run}) unless $run->canedit;
|
||||
|
||||
$run->remove_tag($tag_name);
|
||||
|
||||
# Result 0 on success, otherwise an exception will be thrown
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub get_tags {
|
||||
my $self = shift;
|
||||
my ($run_id) = @_;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my $run = new Bugzilla::Testopia::TestRun($run_id);
|
||||
|
||||
ThrowUserError('invalid-test-id-non-existent', {type => 'Test Run', id => $run_id}) unless $run;
|
||||
ThrowUserError('testopia-permission-denied', {'object' => $run}) unless $run->canview;
|
||||
|
||||
my @results;
|
||||
foreach my $tag (@{$run->tags}){
|
||||
push @results, $tag->name;
|
||||
}
|
||||
# Result list of tags otherwise an exception will be thrown
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub get_completion_report {
|
||||
my $self = shift;
|
||||
my ($runs) = @_;
|
||||
my $vars;
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
my @run_ids;
|
||||
if (ref $runs eq 'ARRAY'){
|
||||
push @run_ids, @$runs
|
||||
}
|
||||
elsif ($runs =~ /,/){
|
||||
push @run_ids, split(/[\s,]+/, $runs);
|
||||
}
|
||||
else{
|
||||
push @run_ids, $runs;
|
||||
}
|
||||
|
||||
my @runs;
|
||||
foreach my $g (@run_ids){
|
||||
my $obj = Bugzilla::Testopia::TestRun->new($g);
|
||||
push @runs, $obj if $obj && $obj->canview;
|
||||
}
|
||||
|
||||
unless (scalar @runs){
|
||||
die "No runs found";
|
||||
}
|
||||
|
||||
my $total = $runs[0]->case_run_count(undef, \@runs);
|
||||
my $passed = $runs[0]->case_run_count(PASSED, \@runs);
|
||||
my $failed = $runs[0]->case_run_count(FAILED, \@runs);
|
||||
my $blocked = $runs[0]->case_run_count(BLOCKED, \@runs);
|
||||
|
||||
my $completed = $passed + $failed + $blocked;
|
||||
|
||||
my $unfinished = $total - $completed;
|
||||
my $unpassed = $completed - $passed;
|
||||
my $unfailed = $completed - $failed;
|
||||
my $unblocked = $completed - $blocked;
|
||||
|
||||
$vars->{'total'} = $total;
|
||||
$vars->{'completed'} = $completed;
|
||||
$vars->{'passed'} = $passed;
|
||||
$vars->{'failed'} = $failed;
|
||||
$vars->{'blocked'} = $blocked;
|
||||
$vars->{'idle'} = $runs[0]->case_run_count(IDLE, \@runs);
|
||||
$vars->{'running'} = $runs[0]->case_run_count(RUNNING, \@runs);
|
||||
$vars->{'paused'} = $runs[0]->case_run_count(PAUSED, \@runs);
|
||||
|
||||
$vars->{'percent_completed'} = calculate_percent($total, $completed);
|
||||
$vars->{'percent_passed'} = calculate_percent($completed, $passed);
|
||||
$vars->{'percent_failed'} = calculate_percent($completed, $failed);
|
||||
$vars->{'percent_blocked'} = calculate_percent($completed, $blocked);
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
sub get_bugs {
|
||||
my $self = shift;
|
||||
my ($runs) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my @run_ids = Bugzilla::Testopia::Util::process_list($runs);
|
||||
|
||||
my $bugs = $dbh->selectcol_arrayref("
|
||||
SELECT DISTINCT tcb.bug_id
|
||||
FROM test_case_bugs AS tcb
|
||||
INNER JOIN test_case_runs AS tcr ON tcr.case_run_id = tcb.case_run_id
|
||||
INNER JOIN bugs on tcb.bug_id = bugs.bug_id
|
||||
INNER JOIN test_case_run_status AS tcrs ON tcr.case_run_status_id = tcrs.case_run_status_id
|
||||
WHERE tcr.run_id in (" . join (',',@run_ids) . ") AND tcr.iscurrent = 1 ORDER BY tcb.bug_id");
|
||||
|
||||
my @bugs;
|
||||
foreach my $id (@{$bugs}){
|
||||
push @bugs, Bugzilla::Bug->new($id, Bugzilla->user->id);
|
||||
}
|
||||
|
||||
return \@bugs;
|
||||
}
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::TestRun
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides methods for automated scripts to manipulate Testopia TestRuns
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<add_cases($case_ids, $run_ids)>
|
||||
|
||||
Description: Add one or more cases to the selected test runs.
|
||||
|
||||
Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database,
|
||||
an arry of case_ids or aliases, or a string of comma separated case_ids.
|
||||
|
||||
$run_ids - Integer/Array/String: An integer representing the ID in the database
|
||||
an array of IDs, or a comma separated list of IDs.
|
||||
|
||||
Returns: Array: empty on success or an array of hashes with failure
|
||||
codes if a failure occured.
|
||||
|
||||
=item C<add_tag($run_ids, $tags)>
|
||||
|
||||
Description: Add one or more tags to the selected test runs.
|
||||
|
||||
Params: $run_ids - Integer/Array/String: An integer representing the ID in the database,
|
||||
an arry of run_ids, or a string of comma separated run_ids.
|
||||
|
||||
$tags - String/Array - A single tag, an array of tags,
|
||||
or a comma separated list of tags.
|
||||
|
||||
Returns: Array: empty on success or an array of hashes with failure
|
||||
codes if a failure occured.
|
||||
|
||||
=item C<create($values)>
|
||||
|
||||
Description: Creates a new Test Run object and stores it in the database.
|
||||
|
||||
Params: $values - Hash: A reference to a hash with keys and values
|
||||
matching the fields of the test run to be created.
|
||||
+-------------------+----------------+-----------+------------------------------------+
|
||||
| Field | Type | Null | Description |
|
||||
+-------------------+----------------+-----------+------------------------------------+
|
||||
| plan_id | Integer | Required | ID of test plan |
|
||||
| environment | Integer/String | Required | ID or Name of Environment |
|
||||
| build | Integer/String | Required | ID or Name of Build |
|
||||
| manager | Integer/String | Required | ID or Login of run manager |
|
||||
| summary | String | Required | |
|
||||
| product_version | String | Optional | Defaults to plan's version |
|
||||
| plan_text_version | Integer | Optional | |
|
||||
| target_completion | Integer | Optional | Targetted Completion percentage |
|
||||
| target_pass | Integer | Optional | Targetted Pass percentage |
|
||||
| notes | String | Optional | |
|
||||
| status | Integer | Optional | 0:STOPPED 1: RUNNING (default 1) |
|
||||
| cases | Array/String | Optional | list of case ids to add to the run |
|
||||
+-------------------+----------------+-----------+------------------------------------+
|
||||
|
||||
Returns: The newly created object hash.
|
||||
|
||||
=item C<get($run_id)>
|
||||
|
||||
Description: Used to load an existing test run from the database.
|
||||
|
||||
Params: $id - Integer: An integer representing the ID of the run in the database
|
||||
|
||||
Returns: Hash: A blessed Bugzilla::Testopia::TestRun object hash
|
||||
|
||||
=item C<get_bugs($runs)>
|
||||
|
||||
Description: Get the list of bugs attached to this run.
|
||||
|
||||
Params: $runs - Integer/Array/String: An integer representing the ID in the database
|
||||
an array of integers or a comma separated list of integers.
|
||||
|
||||
Returns: Array: An array of bug object hashes.
|
||||
|
||||
=item C<get_change_history($run_id)>
|
||||
|
||||
Description: Get the list of changes to the fields of this run.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID of the run in the database
|
||||
|
||||
Returns: Array: An array of hashes with changed fields and their details.
|
||||
|
||||
=item C<get_completion_report($runs)>
|
||||
|
||||
Description: Get a report of the current status of the selected runs combined.
|
||||
|
||||
Params: $runs - Integer/Array/String: An integer representing the ID in the database
|
||||
an array of integers or a comma separated list of integers.
|
||||
|
||||
Returns: Hash: A hash containing counts and percentages of the combined totals of
|
||||
case-runs in the run. Counts only the most recently statused case-run
|
||||
for a given build and environment.
|
||||
|
||||
=item C<get_tags($run_id)>
|
||||
|
||||
Description: Get the list of tags attached to this run.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID of the run in the database
|
||||
|
||||
Returns: Array: An array of tags .
|
||||
|
||||
=item C<get_test_case_runs($run_id, $current)>
|
||||
|
||||
Description: Get the list of cases that this run is linked to.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID in the database
|
||||
for this run.
|
||||
|
||||
$current - Boolean: 1 to only include the current set (what is displayed
|
||||
in the web page) 0: to return all, current and historical.
|
||||
|
||||
Returns: Array: An array of test case-run object hashes.
|
||||
|
||||
=item C<get_test_cases($run_id)>
|
||||
|
||||
Description: Get the list of cases that this run is linked to.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID in the database
|
||||
for this run.
|
||||
|
||||
Returns: Array: An array of test case object hashes.
|
||||
|
||||
=item C<get_test_plan($run_id)>
|
||||
|
||||
Description: Get the plan that this run is associated with.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID in the database
|
||||
for this run.
|
||||
|
||||
Returns: Hash: A plan object hash.
|
||||
|
||||
=item C<list($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting list of test runs.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields.
|
||||
|
||||
+--------------------------------------------------------+
|
||||
| Run Search Parameters |
|
||||
+--------------------------------------------------------+
|
||||
| Key | Valid Values |
|
||||
| build | String: Product Name |
|
||||
| build_id | Integer |
|
||||
| environment | String: Product Name |
|
||||
| environment_id | Integer |
|
||||
| manager | A bugzilla login (email address) |
|
||||
| manager_type | (select from email_variants) |
|
||||
| milestone | String |
|
||||
| notes | String |
|
||||
| notes_type | (select from query_variants) |
|
||||
| plan_id | comma separated integers |
|
||||
| product | String: Product Name |
|
||||
| product_id | Integer |
|
||||
| run_id | comma separated integers |
|
||||
| run_status | 1: RUNNING 0: STOPPED |
|
||||
| summary | String |
|
||||
| summary_type | (select from query_variants) |
|
||||
| tags | String |
|
||||
| tags_type | (select from tag_variants) |
|
||||
| type_id | Integer |
|
||||
| version | String: Product version |
|
||||
+--------------------------------------------------------+
|
||||
|
||||
+--------------------------------------------------------+
|
||||
| Paging and Sorting |
|
||||
+--------------------------------------------------------+
|
||||
| Key | Description |
|
||||
| dir | "ASC" or "DESC" |
|
||||
| order | field to sort by |
|
||||
+--------------------------------------------------------+
|
||||
| page_size | integer: how many per page |
|
||||
| page | integer: page number |
|
||||
| +++++++ OR +++++++ |
|
||||
| start | integer: Start with which record |
|
||||
| limit | integer: limit to how many |
|
||||
+--------------------------------------------------------+
|
||||
| viewall | 1: returns all records 0: first 25 |
|
||||
+--------------------------------------------------------+
|
||||
* The default is to only return 25 records at a time
|
||||
|
||||
+----------------------------------------------------+
|
||||
| query_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
+----------------+-----------------------------------+
|
||||
|
||||
+-------------------------------------+
|
||||
| email_variants |
|
||||
+--------------+----------------------+
|
||||
| Key | Description |
|
||||
| substring | contains |
|
||||
| exact | is |
|
||||
| regexp | matches regexp |
|
||||
| notregexp | doesn't match regexp |
|
||||
+--------------+----------------------+
|
||||
|
||||
+----------------------------------------------------+
|
||||
| tag_variants |
|
||||
+----------------+-----------------------------------+
|
||||
| Key | Description |
|
||||
| anyexact | is tagged with |
|
||||
| allwordssubstr | contains all of the words/strings |
|
||||
| anywordssubstr | contains any of the words/strings |
|
||||
| substring | contains the string |
|
||||
| casesubstring | contains the string (exact case) |
|
||||
| regexp | matches the regexp |
|
||||
| notregexp | doesn't match the regexp |
|
||||
| allwords | contains all of the words |
|
||||
| anywords | contains any of the words |
|
||||
| nowords | contains none of the words |
|
||||
+----------------------------------------------------+
|
||||
|
||||
Returns: Array: Matching test runs are retuned in a list of run object hashes.
|
||||
|
||||
=item C<list_count($query)>
|
||||
|
||||
Description: Performs a search and returns the resulting count of runs.
|
||||
|
||||
Params: $query - Hash: keys must match valid search fields (see list).
|
||||
|
||||
Returns: Integer - total matching runs.
|
||||
|
||||
=item C<remove_tag($run_id, $tag)>
|
||||
|
||||
Description: Remove a tag from a run.
|
||||
|
||||
Params: $run_id - Integer: An integer representing the ID in the database.
|
||||
|
||||
$tag - String - A single tag to be removed.
|
||||
|
||||
Returns: 0 on success.
|
||||
|
||||
=item C<update($ids, $values)>
|
||||
|
||||
Description: Updates the fields of the selected test run.
|
||||
|
||||
Params: $ids - Integer: A single TestRun ID.
|
||||
|
||||
$values - Hash of keys matching TestRun fields and the new values
|
||||
to set each field to. See L<create> for description
|
||||
+-------------------+----------------+
|
||||
| Field | Type |
|
||||
+-------------------+----------------+
|
||||
| plan_id | Integer |
|
||||
| environment | Integer/String |
|
||||
| build | Integer/String |
|
||||
| manager | Integer/String |
|
||||
| summary | String |
|
||||
| product_version | String |
|
||||
| plan_text_version | Integer |
|
||||
| target_completion | Integer |
|
||||
| target_pass | Integer |
|
||||
| notes | String |
|
||||
| status | Integer |
|
||||
+-------------------+----------------+
|
||||
|
||||
Returns: Hash: The updated test run object.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Testopia::TestRun>
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
|
@ -0,0 +1,78 @@
|
|||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dallas Harken <dharken@novell.com>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
package Bugzilla::WebService::Testopia::Testopia;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
sub api_version {
|
||||
my $self = shift;
|
||||
return "2.0";
|
||||
}
|
||||
|
||||
sub testopia_version {
|
||||
my $self = shift;
|
||||
return "2.0-RC2";
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Testopia::Webservice::Testopia
|
||||
|
||||
=head1 EXTENDS
|
||||
|
||||
Bugzilla::Webservice
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Provides information about this installation.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item C<api_version()>
|
||||
|
||||
Description: Returns the API version.
|
||||
|
||||
=item C<testopia_version()>
|
||||
|
||||
Description: Returns the version of Testopia on this server.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Webservice>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Greg Hendricks <ghendricks@novell.com>
|
||||
|
|
@ -125,6 +125,32 @@ sub create {
|
|||
return { id => type('int')->value($user->id) };
|
||||
}
|
||||
|
||||
#################
|
||||
# User Lookup #
|
||||
#################
|
||||
|
||||
sub lookup_login_by_id {
|
||||
my $self = shift;
|
||||
my ($author_id) = @_;
|
||||
|
||||
my $user = new Bugzilla::User($author_id);
|
||||
|
||||
my $result = defined $user ? $user->login : '';
|
||||
|
||||
# Result is user login string or empty string if failed
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub lookup_id_by_login {
|
||||
my $self = shift;
|
||||
my ($author) = @_;
|
||||
|
||||
my $result = Bugzilla::User::login_to_id($author);
|
||||
|
||||
# Result is user id or 0 if failed
|
||||
return $result;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
|
|
@ -370,7 +370,9 @@ if ($action eq 'del') {
|
|||
|
||||
$vars->{'product'} = $product;
|
||||
$vars->{'token'} = issue_session_token('delete_product');
|
||||
|
||||
|
||||
Bugzilla::Hook::process("editproducts-confirm_delete", { vars => $vars });
|
||||
|
||||
$template->process("admin/products/confirm-delete.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
my $vars = Bugzilla->hook_args->{vars};
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
$vars->{'case_id'} = $cgi->param('case_id');
|
||||
$vars->{'caserun_id'} = $cgi->param('caserun_id');
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
sub REQUIRED_MODULES {
|
||||
my @modules = (
|
||||
{
|
||||
package => 'JSON',
|
||||
module => 'JSON',
|
||||
version => '2.10'
|
||||
},
|
||||
{
|
||||
package => 'Text-Diff',
|
||||
module => 'Text::Diff',
|
||||
version => '0.35'
|
||||
},
|
||||
{
|
||||
package => 'GD-Graph3d',
|
||||
module => 'GD::Graph3d',
|
||||
version => '0.63'
|
||||
},
|
||||
);
|
||||
return \@modules;
|
||||
};
|
||||
|
||||
sub OPTIONAL_MODULES {
|
||||
my @modules = (
|
||||
{
|
||||
package => 'Text-CSV',
|
||||
module => 'Text::CSV',
|
||||
version => '1.06',
|
||||
feature => 'CSV Importing of test cases'
|
||||
}
|
||||
);
|
||||
|
||||
return \@modules;
|
||||
};
|
||||
|
|
@ -0,0 +1,559 @@
|
|||
#!/usr/bin/perl -w
|
||||
# -*- 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 Testopia 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): Maciej Maczynski <macmac@xdsnet.pl>
|
||||
# Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Vance Baarda <vrb@novell.com>
|
||||
|
||||
use strict;
|
||||
use lib '.';
|
||||
use Bugzilla;
|
||||
use Bugzilla::Group;
|
||||
|
||||
# Start of main().
|
||||
print "\nChecking Testopia setup ...\n";
|
||||
testopiaUpdateDB();
|
||||
updateACLs();
|
||||
migrateAttachments();
|
||||
createGroup();
|
||||
finalFixups();
|
||||
print "Done checking Testopia setup.\n\n";
|
||||
# End of main().
|
||||
|
||||
sub testopiaUpdateDB {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# If the database contains Testopia tables but bz_schema doesn't
|
||||
# know about them, then we need to update bz_schema.
|
||||
if (grep(/^test_cases$/, $dbh->bz_table_list_real) and
|
||||
!$dbh->_bz_real_schema->get_table_abstract('test_cases')) {
|
||||
my $msg = "Sorry, we cannot upgrade from Testopia 1.0 using this " .
|
||||
"database. Upgrades are supported only with MySQL.";
|
||||
die($msg) unless $dbh->isa('Bugzilla::DB::Mysql');
|
||||
my $built_schema = $dbh->_bz_build_schema_from_disk;
|
||||
foreach my $table (grep(/^test_/, $built_schema->get_table_list())) {
|
||||
$dbh->_bz_real_schema->add_table($table,
|
||||
$built_schema->get_table_abstract($table));
|
||||
}
|
||||
$dbh->_bz_store_real_schema;
|
||||
}
|
||||
|
||||
$dbh->bz_setup_database();
|
||||
|
||||
$dbh->bz_drop_table('test_case_group_map');
|
||||
$dbh->bz_drop_table('test_category_templates');
|
||||
$dbh->bz_drop_table('test_plan_testers');
|
||||
$dbh->bz_drop_table('test_plan_group_map');
|
||||
$dbh->bz_drop_column('test_plans', 'editor_id');
|
||||
|
||||
$dbh->bz_add_column('test_case_bugs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1});
|
||||
$dbh->bz_add_column('test_case_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, 0);
|
||||
$dbh->bz_add_column('test_case_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0);
|
||||
$dbh->bz_add_column('test_case_texts', 'setup', {TYPE => 'MEDIUMTEXT'});
|
||||
$dbh->bz_add_column('test_case_texts', 'breakdown', {TYPE => 'MEDIUMTEXT'});
|
||||
$dbh->bz_add_column('test_environments', 'product_id', {TYPE => 'INT2', NOTNULL => 1}, 0);
|
||||
$dbh->bz_add_column('test_environments', 'isactive', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, 1);
|
||||
$dbh->bz_add_column('test_plan_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0);
|
||||
$dbh->bz_add_column('test_runs', 'default_tester_id', {TYPE => 'INT3'});
|
||||
$dbh->bz_add_column('test_runs', 'target_pass', {TYPE => 'INT1'});
|
||||
$dbh->bz_add_column('test_runs', 'target_completion', {TYPE => 'INT1'});
|
||||
$dbh->bz_add_column('test_run_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0);
|
||||
$dbh->bz_add_column('test_builds', 'isactive', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, 1);
|
||||
$dbh->bz_add_column('test_cases', 'estimated_time', {TYPE => 'TIME'}, 0);
|
||||
$dbh->bz_add_column('test_case_runs', 'running_date', {TYPE => 'DATETIME'}, 0);
|
||||
$dbh->bz_add_column('test_plan_types', 'description', {TYPE => 'MEDIUMTEXT'}, 0);
|
||||
$dbh->bz_add_column('test_case_status', 'description', {TYPE => 'MEDIUMTEXT'}, 0);
|
||||
$dbh->bz_add_column('test_case_run_status', 'description', {TYPE => 'MEDIUMTEXT'}, 0);
|
||||
$dbh->bz_add_column('test_case_runs', 'iscurrent', {TYPE => 'INT1', NOTNULL => 1, DEFAULT => 0}, 0);
|
||||
$dbh->bz_add_column('test_named_queries', 'type', {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0}, 0);
|
||||
fixTables();
|
||||
|
||||
$dbh->bz_alter_column('test_attachment_data', 'attachment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_attachments', 'attachment_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_attachments', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_builds', 'build_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_activity', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_bugs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_bugs', 'case_run_id', {TYPE => 'INT4', UNSIGNED => 1});
|
||||
$dbh->bz_alter_column('test_case_components', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_dependencies', 'blocked', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_dependencies', 'dependson', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_plans', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_plans', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'build_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'case_run_status_id', {TYPE => 'INT2', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'case_run_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_runs', 'iscurrent', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0'});
|
||||
$dbh->bz_alter_column('test_case_runs', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_run_status', 'case_run_status_id', {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_cases', 'case_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_cases', 'case_status_id', {TYPE => 'INT2', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_status', 'case_status_id', {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_tags', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_texts', 'case_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_case_texts', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_environment_map', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_environment_map', 'property_id', {TYPE => 'INT4', UNSIGNED => 1, DEFAULT => undef});
|
||||
$dbh->bz_alter_column('test_environments', 'environment_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_named_queries', 'isvisible', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1});
|
||||
$dbh->bz_alter_column('test_plan_activity', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plans', 'plan_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plans', 'type_id', {TYPE => 'INT2', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plan_types', 'type_id', {TYPE => 'SMALLSERIAL', NOTNULL => 1, PRIMARYKEY => 1}, 0);
|
||||
$dbh->bz_alter_column('test_plan_tags', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plan_texts', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plan_texts', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_plan_texts', 'plan_text', {TYPE => 'MEDIUMTEXT'});
|
||||
$dbh->bz_alter_column('test_run_activity', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_run_cc', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_runs', 'build_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_runs', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_runs', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_runs', 'run_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_runs', 'start_date', {TYPE => 'DATETIME', NOTNULL => 1});
|
||||
$dbh->bz_alter_column('test_run_tags', 'run_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1});
|
||||
|
||||
$dbh->bz_drop_index('test_attachments', 'AI_attachment_id');
|
||||
$dbh->bz_drop_index('test_attachments', 'attachment_id');
|
||||
$dbh->bz_drop_index('test_builds', 'build_id');
|
||||
$dbh->bz_drop_index('test_case_bugs', 'case_run_bug_id_idx');
|
||||
$dbh->bz_drop_index('test_case_bugs', 'case_run_id_idx');
|
||||
$dbh->bz_drop_index('test_case_categories', 'AI_category_id');
|
||||
$dbh->bz_drop_index('test_case_categories', 'category_name_idx');
|
||||
$dbh->bz_drop_index('test_case_categories', 'category_name_indx');
|
||||
$dbh->bz_drop_index('test_case_components', 'case_commponents_component_id_idx');
|
||||
$dbh->bz_drop_index('test_case_components', 'case_components_case_id_idx');
|
||||
$dbh->bz_drop_index('test_case_components', 'case_components_component_id_idx');
|
||||
$dbh->bz_drop_index('test_case_plans', 'case_plans_case_id_idx');
|
||||
$dbh->bz_drop_index('test_case_plans', 'case_plans_plan_id_idx');
|
||||
$dbh->bz_drop_index('test_case_runs', 'AI_case_run_id');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_build_idx');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_env_idx');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_id');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_id_2');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_run_id_idx');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_shortkey_idx');
|
||||
$dbh->bz_drop_index('test_case_runs', 'case_run_sortkey_idx');
|
||||
$dbh->bz_drop_index('test_case_run_status', 'AI_case_run_status_id');
|
||||
$dbh->bz_drop_index('test_case_run_status', 'case_run_status_name_idx');
|
||||
$dbh->bz_drop_index('test_case_run_status', 'case_run_status_sortkey_idx');
|
||||
$dbh->bz_drop_index('test_case_run_status', 'sortkey');
|
||||
$dbh->bz_drop_index('test_cases', 'AI_case_id');
|
||||
$dbh->bz_drop_index('test_cases', 'alias');
|
||||
$dbh->bz_drop_index('test_cases', 'case_id');
|
||||
$dbh->bz_drop_index('test_cases', 'case_id_2');
|
||||
$dbh->bz_drop_index('test_case_status', 'AI_case_status_id');
|
||||
$dbh->bz_drop_index('test_case_status', 'case_status_id');
|
||||
$dbh->bz_drop_index('test_case_status', 'test_case_status_name_idx');
|
||||
$dbh->bz_drop_index('test_cases', 'test_case_requirment_idx');
|
||||
$dbh->bz_drop_index('test_case_tags', 'case_tags_case_id_idx');
|
||||
$dbh->bz_drop_index('test_case_tags', 'case_tags_case_id_idx_v2');
|
||||
$dbh->bz_drop_index('test_case_tags', 'case_tags_tag_id_idx');
|
||||
$dbh->bz_drop_index('test_case_tags', 'case_tags_user_idx');
|
||||
$dbh->bz_drop_index('test_email_settings', 'test_event_user_event_dx');
|
||||
$dbh->bz_drop_index('test_email_settings', 'test_event_user_event_idx');
|
||||
$dbh->bz_drop_index('test_email_settings', 'test_event_user_relationship_idx');
|
||||
$dbh->bz_drop_index('test_environment_category', 'env_category_idx');
|
||||
$dbh->bz_drop_index('test_environment_element', 'env_element_category_idx');
|
||||
$dbh->bz_drop_index('test_environment_property', 'env_element_property_idx');
|
||||
$dbh->bz_drop_index('test_environments', 'environment_id');
|
||||
$dbh->bz_drop_index('test_environments', 'environment_name_idx');
|
||||
$dbh->bz_drop_index('test_fielddefs', 'AI_fieldid');
|
||||
$dbh->bz_drop_index('test_fielddefs', 'fielddefs_name_idx') if $dbh->isa('Bugzilla::DB::Mysql');
|
||||
$dbh->bz_drop_index('test_fielddefs', 'test_fielddefs_name_idx');
|
||||
$dbh->bz_drop_index('test_plans', 'AI_plan_id');
|
||||
$dbh->bz_drop_index('test_plans', 'plan_id');
|
||||
$dbh->bz_drop_index('test_plans', 'plan_id_2');
|
||||
$dbh->bz_drop_index('test_plan_tags', 'plan_tags_idx');
|
||||
$dbh->bz_drop_index('test_plan_tags', 'plan_tags_user_idx');
|
||||
$dbh->bz_drop_index('test_plan_types', 'AI_type_id');
|
||||
$dbh->bz_drop_index('test_plan_types', 'plan_type_name_idx');
|
||||
$dbh->bz_drop_index('test_run_cc', 'run_cc_run_id_who_idx');
|
||||
$dbh->bz_drop_index('test_runs', 'AI_run_id');
|
||||
$dbh->bz_drop_index('test_runs', 'run_id');
|
||||
$dbh->bz_drop_index('test_runs', 'run_id_2');
|
||||
$dbh->bz_drop_index('test_runs', 'test_run_plan_id_run_id__idx');
|
||||
$dbh->bz_drop_index('test_run_tags', 'run_tags_idx');
|
||||
$dbh->bz_drop_index('test_run_tags', 'run_tags_user_idx');
|
||||
$dbh->bz_drop_index('test_tags', 'AI_tag_id');
|
||||
$dbh->bz_drop_index('test_tags', 'tag_name');
|
||||
$dbh->bz_drop_index('test_tags', 'test_tag_name_idx');
|
||||
$dbh->bz_drop_index('test_tags', 'test_tag_name_indx');
|
||||
$dbh->bz_drop_index('test_runs', 'test_runs_summary_idx');
|
||||
|
||||
$dbh->bz_add_index('test_attachment_data', 'test_attachment_data_primary_idx', ['attachment_id']);
|
||||
$dbh->bz_add_index('test_attachments', 'test_attachments_submitter_idx', ['submitter_id']);
|
||||
$dbh->bz_add_index('test_builds', 'build_milestone_idx', ['milestone']);
|
||||
$dbh->bz_add_index('test_builds', 'build_name_idx', ['name']);
|
||||
$dbh->bz_add_index('test_builds', 'build_prod_idx', {FIELDS => [qw(build_id product_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_builds', 'build_product_id_name_idx', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_attachments', 'test_case_attachments_primary_idx', ['attachment_id']);
|
||||
$dbh->bz_add_index('test_case_bugs', 'case_bugs_bug_id_idx', ['bug_id']);
|
||||
$dbh->bz_add_index('test_case_bugs', 'case_bugs_case_id_idx', ['case_id']);
|
||||
$dbh->bz_add_index('test_case_bugs', 'case_bugs_case_run_id_idx', ['case_run_id']);
|
||||
$dbh->bz_add_index('test_case_categories', 'category_name_idx_v2', ['name']);
|
||||
$dbh->bz_add_index('test_case_categories', 'category_product_id_name_idx', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_categories', 'category_product_idx', {FIELDS => [qw(category_id product_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_components', 'components_case_id_idx', {FIELDS => [qw(case_id component_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_components', 'components_component_id_idx', ['component_id']);
|
||||
$dbh->bz_add_index('test_case_dependencies', 'case_dependencies_blocked_idx', ['blocked']);
|
||||
$dbh->bz_add_index('test_case_dependencies', 'case_dependencies_primary_idx', {FIELDS => [qw(dependson blocked)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_plans', 'test_case_plans_case_idx', [qw(case_id)]);
|
||||
$dbh->bz_add_index('test_case_plans', 'test_case_plans_primary_idx', {FIELDS => [qw(plan_id case_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_runs', 'case_run_build_env_idx', {FIELDS => [qw(run_id case_id build_id environment_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_runs', 'case_run_build_idx_v2', ['build_id']);
|
||||
$dbh->bz_add_index('test_case_runs', 'case_run_env_idx_v2', ['environment_id']);
|
||||
$dbh->bz_add_index('test_case_runs', 'case_run_status_idx', ['case_run_status_id']);
|
||||
$dbh->bz_add_index('test_case_runs', 'case_run_text_ver_idx', ['case_text_version']);
|
||||
$dbh->bz_add_index('test_cases', 'test_case_requirement_idx', ['requirement']);
|
||||
$dbh->bz_add_index('test_cases', 'test_case_status_idx', ['case_status_id']);
|
||||
$dbh->bz_add_index('test_cases', 'test_case_tester_idx', ['default_tester_id']);
|
||||
$dbh->bz_add_index('test_case_tags', 'case_tags_case_id_idx_v3', [qw(case_id)]);
|
||||
$dbh->bz_add_index('test_case_tags', 'case_tags_primary_idx', {FIELDS => [qw(tag_id case_id userid)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_tags', 'case_tags_secondary_idx', {FIELDS => [qw(tag_id case_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_case_tags', 'case_tags_userid_idx', [qw(userid)]);
|
||||
$dbh->bz_add_index('test_email_settings', 'test_email_setting_user_id_idx', {FIELDS => [qw(userid relationship_id eventid)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_category', 'test_environment_category_key1', {FIELDS => [qw(env_category_id product_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_category', 'test_environment_category_key2', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_element', 'test_environment_element_key1', {FIELDS => [qw(element_id env_category_id)], TYPE => 'UNIQUE'},);
|
||||
$dbh->bz_add_index('test_environment_element', 'test_environment_element_key2', {FIELDS => [qw(env_category_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_map', 'test_environment_map_key3', {FIELDS => [qw(environment_id element_id property_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_property', 'test_environment_property_key1', {FIELDS => [qw(property_id element_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environment_property', 'test_environment_property_key2', {FIELDS => [qw(element_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environments', 'environment_name_idx_v2', ['name']);
|
||||
$dbh->bz_add_index('test_environments', 'test_environments_key1', {FIELDS => [qw(environment_id product_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_environments', 'test_environments_key2', {FIELDS => [qw(product_id name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_named_queries', 'test_namedquery_primary_idx', {FIELDS => [qw(userid name)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_plan_activity', 'plan_activity_changed_idx', ['changed']);
|
||||
$dbh->bz_add_index('test_plan_activity', 'plan_activity_field_idx', ['fieldid']);
|
||||
$dbh->bz_add_index('test_plan_activity', 'plan_activity_primary_idx', ['plan_id']);
|
||||
$dbh->bz_add_index('test_plan_attachments', 'test_plan_attachments_primary_idx', ['attachment_id']);
|
||||
$dbh->bz_add_index('test_plan_permissions', 'testers_plan_grant_idx', ['grant_type']);
|
||||
$dbh->bz_add_index('test_plan_tags', 'plan_tags_plan_id_idx', [qw(plan_id)]);
|
||||
$dbh->bz_add_index('test_plan_tags', 'plan_tags_primary_idx', {FIELDS => [qw(tag_id plan_id userid)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_plan_tags', 'plan_tags_secondary_idx', {FIELDS => [qw(tag_id plan_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_plan_tags', 'plan_tags_userid_idx', [qw(userid)]);
|
||||
$dbh->bz_add_index('test_run_activity', 'run_activity_field_idx', ['fieldid']);
|
||||
$dbh->bz_add_index('test_run_cc', 'test_run_cc_primary_idx', {FIELDS => [qw(run_id who)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_run_cc', 'test_run_cc_who_idx', [qw(who)]);
|
||||
$dbh->bz_add_index('test_runs', 'test_run_build_idx', ['build_id']);
|
||||
$dbh->bz_add_index('test_runs', 'test_run_env_idx', ['environment_id']);
|
||||
$dbh->bz_add_index('test_runs', 'test_run_plan_id_run_id_idx', [qw(plan_id run_id)]);
|
||||
$dbh->bz_add_index('test_runs', 'test_run_plan_ver_idx', ['plan_text_version']);
|
||||
$dbh->bz_add_index('test_runs', 'test_run_tester_idx', ['default_tester_id']);
|
||||
$dbh->bz_add_index('test_run_tags', 'run_tags_primary_idx', {FIELDS => [qw(tag_id run_id userid)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_run_tags', 'run_tags_run_id_idx', [qw(run_id)]);
|
||||
$dbh->bz_add_index('test_run_tags', 'run_tags_secondary_idx', {FIELDS => [qw(tag_id run_id)], TYPE => 'UNIQUE'});
|
||||
$dbh->bz_add_index('test_run_tags', 'run_tags_userid_idx', [qw(userid)]);
|
||||
$dbh->bz_add_index('test_tags', 'test_tag_name_idx_v2', [qw(tag_name)]);
|
||||
|
||||
populateMiscTables();
|
||||
populateEnvTables();
|
||||
migrateEnvData();
|
||||
}
|
||||
|
||||
sub updateACLs {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return unless $dbh->selectrow_array("SELECT COUNT(*) FROM test_plan_permissions") == 0;
|
||||
|
||||
print "Populating test plan ACLs ...\n";
|
||||
my $ref = $dbh->selectall_arrayref("SELECT plan_id, author_id FROM test_plans", {'Slice' =>{}});
|
||||
foreach my $plan (@$ref){
|
||||
my ($finished) = $dbh->selectrow_array(
|
||||
"SELECT COUNT(*) FROM test_plan_permissions
|
||||
WHERE plan_id = ? AND userid = ?",
|
||||
undef, ($plan->{'plan_id'}, $plan->{'author_id'}));
|
||||
next if ($finished);
|
||||
$dbh->do("INSERT INTO test_plan_permissions(userid, plan_id, permissions)
|
||||
VALUES(?,?,?)",
|
||||
undef, ($plan->{'author_id'}, $plan->{'plan_id'}, 15));
|
||||
}
|
||||
}
|
||||
|
||||
sub migrateAttachments {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
return unless $dbh->bz_column_info('test_attachments', 'case_id');
|
||||
print "Migrating attachments...\n";
|
||||
|
||||
my $rows = $dbh->selectall_arrayref(
|
||||
"SELECT attachment_id, case_id, plan_id
|
||||
FROM test_attachments", {'Slice' => {}});
|
||||
|
||||
foreach my $row (@$rows){
|
||||
if ($row->{'case_id'}){
|
||||
$dbh->do("INSERT INTO test_case_attachments (attachment_id, case_id)
|
||||
VALUES (?,?)", undef, ($row->{'attachment_id'}, $row->{'case_id'}));
|
||||
}
|
||||
elsif ($row->{'plan_id'}){
|
||||
$dbh->do("INSERT INTO test_plan_attachments (attachment_id, plan_id)
|
||||
VALUES (?,?)", undef, ($row->{'attachment_id'}, $row->{'plan_id'}));
|
||||
}
|
||||
}
|
||||
$dbh->bz_drop_column('test_attachments', 'case_id');
|
||||
$dbh->bz_drop_column('test_attachments', 'plan_id');
|
||||
}
|
||||
|
||||
sub populateMiscTables {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Fix and add values to an existing intall.
|
||||
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('ERROR', 7)")
|
||||
if $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status")
|
||||
&& ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status WHERE name = ?", undef, 'ERROR');
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('target_pass', 'Target Pass Rate', 'test_runs')")
|
||||
if $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs")
|
||||
&& ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs WHERE name = ?", undef, 'target_pass');
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('target_completion', 'Target Completion Rate', 'test_runs')")
|
||||
if $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs")
|
||||
&& ! $dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs WHERE name = ?", undef, 'target_completion');
|
||||
$dbh->do("UPDATE test_environment_map SET property_id = ? where property_id = ?",undef, undef, 0);
|
||||
|
||||
if ($dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status")){
|
||||
$dbh->do("UPDATE test_case_run_status SET name='IDLE', sortkey=1 where case_run_status_id=1");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='PASSED', sortkey=4 where case_run_status_id=2");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='FAILED', sortkey=5 where case_run_status_id=3");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='RUNNING', sortkey=2 where case_run_status_id=4");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='PAUSED', sortkey=3 where case_run_status_id=5");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='BLOCKED', sortkey=6 where case_run_status_id=6");
|
||||
$dbh->do("UPDATE test_case_run_status SET name='ERROR', sortkey=7 where case_run_status_id=7");
|
||||
}
|
||||
|
||||
# Insert initial values in static tables. Going out on a limb and
|
||||
# assuming that if one table is empty, they all are.
|
||||
return if $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_status");
|
||||
|
||||
print "Populating test_case_run_status table ...\n";
|
||||
print "Populating test_case_status table ...\n";
|
||||
print "Populating test_plan_types table ...\n";
|
||||
print "Populating test_fielddefs table ...\n";
|
||||
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('IDLE', 1)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('PASSED', 4)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('FAILED', 5)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('RUNNING', 2)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('PAUSED', 3)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('BLOCKED', 6)");
|
||||
$dbh->do("INSERT INTO test_case_run_status (name, sortkey) VALUES ('ERROR', 7)");
|
||||
$dbh->do("INSERT INTO test_case_status (name) VALUES ('PROPOSED')");
|
||||
$dbh->do("INSERT INTO test_case_status (name) VALUES ('CONFIRMED')");
|
||||
$dbh->do("INSERT INTO test_case_status (name) VALUES ('DISABLED')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Unit')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Integration')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Function')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('System')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Acceptance')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Installation')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Performance')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Product')");
|
||||
$dbh->do("INSERT INTO test_plan_types (name) VALUES ('Interoperability')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('isactive', 'Archived', 'test_plans')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('name', 'Plan Name', 'test_plans')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('type_id', 'Plan Type', 'test_plans')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('case_status_id', 'Case Status', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('category_id', 'Category', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('priority_id', 'Priority', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('summary', 'Run Summary', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('isautomated', 'Automated', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('alias', 'Alias', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('requirement', 'Requirement', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('script', 'Script', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('arguments', 'Argument', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('product_id', 'Product', 'test_plans')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('default_product_version', 'Default Product Version', 'test_plans')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('environment_id', 'Environment', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('product_version', 'Product Version', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('build_id', 'Default Build', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('plan_text_version', 'Plan Text Version', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('manager_id', 'Manager', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('default_tester_id', 'Default Tester', 'test_cases')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('stop_date', 'Stop Date', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('summary', 'Run Summary', 'test_runs')");
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) VALUES ('notes', 'Notes', 'test_runs')");
|
||||
}
|
||||
|
||||
sub populateEnvTables {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $sth;
|
||||
my $ary_ref;
|
||||
my $value;
|
||||
|
||||
return unless $dbh->selectrow_array("SELECT COUNT(*) FROM test_environment_category") == 0;
|
||||
if ($dbh->selectrow_array("SELECT COUNT(*) FROM test_environment_element") != 0) {
|
||||
print STDERR "\npopulateEnv: Fatal Error: test_environment_category " .
|
||||
"is empty but\ntest_environment_element is not. This ought " .
|
||||
"to be impossible.\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
print "Populating test_environment_category table ...\n";
|
||||
$dbh->do("INSERT INTO test_environment_category (product_id, name) " .
|
||||
"VALUES (0, 'Operating System')");
|
||||
$dbh->do("INSERT INTO test_environment_category (product_id, name) " .
|
||||
"VALUES (0, 'Hardware')");
|
||||
|
||||
print "Populating test_environment_element table ...\n";
|
||||
$sth = $dbh->prepare("INSERT INTO test_environment_element " .
|
||||
"(env_category_id, name, parent_id, isprivate) " .
|
||||
"VALUES (?, ?, ?, ?)");
|
||||
$ary_ref = $dbh->selectcol_arrayref("SELECT value FROM op_sys");
|
||||
foreach $value (@$ary_ref) {
|
||||
$sth->execute(1, $value, 0, 0);
|
||||
}
|
||||
$ary_ref = $dbh->selectcol_arrayref("SELECT value FROM rep_platform");
|
||||
foreach $value (@$ary_ref) {
|
||||
$sth->execute(2, $value, 0, 0);
|
||||
}
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
}
|
||||
|
||||
sub migrateEnvData {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth;
|
||||
my $value;
|
||||
my $os_mapping;
|
||||
my $platform_mapping;
|
||||
my $ary_ref;
|
||||
my $i;
|
||||
|
||||
return unless $dbh->bz_column_info('test_environments', 'op_sys_id');
|
||||
|
||||
# Map between IDs in op_sys table and IDs in
|
||||
# test_environment_element table.
|
||||
$os_mapping = $dbh->selectall_hashref("SELECT " .
|
||||
"os.id AS op_sys_id, " .
|
||||
"env_elem.element_id AS element_id " .
|
||||
"FROM op_sys os, test_environment_element env_elem " .
|
||||
"WHERE os.value = env_elem.name " .
|
||||
"AND env_elem.env_category_id = 1",
|
||||
'op_sys_id');
|
||||
|
||||
# Map between IDs in rep_platform table and IDs in
|
||||
# test_environment_element table.
|
||||
$platform_mapping = $dbh->selectall_hashref("SELECT " .
|
||||
"platform.id AS rep_platform_id, " .
|
||||
"env_elem.element_id AS element_id " .
|
||||
"FROM rep_platform platform, test_environment_element env_elem " .
|
||||
"WHERE platform.value = env_elem.name " .
|
||||
"AND env_elem.env_category_id = 2",
|
||||
'rep_platform_id');
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
print "Migrating data from test_environments to test_environment_map ...\n";
|
||||
$sth = $dbh->prepare("INSERT INTO test_environment_map " .
|
||||
"(environment_id, property_id, element_id, value_selected) " .
|
||||
"VALUES (?, ?, ?, ?)");
|
||||
$ary_ref = $dbh->selectall_arrayref("SELECT environment_id, op_sys_id " .
|
||||
"FROM test_environments");
|
||||
foreach $i (@$ary_ref) {
|
||||
$sth->execute(@$i[0], 0, $os_mapping->{@$i[1]}->{'element_id'}, '');
|
||||
}
|
||||
$ary_ref = $dbh->selectall_arrayref("SELECT environment_id, rep_platform_id " .
|
||||
"FROM test_environments");
|
||||
foreach $i (@$ary_ref) {
|
||||
$sth->execute(@$i[0], 0, $platform_mapping->{@$i[1]}->{'element_id'}, '');
|
||||
}
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
print "Saving data from test_environments.xml column into text files ...\n";
|
||||
$ary_ref = $dbh->selectall_arrayref("SELECT environment_id, name, xml " .
|
||||
"FROM test_environments WHERE xml != ''");
|
||||
foreach $value (@$ary_ref) {
|
||||
open(FH, ">environment_" . @$value[0] . "_xml.txt");
|
||||
print FH "environment ID: @$value[0]\n";
|
||||
print FH "environment name: @$value[1]\n";
|
||||
print FH "environment xml:\n@$value[2]\n";
|
||||
close(FH);
|
||||
}
|
||||
|
||||
$dbh->bz_drop_column('test_environments', 'op_sys_id');
|
||||
$dbh->bz_drop_column('test_environments', 'rep_platform_id');
|
||||
$dbh->bz_drop_column('test_environments', 'xml');
|
||||
}
|
||||
|
||||
sub fixTables {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Fix test_case_bugs table so that all case_id fields are not null.
|
||||
my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_bugs WHERE case_id IS NULL");
|
||||
if ($count){
|
||||
require Bugzilla::Testopia::TestCaseRun;
|
||||
my $caseruns = $dbh->selectcol_arrayref("SELECT case_run_id FROM test_case_bugs WHERE case_id IS NULL");
|
||||
my $sth = $dbh->prepare_cached("UPDATE test_case_bugs SET case_id = ? WHERE case_run_id = ?");
|
||||
foreach my $cr (@$caseruns){
|
||||
my $caserun = Bugzilla::Testopia::TestCaseRun->new($cr);
|
||||
$sth->execute($caserun->case->id, $cr);
|
||||
}
|
||||
}
|
||||
|
||||
# If we can't add a unique index to (case_id,component_id), then we
|
||||
# need to remove duplicate rows from test_case_components.
|
||||
eval{
|
||||
$dbh->bz_add_index('test_case_components', 'components_case_id_idx', {FIELDS => [qw(case_id component_id)], TYPE => 'UNIQUE'});
|
||||
};
|
||||
if ($@){
|
||||
print "Running component fix...\n";
|
||||
my $rows = $dbh->selectall_arrayref("SELECT * FROM test_case_components", {"Slice" => {}});
|
||||
my $seen;
|
||||
foreach my $row (@$rows){
|
||||
my $line = $row->{'case_id'} . "-" . $row->{'component_id'};
|
||||
if (!$seen->{$line}){
|
||||
$seen->{$line} = 'seen';
|
||||
}
|
||||
elsif ($seen->{$line} eq 'seen'){
|
||||
$dbh->do("DELETE FROM test_case_components
|
||||
WHERE case_id = ? AND component_id = ?",
|
||||
undef, ($row->{'case_id'}, $row->{'component_id'}));
|
||||
$dbh->do("INSERT INTO test_case_components
|
||||
VALUES(?,?)",
|
||||
undef, ($row->{'case_id'}, $row->{'component_id'}));
|
||||
$seen->{$line} = 'fixed';
|
||||
}
|
||||
elsif ($seen->{$line} eq 'fixed'){
|
||||
next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub createGroup {
|
||||
Bugzilla::Group->create({
|
||||
name => 'Testers',
|
||||
description => 'Can read and write all test plans, runs, and cases.',
|
||||
isbuggroup => 0 }) unless new Bugzilla::Group({name => 'Testers'});
|
||||
}
|
||||
|
||||
# A spot for fixing stuff at the very end.
|
||||
sub finalFixups {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# We added the estimated_time field later, so we can't add it
|
||||
# inside populateMiscTables().
|
||||
unless ($dbh->selectrow_array("SELECT COUNT(*) FROM test_fielddefs " .
|
||||
"WHERE name = 'estimated_time'")) {
|
||||
$dbh->do("INSERT INTO test_fielddefs (name, description, table_name) " .
|
||||
"VALUES ('estimated_time', 'Estimated Time', 'test_cases')");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use Bugzilla::Testopia::TestCase;
|
||||
|
||||
my $vars = Bugzilla->hook_args->{vars};
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $caserun_id = $cgi->param('caserun_id');
|
||||
my $case_id = $cgi->param('case_id');
|
||||
if (detaint_natural($caserun_id)) {
|
||||
my $caserun = Bugzilla::Testopia::TestCaseRun->new($cgi->param('caserun_id'));
|
||||
ThrowUserError("invalid-test-id-non-existent", {'id' => $caserun_id, 'type' => 'Case-Run'}) unless $caserun;
|
||||
ThrowUserError("testopia-read-only", {'object' => $caserun}) unless $caserun->canedit;
|
||||
|
||||
$caserun->attach_bug($vars->{'id'});
|
||||
|
||||
$vars->{'caserun'} = $caserun;
|
||||
}
|
||||
elsif (detaint_natural($case_id)) {
|
||||
my $case = Bugzilla::Testopia::TestCase->new($cgi->param('case_id'));
|
||||
ThrowUserError("invalid-test-id-non-existent", {'id' => $case_id, 'type' => 'Case'}) unless $case;
|
||||
ThrowUserError("testopia-read-only", {'object' => $case}) unless $case->canedit;
|
||||
|
||||
$case->attach_bug($vars->{'id'});
|
||||
|
||||
$vars->{'case'} = $case;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/perl -w
|
||||
# -*- 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
|
||||
use strict;
|
||||
use Bugzilla::Testopia::Product;
|
||||
|
||||
my $vars = Bugzilla->hook_args->{vars};
|
||||
|
||||
$vars->{'testopia_product'} = new Bugzilla::Testopia::Product($vars->{product}->id);
|
|
@ -0,0 +1,266 @@
|
|||
|
||||
#header .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#header li.tr_find {
|
||||
}
|
||||
#header li.tr_saved_search {
|
||||
}
|
||||
|
||||
#header .testopia_btn {
|
||||
background-color:#546292;
|
||||
color:#ffffff;
|
||||
font-weight:bold;
|
||||
font-size:100%;
|
||||
border:none;
|
||||
}
|
||||
|
||||
#footer li.tr_find {
|
||||
}
|
||||
|
||||
#footer li.tr_saved_search {
|
||||
}
|
||||
#footer .tr_btn {
|
||||
background-color: #808285;
|
||||
font-size: 80%;
|
||||
color:#ffffff;
|
||||
font-size:100%;
|
||||
border:none;
|
||||
}
|
||||
|
||||
/* old default.css */
|
||||
textarea {
|
||||
/* background-color: #fff; */
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
.resultsTable {
|
||||
border-collapse: collapse; /*cellspacing*/
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.resultsTable td { /*only immediate tds of mytable*/
|
||||
padding: 4px; /*cellpadding*/
|
||||
}
|
||||
|
||||
.resultsTable td a:hover {
|
||||
text-decoration:underline overline;
|
||||
}
|
||||
|
||||
.resultsTable th {
|
||||
border-color:#FFF;
|
||||
background-color:#C0FFC0;
|
||||
|
||||
border-style:solid;
|
||||
border-width: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dlgTable {
|
||||
border-collapse: collapse; /*cellspacing*/
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
.dlgTable td { /*only immediate tds of mytable*/
|
||||
padding: 4px; /*cellpadding*/
|
||||
background-color:#EEE;
|
||||
}
|
||||
|
||||
.spacerTable {
|
||||
border-collapse: collapse; /*cellspacing*/
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.spacerTable td { /*only immediate tds of mytable*/
|
||||
padding: 16px; /*cellpadding*/
|
||||
background-color:#FFF;
|
||||
}
|
||||
|
||||
.menuTable {
|
||||
border-collapse: collapse; /*cellspacing*/
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.menuTable td { /*only immediate tds of mytable*/
|
||||
padding: 4px; /*cellpadding*/
|
||||
background-color:#EEE;
|
||||
|
||||
border-style:solid;
|
||||
border-width: 3px;
|
||||
border-color:#FFF;
|
||||
}
|
||||
|
||||
.evenRow_first {
|
||||
background-color:#FFF;
|
||||
}
|
||||
.evenRow {
|
||||
background-color:#FFF;
|
||||
}
|
||||
.evenRow td{
|
||||
background-color:#FFF;border-top-style:solid;border-top-width:1px;border-top-color:#CCC;
|
||||
}
|
||||
|
||||
.oddRow_first {
|
||||
background-color:#EEE;
|
||||
}
|
||||
.oddRow {
|
||||
background-color:#EEE;
|
||||
}
|
||||
.oddRow td {
|
||||
background-color:#EEE;border-top-style:solid;border-top-width:1px;border-top-color:#CCC;
|
||||
}
|
||||
|
||||
.highlightedCell {
|
||||
border-width:1px;
|
||||
border-style:solid;
|
||||
background-color:lightyellow;
|
||||
}
|
||||
|
||||
/* tr_showcaselog */
|
||||
|
||||
.short_body {
|
||||
border-bottom:1px solid #000;
|
||||
border-left:1px solid #000;
|
||||
border-right:1px solid #000;
|
||||
}
|
||||
|
||||
.short_head {
|
||||
border-top:1px solid #000;
|
||||
border-left:1px solid #000;
|
||||
border-right:1px solid #000;
|
||||
}
|
||||
|
||||
.ae_dv {
|
||||
display:none;
|
||||
margin-left:15px;
|
||||
border-width:0px;
|
||||
}
|
||||
|
||||
.ae_tb {
|
||||
width:100%;
|
||||
border:0;
|
||||
margin:0;
|
||||
margin-right:30px;
|
||||
margin-bottom:10px;
|
||||
border-collapse:collapse;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
.ae_s {
|
||||
border-style:solid;
|
||||
border-width:1px;
|
||||
border-color:#000;
|
||||
background-color:#FFFFE0;
|
||||
}
|
||||
|
||||
.cc_i {
|
||||
float:left;
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
.cc_xx {
|
||||
clear:both;
|
||||
margin-right:30px;
|
||||
padding-top:5px;
|
||||
}
|
||||
|
||||
.cc_trg {
|
||||
padding-left:5px;
|
||||
padding-right:5px;
|
||||
vertical-align:baseline;
|
||||
}
|
||||
|
||||
/* TODO: refactor these two: */
|
||||
|
||||
#floatMsg {
|
||||
text-align:right;
|
||||
background-color:#FFDD66;
|
||||
color:black;
|
||||
padding:4px;
|
||||
padding-left:20px;
|
||||
padding-right:20px;
|
||||
font-family:Arial;
|
||||
font-weight:bold;
|
||||
font-size:12px;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.floatMsg {
|
||||
text-align:right;
|
||||
background-color:#FFDD66;
|
||||
color:black;
|
||||
padding:4px;
|
||||
padding-left:20px;
|
||||
padding-right:20px;
|
||||
font-family:Arial;
|
||||
font-weight:bold;
|
||||
font-size:12px;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.tr_button {
|
||||
width:100px;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
h3 {
|
||||
border-bottom: 1px dotted #000;
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
.bz_row_odd
|
||||
{
|
||||
background-color: #F7F7F7;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.bz_row_even
|
||||
{
|
||||
background-color: #FFFFFF;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.bz_row_header { background-color: #E7E9E7;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.bz_row_data { padding-bottom: 5px;}
|
||||
|
||||
.img_button_16x {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.plan_archived {
|
||||
background: url(../../testopia/img/archived.png);
|
||||
background-repeat: no-repeat;
|
||||
width: 120px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.x-progress-bar-red{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/red_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;}
|
||||
.x-progress-bar-green{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/green_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;}
|
||||
.x-progress-bar-orange{height:18px;float:left;width:0;background:#9CBFEE url( ../../testopia/img/orange_bar.gif ) repeat-x left center;border-top:1px solid #D1E4FD;border-bottom:1px solid #7FA9E4;}
|
||||
.x-progress-text-main{font-size:11px;font-weight:bold;color:#fff;padding:3px 5px;overflow:hidden;position:absolute;left:0;text-align:center;}
|
||||
.x-progress-text-back-main{color:#000;line-height:16px;font-weight:bold;}
|
||||
|
||||
.x-hidden {position: absolute; left: -1000px; top: -100px;}
|
||||
|
||||
.t1 { background-color: #ffffff } /* white */
|
||||
.t2 { background-color: #dfefff } /* light blue */
|
||||
.t3 { background-color: #dddddd } /* grey */
|
||||
.t4 { background-color: #c3d3ed } /* darker blue */
|
||||
.ttotal { background-color: #cfffdf } /* light green */
|
||||
|
||||
.msg .x-box-mc {
|
||||
font-size:14px;
|
||||
}
|
||||
#msg-div {
|
||||
position:absolute;
|
||||
left:35%;
|
||||
top:10px;
|
||||
width:250px;
|
||||
z-index:20000;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%
|
||||
title = "Testopia"
|
||||
desc = "Set Testopia parameters"
|
||||
%]
|
||||
|
||||
[% param_descs = {
|
||||
"private-cases-log" => "If this option is on, the tester cannot view other testers' cases",
|
||||
|
||||
"allow-test-deletion" => "If this option is on, users can delete objects including plans and cases",
|
||||
|
||||
"testopia-allow-group-member-deletes" => "If this option is on, members of the Testers group will be
|
||||
allowed to delete test objects",
|
||||
|
||||
"testopia-default-plan-testers-regexp" => "This is the default regular expression for granting
|
||||
access to new test plans",
|
||||
|
||||
"print-tag-in-case-log" => 'If this option is on, the entire tag text is printed in a test case ' _
|
||||
'log entry. Otherwise, only an href to the tag is put there.',
|
||||
|
||||
"new-case-action-template" => "This is the text to be put in a newly created test case \"action\" field",
|
||||
|
||||
"new-case-results-template" => "This is the text to be put in a newly created test case \"expected results\" field",
|
||||
|
||||
"bug-to-test-case-summary" => 'This is the default summary text used when generating a test case ' _
|
||||
'out of a bug. The special symbol %id% is replaced with the bug id and ' _
|
||||
"%summary% is replaced with the bug's summary",
|
||||
|
||||
"bug-to-test-case-action" => 'This is the default action text used when generating a test case ' _
|
||||
'out of a bug. The special symbol %id% is replaced with a ' _
|
||||
"hyperlink to the bug's page. The special symbol \%description\% is " _
|
||||
"replaced with the bug's description",
|
||||
|
||||
"bug-to-test-case-results" => 'This is the default results text used when generating a test case ' _
|
||||
'out of a bug. The special symbol %id% is replaced with a ' _
|
||||
"hyperlink to the bug's page",
|
||||
|
||||
"default-test-case-status" => 'Default status for newly created test cases.',
|
||||
|
||||
"new-testrun-email-notif" => 'E-mail message sent to assigned testers when a new test run is started. ' _
|
||||
'There are some special symbols replaced at run time:<br/>' _
|
||||
'%to%: list of assigned testers email addresses<br/>' _
|
||||
'%summary%: test run summary<br/>' _
|
||||
'%plan%: plan\'s name<br/>' _
|
||||
'%plan_id%: plan\'s id<br/>' _
|
||||
'%product%: product\'s name<br/>' _
|
||||
'%product_id%: product\'s id',
|
||||
|
||||
"case-failed-email-notif" => 'E-mail message sent when a test case log is marked as \'failed\'. ' _
|
||||
'There are some special symbols replaced at run time:<br/>' _
|
||||
'%id%: test case log id<br/>' _
|
||||
'%manager%: test run\'s manager<br/>' _
|
||||
'%test_run%: test run\'s summary<br/>' _
|
||||
'%tester%: tester<br/>' _
|
||||
'%component%: component\'s name',
|
||||
|
||||
"tester-completed-email-notif" => 'E-mail message sent when a tester has run every assigned test case.',
|
||||
|
||||
"test-run-completed-email-notif" => 'E-mail message sent when every test case in a test run is completed.',
|
||||
"testopia-max-allowed-plan-testers" => 'Limit to how many users a plan access regular expression should match',
|
||||
"testopia-debug" => 'Developer: Load individual files (Slowest) <br>ON: Use the debug version of Javascript files (Slower but needful if reporting javascript related problems) <br/> OFF: Use the compressed version (faster)',
|
||||
|
||||
}
|
||||
|
||||
%]
|
|
@ -255,6 +255,8 @@
|
|||
|
||||
[% END %]
|
||||
|
||||
[% Hook.process("additional-product-values") %]
|
||||
|
||||
[% IF product.bug_count == 0 || Param('allowbugdeletion') %]
|
||||
|
||||
<p>Do you really want to delete this product?</p>
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"never" => "Never",
|
||||
"cc_unless_role" => "Only if I have no role on them",
|
||||
"lang" => "Language used in email",
|
||||
"view_testopia" => "View the Testopia links",
|
||||
"quote_replies" => "Quote the associated comment when you click on its reply link",
|
||||
"quoted_reply" => "Quote the full comment",
|
||||
"simple_reply" => "Reference the comment number only",
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td style="color: red; weight: bolder">
|
||||
This product has [% testopia_product.plans.size %] test plans
|
||||
and [% testopia_product.runs.size %] test runs.
|
||||
Deleting this product will delete all associated Testopia data including
|
||||
plans, cases, runs, builds, and environments.</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -0,0 +1,5 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
[% IF caserun_id %]
|
||||
<a href="tr_show_case.cgi?case_id=[% case_id FILTER url_quote %]">Back to test Case.</a>
|
||||
[% END %]
|
|
@ -0,0 +1,8 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
[% IF caserun_id %]
|
||||
<input type="hidden" name="caserun_id" value="[% caserun_id FILTER none %]" />
|
||||
[% END %]
|
||||
[% IF case_id %]
|
||||
<input type="hidden" name="case_id" value="[% case_id FILTER none %]" />
|
||||
[% END %]
|
|
@ -0,0 +1,5 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
[% IF caserun %]
|
||||
<h4 style="color:#C00;">[% terms.Bug %] attached to <a href="tr_show_case.cgi?case_id=[% case.id FILTER none %]">Test case [% case.id FILTER none %]</a></h4>
|
||||
[% END %]
|
|
@ -0,0 +1,16 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
[% test_case_count = bug.get_test_case_count %]
|
||||
<ul>
|
||||
[% IF test_case_count > 0 %]
|
||||
<li><a href="tr_list_cases.cgi?bug_id=[% bug.bug_id %]¤t_tab=case">
|
||||
View [% terms.Bug %] Test Cases ([% test_case_count %]
|
||||
[% IF test_case_count > 1 %]
|
||||
cases)
|
||||
[% ELSE %]
|
||||
case)
|
||||
[% END %]
|
||||
</a></li>
|
||||
[% END %]
|
||||
<li><a href="tr_new_case.cgi?product=[% bug.product FILTER url_quote %]&bug=[% bug.bug_id %]">Create test case</a></li>
|
||||
</ul>
|
|
@ -0,0 +1,6 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
[% IF case_id %]
|
||||
<br />
|
||||
<a href="tr_show_case.cgi?case_id=[% case_id FILTER url_quote %]">Back to test case.</a>
|
||||
[% END %]
|
|
@ -0,0 +1,21 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
<a href="http://www.mozilla.org/projects/testopia"><span>Testopia</span></a>
|
||||
<span>Version 2.1</span>
|
|
@ -0,0 +1,28 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[% IF error == "testopia_undefined_field" %]
|
||||
[% title = "Missing Field in test_fielddefs" %]
|
||||
The [% fieldname %] field was not found for the [% table %] table while logging activity.
|
||||
[% ELSIF error == "testopia-missing-attachment-key" %]
|
||||
[% title = "Missing Key" %]
|
||||
You have requested to save an attachment, but I didn't see which test plan
|
||||
or test case you had in mind to store it with.
|
||||
[% END %]
|
|
@ -0,0 +1,60 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% IF user.login AND user.settings.view_testopia.value == 'on' %]
|
||||
</ul>
|
||||
<div class="label">Testopia: </div>
|
||||
<ul class="links testopia_links">
|
||||
<li><a href="tr_show_product.cgi">Product Dashboard</a></li>
|
||||
[% IF user.in_group('Testers') %]
|
||||
<li><span class="separator">| </span><a href="tr_new_plan.cgi">New Plan</a></li>
|
||||
[% END %]
|
||||
<li><span class="separator">| </span><a href="tr_new_case.cgi?plan_id=[% plan_id FILTER none %]">New Case</a></li>
|
||||
<li><span class="separator">| </span><a href="tr_new_run.cgi?plan_id=[% plan_id FILTER none %]&case_status_id=2">New Run</a></li>
|
||||
<li><span class="separator">| </span><a href="tr_query.cgi">Search</a></li>
|
||||
|
||||
[% IF user.in_group('admin') %]
|
||||
<li><span class="separator">| </span><a href="tr_admin.cgi">Admin</a></li>
|
||||
[% END %]
|
||||
<li><span class="separator">| </span><a href="testopia/doc/Manual.pdf" target="_blank">Help</a></li>
|
||||
|
||||
<li class="tr_saved_search">
|
||||
<select class="dropdown" onchange="if (this.selectedIndex != 0) {document.location=this.value}">
|
||||
<option>--Testopia--</option>
|
||||
<option value="tr_list_runs.cgi?current_tab=run&run_status=0&distinct=1">Current Runs</a></option>
|
||||
<option value="tr_list_plans.cgi?current_tab=plan&report_type=myplans">My Plans</a></option>
|
||||
<option value="tr_list_cases.cgi?current_tab=case&summary_type=allwordssubstr&summary=&tcaction_type=allwordssubstr&tcaction=&tceffect_type=allwordssubstr&tceffect=&script_type=allwordssubstr&script=&requirement_type=allwordssubstr&requirement=&tag_type=allwords&tags=&author_type=exact&author=&default_tester_type=substring&default_tester=[% user.login FILTER url_quote %]&case_id=&plan_id=&distinct=1">My Cases</a></option>
|
||||
<option value="tr_list_runs.cgi?current_tab=run&summary_type=allwordssubstr&summary=¬es_type=allwordssubstr¬es=&environment_type=allwordssubstr&environment=&tag_type=allwords&tag=&manager_type=substring&manager=[% user.login FILTER url_quote %]&run_id=&plan_id=&assignee_type=substr&assignee=[% user.login FILTER url_quote %]&distinct=1&order=run_id&dir=DESC">My Runs</a></option>
|
||||
[% FOREACH q = user.testopia_queries %]
|
||||
<option value="[% q.query %]">[% q.name FILTER html %]</option>
|
||||
[% END %]
|
||||
</select>
|
||||
</li>
|
||||
<li class="form tr_find">
|
||||
<form action="tr_quicksearch.cgi">
|
||||
<input class="txt" type="text" name="searchstr" id="tr_quicksearch" />
|
||||
<input class='btn testopia_btn' type='submit' value='Find'>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
[% END %]
|
||||
<script type="text/javascript">
|
||||
DEFAULT_CASE_STATUS = '[% Param('default-test-case-status') %]';
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
<link href="skins/standard/testopia.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript">
|
||||
Testopia_user = {login: "[% user.login %]", id: [% user.id %]};
|
||||
</script>
|
|
@ -0,0 +1,36 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
[% IF user.login AND user.settings.view_testopia.value == 'on' %]
|
||||
|
||||
<div id="links-saved">
|
||||
<div class="label">
|
||||
Testopia Saved Searches:
|
||||
</div>
|
||||
<ul class="links">
|
||||
<li><a href="tr_list_runs.cgi?current_tab=run&run_status=0&distinct=1">Current Runs</a></li>
|
||||
<li><span class="separator">| </span><a href="tr_list_plans.cgi?current_tab=plan&report_type=myplans">My Plans</a></li>
|
||||
<li><span class="separator">| </span><a href="tr_list_cases.cgi?current_tab=case&summary_type=allwordssubstr&summary=&tcaction_type=allwordssubstr&tcaction=&tceffect_type=allwordssubstr&tceffect=&script_type=allwordssubstr&script=&requirement_type=allwordssubstr&requirement=&tag_type=allwords&tags=&author_type=exact&author=&default_tester_type=substring&default_tester=[% user.login FILTER url_quote %]&case_id=&plan_id=&distinct=1">My Cases</a></li>
|
||||
<li><span class="separator">| </span><a href="tr_list_runs.cgi?current_tab=run&summary_type=allwordssubstr&summary=¬es_type=allwordssubstr¬es=&environment_type=allwordssubstr&environment=&tag_type=allwords&tag=&manager_type=substring&manager=[% user.login FILTER url_quote %]&run_id=&plan_id=&assignee_type=substr&assignee=[% user.login FILTER url_quote %]&distinct=1&order=run_id&dir=DESC">My Runs</a></li>
|
||||
[% FOREACH query = user.testopia_queries %]
|
||||
<li><span class="separator">| </span><a href="[% query.query %]">[% query.name FILTER html %]</a></li>
|
||||
[% END %]
|
||||
</ul>
|
||||
[% END %]
|
||||
<form id="testopia_helper_frm"></form>
|
|
@ -0,0 +1,191 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[% IF error == "testopia-permission-denied" %]
|
||||
[% title = "Insufficient Permissions" %]
|
||||
You are not authorized to view [% object.type FILTER html %] [% object.id FILTER html %]
|
||||
[% ELSIF error == "testopia-read-only" %]
|
||||
[% title = "Insufficient Permissions" %]
|
||||
You are not authorized to edit [% object.type FILTER html %] [% object.id FILTER html %]
|
||||
[% IF object.type == 'caserun' %]
|
||||
Only test runs in the RUNNING state may be updated. Otherwise,
|
||||
[% END %]
|
||||
Contact the plan administrator to grant rights to update these [% object.type FILTER html %]s
|
||||
[% ELSIF error == "testopia-no-delete" %]
|
||||
[% title = "Insufficient Permissions" %]
|
||||
You are not authorized to delete [% object.type FILTER html %] [% object.id FILTER html %]
|
||||
[% IF user.in_group('admin') %]
|
||||
If you are the administrator, you can check that your installation
|
||||
allows deletion of Testopia objects in the <a href="editparams.cgi">paramaters </a>
|
||||
and then add delete rights to your plan on the "Permissions" tab.
|
||||
[% END %]
|
||||
[% ELSIF error == "testopia-create-denied" %]
|
||||
[% title = "Cannot Create Object" %]
|
||||
You do not have sufficient rights to create new [% object FILTER html %]s
|
||||
[% IF plan %] for plan [% plan.id %][% END %].
|
||||
[% ELSIF error == "testopia-missing-parameter" %]
|
||||
[% title = "Missing Parameter" %]
|
||||
Missing required parameter [% param FILTER html %]
|
||||
[% ELSIF error == "testopia-non-zero-case-count" %]
|
||||
[% title = "Category Has Test Cases" %]
|
||||
You have attempted to delete a category that still has test cases
|
||||
associated with it.
|
||||
Please move the test cases to another category first.
|
||||
[% ELSIF error == "testopia-non-zero-run-count" %]
|
||||
[% title = "$object Has Test Runs" %]
|
||||
You have attempted to delete a [% object FILTER html %] that has test runs
|
||||
associated with it.
|
||||
[% ELSIF error == "testopia-non-zero-case-run-count" %]
|
||||
[% title = "Build Has Case Runs" %]
|
||||
You have attempted to delete a build that still has test case runs
|
||||
associated with it. This is historical data and should not be removed.
|
||||
[% ELSIF error == "testopia-invalid-test-id-or-alias" %]
|
||||
[% title = "Invalid ID or Alias" %]
|
||||
The test case you entered does not exist.
|
||||
[% ELSIF error == "invalid-test-id-non-existent" %]
|
||||
[% title = "Invalid ID" %]
|
||||
[% IF type == 'case_run' %]
|
||||
The specified case-run does not exist.
|
||||
[% ELSE %]
|
||||
Test [% type FILTER html %] [% id FILTER html %] does not exist.
|
||||
[% END %]
|
||||
[% ELSIF error == "testopia-name-not-unique" %]
|
||||
[% title = "Name Must be Unique" %]
|
||||
The name you chose, [% name FILTER html %], for this
|
||||
[% object FILTER html %] must be unique. Another [% object FILTER html %]
|
||||
of the same name already exists. Please choose a different one.
|
||||
[% ELSIF error == 'testopia-create-category' %]
|
||||
[% title = "No Categories Defined" %]
|
||||
No categories have been created for this product yet.
|
||||
[% IF plan %]
|
||||
You can create categories <a href="tr_categories.cgi?product_id=[% plan.product.id FILTER none %]">
|
||||
here</a>.
|
||||
[% END %]
|
||||
[% ELSIF error == "testopia-create-build" %]
|
||||
[% title = "No Builds Defined" %]
|
||||
No builds have been created for this product yet.
|
||||
[% IF plan %]
|
||||
You can create builds <a href="tr_builds.cgi?product_id=[% plan.product.id FILTER none %]">
|
||||
here</a>.
|
||||
[% END %]
|
||||
[% ELSIF error == "testopia-create-environment" %]
|
||||
[% title = "No Environments Defined" %]
|
||||
No environments have been created yet.
|
||||
You can create environments <a href="tr_new_environment.cgi">here</a>.
|
||||
[% ELSIF error == "testopia-missing-required-field" %]
|
||||
[% title = "Missing Required Field" %]
|
||||
It seems there was either no value entered for [% field FILTER none %], or the value entered
|
||||
did not match any known values.
|
||||
[% ELSIF error == "testopia-missing-plans-list" %]
|
||||
[% title = "Plan list missing" %]
|
||||
Somehow the list of plans associated with the test case to be cloned is missing.
|
||||
Please try again.
|
||||
[% ELSIF error == "testopia-unkown-object" %]
|
||||
[% title = "Unkown Object" %]
|
||||
Attempted to attach tags to something other than a Test Plan, Test Case or Test Run.
|
||||
[% ELSIF error == "testopia-element-in-use" %]
|
||||
[% title = "Element in Use" %]
|
||||
The selected element is being used in one or more environments.
|
||||
Please [% IF NOT delete %]create a new one or [% END %]
|
||||
remove it from existing environments before performing this operation.
|
||||
[% ELSIF error == "testopia-invalid-char" %]
|
||||
[% title = "Invalid Character" %]
|
||||
Valid names for values cannot contain '|' characters.
|
||||
[% ELSIF error == "testiopia-alias-exists" %]
|
||||
[% title = "Alias Unavailable" %]
|
||||
The alias '[% alias FILTER html %]' is already in use by another test case, please
|
||||
select another one.
|
||||
[% ELSIF error == "testiopia-invalid-data" %]
|
||||
[% title = "Invalid Option Selected" %]
|
||||
The field [% field FILTER none %] has and invalid value of [% value FILTER html %]
|
||||
[% ELSIF error == "testopia-none-selected" %]
|
||||
[% title = "Nothing Selected" %]
|
||||
You did not select any [% object FILTER none %]s to change.
|
||||
[% ELSIF error == "testopia-query-too-large" %]
|
||||
This query returns more than [% limit FILTER none %] rows. <br>
|
||||
Please do one of the following:
|
||||
<ol>
|
||||
<li>Try to narrow your search.</li>
|
||||
<li>Try using the pagesize and page URL paramaters to limit your results.</li>
|
||||
<li>Use the XML-RPC Interface.</li>
|
||||
</ol>
|
||||
[% ELSIF error == "missing-plans-list" %]
|
||||
[% title = "No plans selected" %]
|
||||
You did not select any plans to copy or link this case to.
|
||||
[% ELSIF error == "testopia-tester-already-on-list" %]
|
||||
[% title = "Selected user is already on the list" %]
|
||||
The user [% login FILTER html %] is already a member of the ACL for this plan.
|
||||
[% ELSIF error == "testopia-plan-acl-denied" %]
|
||||
[% title = "Plan Administrator Privileges Required" %]
|
||||
You must be an administrator of this test plan to modify the access control list.
|
||||
[% ELSIF error == "testopia-no-admins" %]
|
||||
[% title = "Plan Must Have at Least One Admin" %]
|
||||
There do not seem to be any admins selected. You must select at least one admin for this test plan.
|
||||
[% ELSIF error == "testopia-format-error" %]
|
||||
[% title = "Field format error" %]
|
||||
[% field %] is not formatted correctly.
|
||||
[% ELSIF error == "plan-has-children" %]
|
||||
[% title = "Plan has Children" %]
|
||||
This test plan has children and so it is not possible to change the product.
|
||||
You may wish to clone the plan instead.
|
||||
[% ELSIF error == "testopia-no-status" %]
|
||||
[% title = "Permission Denied" %]
|
||||
You must either be the run manager or a plan administrator to change
|
||||
[% IF field == 'status' %]
|
||||
the status of a run.
|
||||
[% ELSIF field == 'manager' %]
|
||||
the run manager
|
||||
[% ELSIF field == 'target' %]
|
||||
the target pass or completion rates
|
||||
[% END %]
|
||||
[% ELSIF error == "testopia-update-failed" %]
|
||||
[% title = "One or more of your test $object failed to update" %]
|
||||
Update of one or more of your [% object %]s failed. The most likely cause is that you don't have permissions
|
||||
to update them.
|
||||
[% IF object == 'case-run' %]
|
||||
If the case is in the RUNNING state, only the assignee may update it. Otherwise,
|
||||
[% END %]
|
||||
Contact the plan administrator to grant rights to update these [% object %]s.
|
||||
<p>
|
||||
The id's of those that failed are: [% list FILTER none %]
|
||||
</p>
|
||||
[% ELSIF error == "testopia-unknown-tag" %]
|
||||
The tag "[% name FILTER html %]" does not exist.
|
||||
[% ELSIF error == "testopia-regexp-too-inclusive" %]
|
||||
You are attempting to allow access to your test plan to more people than the limit set by your administrator allows.
|
||||
Please contatct your administrator.
|
||||
[% ELSIF error == "testopia-unknown-tab" %]
|
||||
I don't know what a [% tab FILTER html %] is.
|
||||
[% ELSIF error == 'invalid_import_type' %]
|
||||
Invalid file type. Only CSV and XML files can be imported (.csv or .xml extension). You uploaded a [% type FILTER html %] file.
|
||||
[% ELSIF error == 'import_repeat' %]
|
||||
It appears you tried to import the same file twice.
|
||||
[% ELSIF error == 'csv_parse_failure' %]
|
||||
There was an error parsing the input. Please verify that the CSV is valid.
|
||||
The Error was on row [% row FILTER none %]
|
||||
[% ELSIF error == "testopia_component_attached" %]
|
||||
The component you are attemting to add is already attached to this case.
|
||||
[% ELSIF error == "testopia-no-action" %]
|
||||
I give up. What were you trying to do?
|
||||
Either there was no action specified, or I didn't recognize it.
|
||||
[% ELSIF error == "invalid_target" %]
|
||||
Target Completion and Pass rates must be a number between 0 and 100 inclusive.
|
||||
[% END %]
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
<hr />
|
||||
<a name="testopia" />
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<th colspan="2">Testopia Version 2.2-BETA1</th>
|
||||
</tr>
|
||||
<tr valign="TOP">
|
||||
<td width="128">
|
||||
<p><img
|
||||
src="testopia/img/testopia_city_128.png"
|
||||
width="128" height="128" border="0" alt="Testopia"><br>
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p><b>Testopia</b> choices:</p>
|
||||
[% IF user.login %]
|
||||
<p>
|
||||
<a href="tr_list_runs.cgi?current_tab=run&run_status=0">Do some testing</a><br>
|
||||
<a href="tr_show_product.cgi?tab=plan">Manage test plans</a><br>
|
||||
<a href="tr_show_product.cgi?search=1">Search existing test cases</a><br>
|
||||
[% ELSE %]
|
||||
You must <a href="relogin.cgi">login to [% terms.Bugzilla %]</a> to see Testopia features.
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<br />
|
|
@ -0,0 +1,3 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
|
||||
<!-- <a href="tr_buglist_to_plan.cgi">Add [% terms.bugs %] to test plan</a> | -->
|
|
@ -0,0 +1,42 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Add a New Test Plan Type"
|
||||
%]
|
||||
|
||||
<form method="POST" action="tr_admin.cgi">
|
||||
<input type="hidden" name="item" value="plan_type" />
|
||||
<input type="hidden" name="action" value="doadd" />
|
||||
<table>
|
||||
<tr>
|
||||
<th align="right">Type Name:</th>
|
||||
<td><input name="name" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right" valign="top">Description:</th>
|
||||
<td><textarea cols="40" name="description" ></textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit" value="Commit" />
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,24 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Test Plan Types"
|
||||
%]
|
|
@ -0,0 +1,43 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Edit Test Plan Types"
|
||||
%]
|
||||
|
||||
<form method="POST" action="tr_admin.cgi">
|
||||
<input type="hidden" name="item" value="plan_type" />
|
||||
<input type="hidden" name="action" value="doedit" />
|
||||
<input type="hidden" name="type_id" value="[% type.id FILTER html %]" />
|
||||
<table>
|
||||
<tr>
|
||||
<th align="right">Type Name:</th>
|
||||
<td><input name="name" value="[% type.name FILTER html %]"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right" valign="top">Description:</th>
|
||||
<td><textarea cols="40" name="description" >[% type.description FILTER html %]</textarea></td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit" value="Commit" />
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,42 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Test Plan Types"
|
||||
%]
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
[% FOREACH type = plan.get_plan_types %]
|
||||
<tr>
|
||||
<td>[% type.name FILTER html %]</td>
|
||||
<td><a href="tr_admin.cgi?item=plan_type&action=edit&type_id=[% type.id FILTER none %]">Edit</a></td>
|
||||
</tr>
|
||||
[% END %]
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><a href="tr_admin.cgi?item=plan_type&action=add">Add</a></td>
|
||||
</table>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,45 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Admin Settings for Testopia" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
|
||||
<a href="tr_admin.cgi?item=plan_type">Plan Types</a>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,38 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Search by Attachment Number"
|
||||
%]
|
||||
|
||||
<form method="get" action="tr_attachment.cgi">
|
||||
<p>
|
||||
You may find an attachment by entering its id here:
|
||||
<input name="attach_id" size="6" />
|
||||
<input type="submit" value="Show Me This Attachment" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,21 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Jeff Dayley.
|
||||
# Portions created by Jeff Dayley are Copyright (C) 2007
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Jeff Dayley <jedayley@novell.com>
|
||||
#%]
|
||||
|
||||
[% json %]
|
|
@ -0,0 +1,165 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2001
|
||||
# Greg Hendricks. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
# Karla Hendricks <palegreensocks@gmail.com>
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Block for SELECT fields #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% BLOCK select %]
|
||||
<label for="[% sel.name %]" accesskey="[% sel.accesskey %]"></label>
|
||||
<select name="[% sel.name %]" id="[% sel.id || sel.name %]"
|
||||
[%- "multiple=\"multiple\"" IF sel.mult %]
|
||||
[%- "size=\"$sel.elements\"" IF sel.elements %]
|
||||
[%- sel.events IF sel.events %]
|
||||
[%- sel.style IF sel.style %]>
|
||||
[% FOREACH item = sel.list %]
|
||||
<option value="[% sel.byname ? item.name : item.id FILTER html %]"
|
||||
[%- 'selected="selected"' IF (sel.default == item.name && item.name != '') %]
|
||||
[%- 'selected="selected"' IF sel.default == item.id && item.id != ''%]
|
||||
[%- 'selected="selected"' IF sel.default == 'all' %]
|
||||
[%- 'selected="selected"' IF (sel.deflist == 1 AND lsearch(sel.default, item.id)> -1) %]>
|
||||
[% item.name FILTER html %]</option>
|
||||
[% END %]
|
||||
</select>
|
||||
|
||||
[% END %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Block for Navigation Links #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% BLOCK navigation %]
|
||||
[% SET MAX_LIMIT = 10000 %]
|
||||
[% IF ajax %]
|
||||
[% url = "javascript:page(" %]
|
||||
[% pc = ")" %]
|
||||
[% ELSE %]
|
||||
[% url = "$table.get_page_url&pagesize=$table.page_size&page="%]
|
||||
[%# url = "$table.url_loc?direction=$direction&getlist=1&page="%]
|
||||
[% END %]
|
||||
[% IF NOT table.viewall %]
|
||||
[% pagesizelist = [{name => 25, id => 25},
|
||||
{name => 50, id => 50},
|
||||
{name => 100, id => 100},
|
||||
{name => 500, id => 500}]%]
|
||||
|
||||
<script type='text/javascript'>
|
||||
var [% table.type %]Table = {
|
||||
|
||||
jumpToPage: function (page){
|
||||
var digit = /^\d+$/;
|
||||
page = page - 1;
|
||||
if ((page >= [% table.page_count %]) || (page < 0) || !digit.test(page)){
|
||||
alert('Invalid page number. Please enter a value between 1 and [% table.page_count %] ');
|
||||
return;
|
||||
}
|
||||
var url = '[% url %]' + page;
|
||||
document.location = url;
|
||||
}
|
||||
};
|
||||
function adjustPageSize(psize) {
|
||||
document.location = "[% table.get_page_url %]&pagesize=" + psize;
|
||||
}
|
||||
</script>
|
||||
|
||||
<table align="center">
|
||||
<tr style="background-color: #eeeeee; ">
|
||||
[% IF table.page != 0 %]
|
||||
<td><a href="[% url %]0[% pc %]"><< First</a></td>
|
||||
<td><a href="[% url %][% table.page - 1 %][% pc %]">< Previous</a></td>
|
||||
[% ELSE %]
|
||||
<td><< First</td>
|
||||
<td>< Previous</td>
|
||||
[% END %]
|
||||
<td align="center" style="font-size: medium">
|
||||
[% SET framewidth = 10 %]
|
||||
[% IF (table.page_count - 1) < framewidth %]
|
||||
[% SET p = 0 %]
|
||||
[% SET frame_end = table.page_count - 1 %]
|
||||
[% ELSIF (table.page_count - table.page) < (framewidth / 2) %]
|
||||
[% SET p = table.page_count - framewidth %]
|
||||
[% SET frame_end = table.page_count - 1 %]
|
||||
[% SET start_mark = '...' %]
|
||||
[% ELSIF table.page < (framewidth / 2) %]
|
||||
[% SET p = 0 %]
|
||||
[% SET frame_end = framewidth %]
|
||||
[% SET end_mark = '...' %]
|
||||
[% ELSE %]
|
||||
[% SET p = table.page - (framewidth / 2) %]
|
||||
[% SET frame_end = table.page + (framewidth / 2) %]
|
||||
[% SET start_mark = '...' %]
|
||||
[% SET end_mark = '...' %]
|
||||
[% END %]
|
||||
|
||||
[% start_mark %]
|
||||
[% WHILE p <= frame_end %]
|
||||
[% IF p != table.page %]
|
||||
<a href="[% url %][% p %][% pc %]">[% p + 1 %]</a>
|
||||
[% ELSE %]
|
||||
[% p + 1 %]
|
||||
[% END %]
|
||||
[% p = p + 1 %]
|
||||
[% END %]
|
||||
[% end_mark %]
|
||||
|
||||
</td>
|
||||
[% IF table.page < table.page_count - 1 %]
|
||||
<td><a href="[% url %][% table.page + 1 %][% pc %]">Next ></a></td>
|
||||
<td><a href="[% url %][% table.page_count - 1 %][% pc %]" title="Jump to Page [% table.page_count %]">Last >></a></td>
|
||||
[% ELSE %]
|
||||
<td>Next ></td>
|
||||
<td>Last >></td>
|
||||
[% END %]
|
||||
|
||||
</tr>
|
||||
<td colspan="5">
|
||||
<input type="button" onclick="[% table.type %]Table.jumpToPage(document.getElementById('jump_[% table.type %]').value)" value="Jump To Page">
|
||||
<input id="jump_[% table.type %]" size="4">
|
||||
Out of [% table.page_count %] pages
|
||||
|
||||
View [% PROCESS select sel = {list => pagesizelist,
|
||||
default => table.page_size,
|
||||
events => 'onchange="adjustPageSize(this.value)"' }%]
|
||||
records at a time
|
||||
|
||||
[% IF ajax %]
|
||||
<a href="[% url %]null,1[% pc %]">View All</a>
|
||||
[% ELSE %]
|
||||
[% IF table.list_count < MAX_LIMIT AND table.list_count > table.page_size %]
|
||||
<a href="[% url %]&viewall=1">View All</a>
|
||||
[% ELSE %]
|
||||
<span style="color:gray">View All</span>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr><th colspan="5" style="font-size: 12pt;">[% table.list_count %] records found [% '(filtered)' IF filtered %]</th></tr>
|
||||
</table>
|
||||
[% ELSE %]
|
||||
<table align="center">
|
||||
<tr style="background-color: #eeeeee; ">
|
||||
<td align="center"><a href="[% url %]0">Paged View</a></td>
|
||||
</tr>
|
||||
<tr><th colspan="5" style="font-size: 12pt;">[% table.list_count %] records found [% '(filtered)' IF filtered %]</th></tr>
|
||||
</table>
|
||||
[% END %]
|
||||
[% END %]
|
|
@ -0,0 +1,59 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%# testopia/case/add.html.tmpl - Services tr_new_case.cgi
|
||||
#
|
||||
# INTERFACE:
|
||||
# components - a JSON list of component QA contacts
|
||||
# case - The case object
|
||||
# form_action - cgi script to which the form should be submitted
|
||||
# since form.html.tmpl is used for both creating a test case
|
||||
# and updating an existing one.
|
||||
#
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
|
||||
[% title = "Create a New Test Case" %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl %]
|
||||
|
||||
<script type="text/javascript">
|
||||
Ext.onReady(function(){
|
||||
Ext.QuickTips.init();
|
||||
var form = new NewCaseForm('[% tc.plans %]', [% product_id FILTER none %]);
|
||||
form.render('caseform');
|
||||
[% IF tc.summary %]
|
||||
Ext.getCmp('ncf-summary').setValue('[% tc.summary FILTER js %]');
|
||||
[% END %]
|
||||
[% IF bugs %]
|
||||
Ext.getCmp('ncf-bugs').setValue('[% bugs FILTER js %]');
|
||||
[% END %]
|
||||
Ext.getCmp('ncf-action').setValue('[% tc.text.action FILTER js %]');
|
||||
Ext.getCmp('ncf-effect').setValue('[% tc.text.effect FILTER js %]');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="caseform"></div>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,38 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Search by Test Case Number"
|
||||
%]
|
||||
|
||||
<form method="get" action="tr_show_case.cgi">
|
||||
<p>
|
||||
You may find a test case by entering its id here:
|
||||
<input name="case_id" size="6">
|
||||
<input type="submit" value="Show Me This Test Case">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,88 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
|
||||
<form id="case_filter" method="GET">
|
||||
<input type="hidden" name="plan_id" value="[% plan.id FILTER none %]" />
|
||||
<input type="hidden" name="addrun" value="[% addrun FILTER none %]" />
|
||||
<input type="hidden" name="case_status" value="CONFIRMED" />
|
||||
<table>
|
||||
<tr class="bz_row_header">
|
||||
<th>Category</th>
|
||||
<th>Priority</th>
|
||||
<th>Components</th>
|
||||
<th>Automatic</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[% PROCESS select sel = { name => 'category_id',
|
||||
accesskey => 't',
|
||||
list => plan.product.categories,
|
||||
elements => 5,
|
||||
mult => 1 } %]
|
||||
</td>
|
||||
<td>
|
||||
[% PROCESS select sel = { name => 'priority_id',
|
||||
accesskey => 'p',
|
||||
list => case.get_priority_list
|
||||
elements => 5,
|
||||
mult => 1 } %]
|
||||
</td>
|
||||
<td>
|
||||
[% PROCESS select sel = { name => 'component',
|
||||
accesskey => 'm',
|
||||
list => plan.product.components
|
||||
elements => 5,
|
||||
mult => 1,
|
||||
byname => 1 } %]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => 'isautomated',
|
||||
accesskey => 'a',
|
||||
list =>
|
||||
[ { id => "0", name => "Manual" },
|
||||
{ id => "1", name => "Automatic" } ]
|
||||
elements => 5,
|
||||
mult => 1 } %]
|
||||
</td>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<th align="right" class="bz_row_header">Summary Contains:</th>
|
||||
<td><input type="hidden" name="summary_type" value="allwordssubstr" />
|
||||
<input name="summary"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right" class="bz_row_header">Tags:</th>
|
||||
<td><input type="hidden" name="tags_type" value="anyexact" />
|
||||
<input id="tags" name="tags">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right" class="bz_row_header">Default Tester Contains:</th>
|
||||
<td><input type="hidden" name="default_tester_type" value="substring" />
|
||||
<input name="default_tester"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
|
@ -0,0 +1,40 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Version:</th><td>[% text.version FILTER none %]</td><th>Author:</th><td>[% text.author FILTER html %]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="plan_text_version">
|
||||
<h3>Setup</h3>
|
||||
[% text.setup FILTER html_light %]
|
||||
<h3>Breakdown</h3>
|
||||
[% text.breakdown FILTER html_light %]
|
||||
<h3>Action</h3>
|
||||
[% text.action FILTER html_light %]
|
||||
<h3>Expected Results</h3>
|
||||
[% text.effect FILTER html_light %]
|
||||
</div>
|
|
@ -0,0 +1,33 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# David Koenig <dkoenig@novell.com>
|
||||
#%]
|
||||
|
||||
[%# Testopia Test Case list CSV export template #%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% colsepchar = user.settings.csv_colsepchar.value %]
|
||||
|
||||
[% PROCESS testopia/export/csv.caseheader.tmpl %]
|
||||
|
||||
[% FOREACH test_case = table.list %]
|
||||
[% PROCESS testopia/export/csv.case.tmpl case=test_case %]
|
||||
|
||||
[% END %]
|
|
@ -0,0 +1,100 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/Portal.js") %]
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/PortalColumn.js") %]
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/Portlet.js") %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl %]
|
||||
|
||||
<script type="text/javascript">
|
||||
Ext.onReady(function(){
|
||||
Ext.QuickTips.init();
|
||||
var params = searchToJson(window.location.search);
|
||||
var list_grid = new CaseGrid(params, {});
|
||||
var objPanel = new Ext.Panel({
|
||||
layout: 'border',
|
||||
height: 650,
|
||||
monitorResize: true,
|
||||
split: true,
|
||||
id:'list_view',
|
||||
applyTo: 'list_div',
|
||||
items: [{
|
||||
id: 'object_panel',
|
||||
region: 'center',
|
||||
xtype: 'tabpanel',
|
||||
enableTabScroll: true,
|
||||
activeTab: 0,
|
||||
items: [list_grid, new DashboardPanel({})]
|
||||
},{
|
||||
region: 'east',
|
||||
width: 250,
|
||||
split: true,
|
||||
enableTabScroll: true,
|
||||
activeTab: 2,
|
||||
xtype: 'tabpanel',
|
||||
items: [
|
||||
new ReportGrid({
|
||||
title: 'Dashboards',
|
||||
id: 'dashboard_grid',
|
||||
type: 3
|
||||
}),
|
||||
new ReportGrid({
|
||||
title: 'Reports',
|
||||
id: 'reports_grid',
|
||||
type: 1
|
||||
}),
|
||||
new ReportGrid({
|
||||
title: 'Searches',
|
||||
id: 'searches_grid',
|
||||
type: 0
|
||||
})
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
list_grid.store.load();
|
||||
|
||||
Ext.getCmp('dashboardpanel').on('render', function(){
|
||||
Ext.getCmp('dashboardpanel').getTopToolbar().add({
|
||||
xtype: 'button',
|
||||
id: 'save_dashboard_btn',
|
||||
icon: 'testopia/img/save.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Save this dashboard',
|
||||
handler: function(b,e){
|
||||
saveSearch('dashboard', Testopia.Search.dashboard_urls);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<div id="list_div" style="height:650px"></div>
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,21 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%- json -%]
|
|
@ -0,0 +1,29 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): David Koenig <dkoenig@novell.com>
|
||||
#%]
|
||||
|
||||
[%# Testopia Show Test Case XML export template #%]
|
||||
|
||||
[% PROCESS testopia/export/xml.header.tmpl -%]
|
||||
|
||||
[% FOREACH test_case = table.list %]
|
||||
[%+ PROCESS testopia/export/xml.case.tmpl case=test_case %]
|
||||
[% END %]
|
||||
|
||||
[%- PROCESS testopia/export/xml.footer.tmpl %]
|
|
@ -0,0 +1,76 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
<a href="tr_show_product.cgi?product_id=[% case.plans.0.product_id FILTER none %]">Product: [% case.plans.0.product.name FILTER html %]</a>
|
||||
>
|
||||
[% IF plan_id %]
|
||||
<a href="tr_show_plan.cgi?plan_id=[% plan_id FILTER none %]">Plan: [% plan_id FILTER html %]</a>
|
||||
[% ELSE %]
|
||||
<a href="tr_show_plan.cgi?plan_id=[% case.plans.0.id FILTER none %]" title="[% case.plans.0.name FILTER html %]">Plan [% case.plans.0.id FILTER html %]</a>
|
||||
[% END %]
|
||||
> Case [% case.id FILTER html %]
|
||||
<br/>
|
||||
[% list = table.get_saved_list('case') %]
|
||||
<b>Case List:</b>
|
||||
[% IF table && list > 0 %]
|
||||
[% curr = lsearch(list, case.id) %]
|
||||
[% IF curr != -1 && (curr > 0) %]
|
||||
<a href="tr_show_case.cgi?case_id=[% list.first %]"><< First</a>
|
||||
[% ELSE %]
|
||||
<i><font color="#777777"><<First</font></i>
|
||||
[% END %]
|
||||
|
||||
[% IF case %]
|
||||
[% IF curr != -1 %]
|
||||
[% IF curr > 0 %]
|
||||
[% prev = curr - 1 %]
|
||||
<a href="tr_show_case.cgi?case_id=[% list.$prev %]">< Prev</a>
|
||||
[% ELSE %]
|
||||
<i><font color="#777777">Prev</font></i>
|
||||
[% END %]
|
||||
|
||||
[% IF curr != -1 %]
|
||||
([% curr + 1 %] of [% list.size %])
|
||||
[% END %]
|
||||
|
||||
[% IF curr + 1 < list.size %]
|
||||
[% next = curr + 1 %]
|
||||
<a href="tr_show_case.cgi?case_id=[% list.$next %]">Next ></a>
|
||||
[% ELSE %]
|
||||
<i><font color="#777777">Next</font></i>
|
||||
[% END %]
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
|
||||
[% END %]
|
||||
|
||||
[% IF curr != -1 && (curr + 1 < list.size) %]
|
||||
<a href="tr_show_case.cgi?case_id=[% list.last %]">Last >></a>
|
||||
[% ELSE %]
|
||||
<i><font color="#777777">Last>></font></i>
|
||||
[% END %]
|
||||
|
||||
[% ELSE %]
|
||||
[%# With no list, don't show link to search results %]
|
||||
|
||||
<i><font color="#777777">No search results available</font></i>
|
||||
[% END %]
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<text>
|
||||
<newbug>
|
||||
<product>[% caserun ? caserun.run.plan.product.name : case.plans.0.product.name FILTER xml %]</product>
|
||||
<case_id>[% case.id FILTER none %]</case_id>
|
||||
<version>[% caserun ? caserun.run.plan.product_version : case.plans.0.product_version FILTER xml %]</version>
|
||||
<component>[% case.components.0.name FILTER xml %]</component>
|
||||
<short_desc><![CDATA[[Test Case [% case.id FILTER none %]] [% case.summary FILTER txt %]]]></short_desc>
|
||||
<comment><![CDATA[[% IF caserun %]
|
||||
STATUS: [% caserun.status %]
|
||||
BUILD: [% caserun.build.name FILTER txt %]
|
||||
ENVIRONMENT: [% caserun.environment.name FILTER txt %][% END %]
|
||||
STEPS TO REPRODUCE: [% case.text.action FILTER txt %]
|
||||
|
||||
EXPECTED OUTCOME: [% case.text.effect FILTER txt %]]]></comment>
|
||||
<assigned_to>[% case.components.0.default_assignee.login FILTER xml %]</assigned_to>
|
||||
<qa_contact>[% case.components.0.default_qa_contact.login FILTER xml %]</qa_contact>
|
||||
</newbug>
|
||||
</text>
|
|
@ -0,0 +1,32 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# David Koenig <dkoenig@novell.com>
|
||||
#%]
|
||||
|
||||
[%# Testopia Test Case CSV export template #%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% USE date %]
|
||||
|
||||
[% colsepchar = user.settings.csv_colsepchar.value %]
|
||||
|
||||
[% PROCESS testopia/export/csv.caseheader.tmpl %]
|
||||
|
||||
[% PROCESS testopia/export/csv.case.tmpl %]
|
|
@ -0,0 +1,721 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
|
||||
[% title = "Test Case $case.id: $case.summary" %]
|
||||
[% PROCESS global/header.html.tmpl %]
|
||||
|
||||
<script type="text/javascript">
|
||||
var tcase = [% case.TO_JSON FILTER html_light %];
|
||||
|
||||
Ext.onReady(function(){
|
||||
Ext.QuickTips.init();
|
||||
|
||||
var testopia_form = new Ext.form.BasicForm('testopia_helper_frm',{});
|
||||
|
||||
var confirmDelete = function(){
|
||||
Ext.Msg.show({
|
||||
title:'Confirm Delete?',
|
||||
msg: 'You are about to delete case ' + tcase.case_id + ' with all of its children and history. Are you sure you want to continue?',
|
||||
buttons: Ext.Msg.YESNO,
|
||||
fn: caseDelete,
|
||||
animEl: 'plan-delete-btn',
|
||||
icon: Ext.MessageBox.QUESTION
|
||||
});
|
||||
};
|
||||
var caseDelete = function(btn){
|
||||
if (btn == 'yes'){
|
||||
testopia_form.submit({
|
||||
url: 'tr_process_case.cgi',
|
||||
params: {case_id: tcase.case_id, action:'delete'},
|
||||
success: function(data){
|
||||
Ext.Msg.show({
|
||||
title:'Test Case Deleted',
|
||||
msg: "Test case " + tcase.case_id + " was deleted",
|
||||
buttons: Ext.Msg.OK,
|
||||
icon: Ext.MessageBox.INFO
|
||||
});
|
||||
window.location = 'tr_show_plan.cgi?plan_id=[% case.plans.0.id FILTER none %]';
|
||||
},
|
||||
failure: testopiaError
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function submitEdits(field, value){
|
||||
var params = {action: 'edit', case_id: tcase.case_id};
|
||||
switch(field){
|
||||
case 'priority':
|
||||
params.priority = value;
|
||||
break;
|
||||
case 'alias':
|
||||
params.alias = value;
|
||||
break;
|
||||
case 'status':
|
||||
params.status = value;
|
||||
break;
|
||||
case 'summary':
|
||||
params.summary = value;
|
||||
break;
|
||||
case 'category':
|
||||
params.category = value;
|
||||
break;
|
||||
case 'estimated_time':
|
||||
params.estimated_time = value;
|
||||
break;
|
||||
case 'tester':
|
||||
params.tester = value;
|
||||
break;
|
||||
case 'requirement':
|
||||
params.requirement = value;
|
||||
break;
|
||||
}
|
||||
testopia_form.submit({
|
||||
url: 'tr_process_case.cgi',
|
||||
params: params,
|
||||
success: function(){
|
||||
TestopiaUtil.notify.msg('Test Case updated', 'Test Case {0} was updated successfully', field);
|
||||
},
|
||||
failure: testopiaError
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function processText(){
|
||||
var params = {};
|
||||
params.tcsetup = Ext.getCmp('setup_editor').getValue();
|
||||
params.tcbreakdown = Ext.getCmp('breakdown_editor').getValue();
|
||||
params.tcaction = Ext.getCmp('action_editor').getValue();
|
||||
params.tceffect = Ext.getCmp('effect_editor').getValue();
|
||||
params.case_id = tcase.case_id;
|
||||
params.action = 'update_doc';
|
||||
testopia_form.submit({
|
||||
url: 'tr_process_case.cgi',
|
||||
method: 'POST',
|
||||
params: params,
|
||||
success: function(){
|
||||
TestopiaUtil.notify.msg('Test case updated', 'Test Case {0} was updated successfully', 'Document');
|
||||
},
|
||||
failure: testopiaError
|
||||
});
|
||||
}
|
||||
|
||||
var fileMenu = new Ext.menu.Menu({
|
||||
id: 'file-menu',
|
||||
items: [{
|
||||
text: "Clone",
|
||||
icon: 'testopia/img/copy.png',
|
||||
iconCls: 'img_button_16x',
|
||||
handler: function(){
|
||||
caseClonePopup(tcase.product_id, tcase.case_id);
|
||||
}
|
||||
},{
|
||||
text: "Print",
|
||||
handler: function(){
|
||||
window.location='tr_show_case.cgi?&case_id=[% case.id FILTER none %]&ctype=print';
|
||||
}
|
||||
},{
|
||||
text: 'Export',
|
||||
menu: [{
|
||||
text: 'CSV',
|
||||
handler: function(){
|
||||
window.location='tr_show_case.cgi?&case_id=[% case.id FILTER none %]&ctype=csv';
|
||||
}
|
||||
},{
|
||||
text: 'XML',
|
||||
handler: function(){
|
||||
window.location='tr_show_case.cgi?&case_id=[% case.id FILTER none %]&ctype=xml';
|
||||
}
|
||||
}]
|
||||
},{
|
||||
text: "Delete",
|
||||
id: 'btn-delete',
|
||||
icon: 'testopia/img/delete.png',
|
||||
iconCls: 'img_button_16x',
|
||||
disabled: tcase.candelete ? false : true,
|
||||
handler: confirmDelete
|
||||
}]
|
||||
});
|
||||
|
||||
var caseControls = new Ext.Toolbar();
|
||||
caseControls.render('case-tb-div');
|
||||
caseControls.add({
|
||||
text: 'File',
|
||||
menu: fileMenu
|
||||
},
|
||||
new Ext.Toolbar.Fill(),
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'plan-copy-button',
|
||||
template: button_16x_tmpl,
|
||||
icon: 'testopia/img/copy.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Clone this Test Case',
|
||||
handler: function(){
|
||||
caseClonePopup(tcase.product_id, tcase.case_id);
|
||||
}
|
||||
}
|
||||
[% IF case.candelete %]
|
||||
,{
|
||||
xtype: 'button',
|
||||
template: button_16x_tmpl,
|
||||
id: 'plan-delete-btn',
|
||||
icon: 'testopia/img/delete.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Delete this Test Case',
|
||||
handler: confirmDelete
|
||||
}
|
||||
[% END %]
|
||||
);
|
||||
var cases = new Ext.grid.GridPanel({
|
||||
|
||||
});
|
||||
var tutil = new TestopiaUtil();// utils yo!
|
||||
var casepanel = new Ext.Panel({
|
||||
layout: 'border',
|
||||
monitorResize: true,
|
||||
applyTo: 'case-body-div',
|
||||
height: 800,
|
||||
footer: true,
|
||||
items:[{
|
||||
xtype: 'tabpanel',
|
||||
enableTabScroll: true,
|
||||
id: 'object_panel',
|
||||
split: true,
|
||||
region: 'center',
|
||||
activeTab: 0,
|
||||
items: [
|
||||
{
|
||||
layout: 'column',
|
||||
title: 'Action / Expected Results',
|
||||
tbar: new DocCompareToolbar('case',tcase.case_id),
|
||||
id: 'action_panel',
|
||||
items: [{
|
||||
columnWidth:0.5,
|
||||
layout:'fit',
|
||||
items:{
|
||||
title: 'Action',
|
||||
height: 450,
|
||||
bodyBorder: false,
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
items:[{
|
||||
id: 'action_editor',
|
||||
xtype:'htmleditor',
|
||||
value: '[% case.text.action FILTER js %]'
|
||||
}]
|
||||
}
|
||||
},{
|
||||
columnWidth:0.5,
|
||||
layout:'fit',
|
||||
items:{
|
||||
title: 'Expected Results',
|
||||
height: 450,
|
||||
bodyBorder: false,
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
items:[{
|
||||
id: 'effect_editor',
|
||||
xtype:'htmleditor',
|
||||
value: '[% case.text.effect FILTER js %]'
|
||||
}]
|
||||
}
|
||||
}],
|
||||
buttons: [{
|
||||
text: 'Update Action/Results',
|
||||
handler: processText
|
||||
}]
|
||||
},{
|
||||
layout: 'column',
|
||||
title: 'Set Up / Break Down',
|
||||
items: [{
|
||||
columnWidth:0.5,
|
||||
layout:'fit',
|
||||
items:{
|
||||
title: 'Setup',
|
||||
height: 450,
|
||||
bodyBorder: false,
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
items:[{
|
||||
id: 'setup_editor',
|
||||
xtype:'htmleditor',
|
||||
value: '[% case.text.setup FILTER js %]'
|
||||
}]
|
||||
}
|
||||
},{
|
||||
columnWidth:0.5,
|
||||
layout:'fit',
|
||||
items:{
|
||||
title: 'Breakdown',
|
||||
height: 450,
|
||||
bodyBorder: false,
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
items:[{
|
||||
id: 'breakdown_editor',
|
||||
xtype:'htmleditor',
|
||||
value: '[% case.text.breakdown FILTER js %]'
|
||||
}]
|
||||
}
|
||||
}],
|
||||
buttons: [{
|
||||
text: 'Update Setup/Breakdown',
|
||||
handler: processText
|
||||
}]
|
||||
},
|
||||
new HistoryGrid('case', [% case.id FILTER none %]),
|
||||
new CaseRunListGrid({case_id: tcase.case_id, viewall: 1}),
|
||||
new AttachGrid(tcase),
|
||||
new CaseBugsGrid([% case.id FILTER none %]),
|
||||
new CaseComponentsGrid(tcase.case_id),
|
||||
{
|
||||
title: 'Dependencies',
|
||||
listeners: {'activate': function(){ this.doLayout(); }},
|
||||
buttons: [{
|
||||
text: 'Submit',
|
||||
disabled: tcase.canedit ? false : true,
|
||||
handler: function(){
|
||||
var params = {};
|
||||
params.tcdependson = Ext.getCmp('tcdependson').getValue();
|
||||
params.tcblocks = Ext.getCmp('tcblocks').getValue();
|
||||
params.action = 'edit';
|
||||
params.case_id = tcase.case_id;
|
||||
testopia_form.submit({
|
||||
url: 'tr_process_case.cgi',
|
||||
params: params,
|
||||
success: function(f,a){
|
||||
var blocked = '';
|
||||
var blist = a.result.tcase.blocked.split(',');
|
||||
for (var i in blist){
|
||||
if (typeof blist[i] != "string"){
|
||||
continue;
|
||||
}
|
||||
blocked = blocked +'<a href="tr_show_case.cgi?case_id=' + blist[i] + '">' + blist[i] + '</a>';
|
||||
if (i != blist.length - 1){
|
||||
blocked = blocked + ', ';
|
||||
}
|
||||
}
|
||||
var deps = '';
|
||||
var dlist = a.result.tcase.dependson.split(',');
|
||||
for (var i in dlist){
|
||||
if (typeof dlist[i] != "string"){
|
||||
continue;
|
||||
}
|
||||
deps = deps +'<a href="tr_show_case.cgi?case_id=' + dlist[i] + '">' + dlist[i] + '</a>, ';
|
||||
if (i != dlist.length - 1){
|
||||
deps = deps + ', ';
|
||||
}
|
||||
}
|
||||
Ext.getCmp('case_blocked_list').body.dom.innerHTML = blocked;
|
||||
Ext.getCmp('case_dependson_list').body.dom.innerHTML = deps;
|
||||
TestopiaUtil.notify.msg('Test Case updated', 'Test Case {0} was updated successfully', 'dependencies');
|
||||
},
|
||||
failure: testopiaError
|
||||
});
|
||||
}
|
||||
}],
|
||||
items: [{
|
||||
layout: 'column',
|
||||
items: [{
|
||||
columnWidth: 0.5,
|
||||
layout: 'fit',
|
||||
border: false,
|
||||
items: [{
|
||||
xtype: 'treepanel',
|
||||
border: false,
|
||||
root: new Ext.tree.TreeNode({text: 'Test Case [% case.id FILTER none %]'})
|
||||
}]
|
||||
|
||||
},{
|
||||
columnWidth: 0.5,
|
||||
layout: 'fit',
|
||||
border: false,
|
||||
items: [{
|
||||
layout: 'table',
|
||||
height: 450,
|
||||
border: false,
|
||||
layoutConfig: { columns: 2 },
|
||||
items: [{
|
||||
html: '<b>Depends On</b>'
|
||||
},{
|
||||
html: '<b>Blocks</b>'
|
||||
},{
|
||||
xtype: 'textarea',
|
||||
name: 'tcblocks',
|
||||
id: 'tcblocks',
|
||||
value: '[% case.blocked_list FILTER html %]',
|
||||
width: 250
|
||||
},{
|
||||
xtype: 'textarea',
|
||||
name: 'tcdependson',
|
||||
id: 'tcdependson',
|
||||
value: '[% case.dependson_list FILTER html %]',
|
||||
width: 250
|
||||
},{
|
||||
border: false,
|
||||
height: 200,
|
||||
id: 'case_blocked_list',
|
||||
autoScroll: true,
|
||||
html:'[% FOREACH dep = case.blocked %]<a href="tr_show_case.cgi?case_id=[% dep.id FILTER none %]" title="[% dep.summary FILTER js %]">[% dep.id FILTER js %]</a>[% ', ' IF NOT loop.last%] [% END %]'
|
||||
},{
|
||||
border: false,
|
||||
height: 200,
|
||||
id: 'case_dependson_list',
|
||||
autoScroll: true,
|
||||
html:'[% FOREACH dep = case.dependson %]<a href="tr_show_case.cgi?case_id=[% dep.id FILTER none %]" title="[% dep.summary FILTER js %]">[% dep.id FILTER js %]</a>[% ', ' IF NOT loop.last%] [% END %]'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
},{
|
||||
title: "Automation",
|
||||
xtype: 'form',
|
||||
id: 'case_automation_frm',
|
||||
bodyStyle: 'padding: 10px',
|
||||
listeners: {'activate': function(){ this.doLayout(); }},
|
||||
buttons: [{
|
||||
text: 'Submit',
|
||||
disabled: tcase.canedit ? false : true,
|
||||
handler: function(){
|
||||
var params = Ext.getCmp('case_automation_frm').getForm().getValues();
|
||||
params.case_id = tcase.case_id;
|
||||
params.action = 'edit';
|
||||
params.isautomated = params['ncf-automated-checkbox'] == 'on' ? 'on' : 'off';
|
||||
testopia_form.submit({
|
||||
url: 'tr_process_case.cgi',
|
||||
params: params,
|
||||
success: function(){
|
||||
TestopiaUtil.notify.msg('Test case updated', 'Test Case {0} was updated successfully', 'automation');
|
||||
},
|
||||
failure: testopiaError
|
||||
});
|
||||
}
|
||||
}],
|
||||
items: [{
|
||||
xtype: 'fieldset',
|
||||
autoHeight: true,
|
||||
checkboxToggle: true,
|
||||
title: 'Automated',
|
||||
id: 'ncf-automated',
|
||||
checkBoxName: 'isautomated',
|
||||
collapsed: tcase.isautomated == 1 ? false : true,
|
||||
items: [{
|
||||
xtype:'textfield',
|
||||
fieldLabel: 'Scripts',
|
||||
id:'ncf-scripts',
|
||||
name: 'script',
|
||||
value: tcase.script
|
||||
},{
|
||||
xtype:'textfield',
|
||||
fieldLabel: 'Arguments',
|
||||
id:'ncf-arguments',
|
||||
name: 'arguments',
|
||||
value: tcase.arguments
|
||||
},{
|
||||
xtype:'textfield',
|
||||
fieldLabel: 'Alias',
|
||||
id:'case_alias',
|
||||
name: 'alias',
|
||||
value: tcase.alias
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
},{
|
||||
xtype: 'panel',
|
||||
split: true,
|
||||
title: '[% title FILTER js %]',
|
||||
region: 'north',
|
||||
contentEl: 'case_overview',
|
||||
height: 245,
|
||||
hideBorders: true
|
||||
},{
|
||||
xtype: 'tabpanel',
|
||||
split: true,
|
||||
region:'east',
|
||||
width: 200,
|
||||
activeTab: 0,
|
||||
items:[
|
||||
new TestopiaObjectTags('case', [% case.id FILTER none %]),
|
||||
new CasePlans([% case.id FILTER none %], [% case.plans.0.product_id FILTER none %])
|
||||
]
|
||||
}]
|
||||
});
|
||||
[% IF case.canedit %]
|
||||
// Summary Editing
|
||||
var summary_edit_btn = new Ext.Button({
|
||||
applyTo: 'case_summary_edit_btn_div',
|
||||
template: button_16x_tmpl,
|
||||
id:'case_summary_edit_btn',
|
||||
icon: 'testopia/img/edit.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Edit Case Summary',
|
||||
handler: function(){
|
||||
Ext.getCmp('case_summary_edt').startEdit(Ext.get('case_summary_div'));
|
||||
}
|
||||
});
|
||||
var case_summary_edt = new Ext.Editor(
|
||||
new Ext.form.TextField({
|
||||
width: Ext.get('case_summary_div').getWidth() < 200 ? 200 : Ext.get('case_summary_div').getWidth(),
|
||||
name: 'summary'
|
||||
}),{
|
||||
alignment: 'tl-tl',
|
||||
id: 'case_summary_edt',
|
||||
ignoreNoChange: true,
|
||||
updateEl: true,
|
||||
completeOnEnter: true,
|
||||
ignoreNoChange: true,
|
||||
cancelOnEsc: true
|
||||
});
|
||||
case_summary_edt.on('complete', function(e,v,sv){
|
||||
submitEdits('summary', v);
|
||||
});
|
||||
|
||||
// Tester Editing
|
||||
var tester_edit_btn = new Ext.Button({
|
||||
applyTo: 'tester_edit_btn_div',
|
||||
template: button_16x_tmpl,
|
||||
id:'tester_edit_btn',
|
||||
icon: 'testopia/img/edit.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Edit Default Tester',
|
||||
handler: function(){
|
||||
Ext.getCmp('tester_edt').startEdit(Ext.get('tester_div'), '[% case.default_tester.name FILTER js %]');
|
||||
}
|
||||
});
|
||||
var tester_edt = new Ext.Editor(
|
||||
new UserLookup({
|
||||
hiddenName: 'tester',
|
||||
value: tcase.tester
|
||||
}),
|
||||
{
|
||||
alignment: 'tl-tl',
|
||||
id: 'tester_edt',
|
||||
updateEl: true,
|
||||
completeOnEnter: true,
|
||||
ignoreNoChange: true,
|
||||
cancelOnEsc: true
|
||||
});
|
||||
tester_edt.on('complete', function(e,v){
|
||||
submitEdits('tester', v);
|
||||
});
|
||||
|
||||
// Requirement Editing
|
||||
var requirement_edit_btn = new Ext.Button({
|
||||
applyTo: 'requirement_edit_btn_div',
|
||||
template: button_16x_tmpl,
|
||||
id:'requirment_edit_btn',
|
||||
icon: 'testopia/img/edit.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Edit Requirement',
|
||||
handler: function(){
|
||||
Ext.getCmp('requirement_edt').startEdit(Ext.get('requirement_div'));
|
||||
}
|
||||
});
|
||||
var requirement_edt = new Ext.Editor(
|
||||
new Ext.form.TextField({
|
||||
width: 300,
|
||||
name: 'requirement'
|
||||
}),
|
||||
{
|
||||
alignment: 'tl-tl',
|
||||
id: 'requirement_edt',
|
||||
ignoreNoChange: true,
|
||||
updateEl: true,
|
||||
completeOnEnter: true,
|
||||
ignoreNoChange: true,
|
||||
cancelOnEsc: true
|
||||
}
|
||||
);
|
||||
requirement_edt.on('complete', function(e,v,sv){
|
||||
submitEdits('requirement', v);
|
||||
});
|
||||
|
||||
// Time Editing
|
||||
var time_edit_btn = new Ext.Button({
|
||||
applyTo: 'time_edit_btn_div',
|
||||
template: button_16x_tmpl,
|
||||
id:'time_edit_btn',
|
||||
icon: 'testopia/img/edit.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Edit Estimated Time',
|
||||
handler: function(){
|
||||
Ext.getCmp('time_edt').startEdit(Ext.get('estimated_time_div'));
|
||||
}
|
||||
});
|
||||
var time_edt = new Ext.Editor(
|
||||
new Ext.form.TextField({
|
||||
width: 300,
|
||||
name: 'estimated_time'
|
||||
}),{
|
||||
alignment: 'tl-tl',
|
||||
id: 'time_edt',
|
||||
ignoreNoChange: true,
|
||||
updateEl: true,
|
||||
completeOnEnter: true,
|
||||
ignoreNoChange: true,
|
||||
cancelOnEsc: true
|
||||
});
|
||||
time_edt.on('complete', function(e,v,sv){
|
||||
submitEdits('estimated_time', v);
|
||||
});
|
||||
|
||||
// Combo Boxes
|
||||
var pbox = new PriorityCombo({
|
||||
hiddenName: 'priority',
|
||||
transform: 'priority'
|
||||
});
|
||||
pbox.on('select', function(c,r,i){
|
||||
submitEdits('priority', r.get('value'));
|
||||
});
|
||||
|
||||
var sbox = new CaseStatusCombo({
|
||||
hiddenName: 'status',
|
||||
transform: 'status'
|
||||
});
|
||||
sbox.on('select', function(c,r,i){
|
||||
submitEdits('status', r.get('value'));
|
||||
});
|
||||
|
||||
var cbox = new CaseCategoryCombo({
|
||||
hiddenName: 'category',
|
||||
transform: 'category'
|
||||
});
|
||||
cbox.on('select', function(c,r,i){
|
||||
submitEdits('category', r.get('value'));
|
||||
});
|
||||
[% END %]
|
||||
});
|
||||
</script>
|
||||
|
||||
[% PROCESS testopia/case/navigate.html.tmpl %]
|
||||
|
||||
<div id="case-body-div"></div>
|
||||
|
||||
<div id="case_overview">
|
||||
<div id="case-tb-div"></div>
|
||||
<table width="100%">
|
||||
<tbody align="left">
|
||||
<tr class="bz_row_header">
|
||||
<th colspan="3">Case Summary</th>
|
||||
</tr>
|
||||
<tr class="bz_row_data">
|
||||
<td colspan="3">
|
||||
<table>
|
||||
<tr style="width:100%;">
|
||||
<td><div id="case_summary_div">[% case.summary FILTER html %]</div></td>
|
||||
<td><div id="case_summary_edit_btn_div"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="bz_row_header">
|
||||
<th>Author</th>
|
||||
<th>Requirement</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
<tr class="bz_row_data">
|
||||
<td><a href="mailto:[% case.author.email FILTER html %]">[% case.author.identity FILTER html %]</a><br></td>
|
||||
<td>
|
||||
<table>
|
||||
<tr style="width:100%;">
|
||||
<td><div id="requirement_div">[% case.requirement FILTER html %]</div></td>
|
||||
<td><div id="requirement_edit_btn_div"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>[% case.creation_date FILTER time %]</td>
|
||||
</tr>
|
||||
<tr class="bz_row_header">
|
||||
<th>Tester</th>
|
||||
<td><table><tr><th width="200">Estimated Time</th><td width="20"></td><th>Average Time</th></tr></table></td>
|
||||
<th>Automated</th>
|
||||
</tr>
|
||||
<tr class="bz_row_data">
|
||||
<td>
|
||||
<table>
|
||||
<tr><td><a href="mailto:[% case.default_tester.email FILTER html %]" id="tester_div">[% case.default_tester.identity FILTER html %]</a></td>
|
||||
<td><div id="tester_edit_btn_div"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table>
|
||||
<tr><td width="200"><div id="estimated_time_div">[% case.estimated_time FILTER html %] (H:M:S)</div></td>
|
||||
<td><div id="time_edit_btn_div"></div></td>
|
||||
<td>[% case.calculate_average_time FILTER none %] (H:M:S)</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>[% case.isautomated ? "YES" : "NO" FILTER html %]</td>
|
||||
</tr>
|
||||
<tr class="bz_row_header">
|
||||
<th>Category</th>
|
||||
<th>Priority</th>
|
||||
<th>Status</th>
|
||||
|
||||
</tr>
|
||||
<tr class="bz_row_data">
|
||||
<td>[% IF case.canedit %]
|
||||
[% PROCESS select sel = { name => 'category',
|
||||
accesskey => 'c',
|
||||
list => case.get_category_list
|
||||
default => case.category.name } %]
|
||||
[% ELSE %]
|
||||
[% case.category.name FILTER html %]
|
||||
[% END %]
|
||||
</td>
|
||||
<td>
|
||||
<table><tr><td>
|
||||
[% IF case.canedit %]
|
||||
[% PROCESS select sel = { name => 'priority',
|
||||
accesskey => 'p',
|
||||
list => case.get_priority_list
|
||||
default => case.priority } %]
|
||||
[% ELSE %]
|
||||
[% case.priority FILTER html %]
|
||||
[% END %]
|
||||
</td><td> <img src='images/help.png' id="priority_help" style="cursor:pointer" onclick='window.open("testing_priorities.html","Priority Definitions","resizable=no, scrollbars=yes, width=550,height=420");'/></td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>[% IF case.canedit %]
|
||||
[% PROCESS select sel = { name => 'status',
|
||||
accesskey => 's',
|
||||
list => case.get_status_list
|
||||
default => case.status } %]
|
||||
[% ELSE %]
|
||||
[% case.status FILTER html %]
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,97 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006 Novell.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Case [% case.id FILTER html %] - [% case.summary FILTER html %]</title>
|
||||
<link href="testopia/css/print.css" rel="stylesheet" type="text/css"/>
|
||||
</head>
|
||||
</html>
|
||||
<h1>Test Case [% case.id FILTER html %] - [% case.summary FILTER html %]</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="print_table_bold">Author:</td>
|
||||
<td class="print_table_body">[% case.author.identity FILTER html %]</td>
|
||||
<td class="print_table_bold">Created:</td>
|
||||
<td class="print_table_body">[% case.creation_date FILTER time %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="print_table_bold">Default Tester:</td>
|
||||
<td class="print_table_body">[% case.default_tester.identity FILTER html %]</td>
|
||||
<td class="print_table_bold">Priority:</td>
|
||||
<td class="print_table_body">[% case.priority FILTER html %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="print_table_bold">Requirement:</td>
|
||||
<td class="print_table_body">[% case.requirement FILTER html %]</td>
|
||||
<td class="print_table_bold">Case Text Version:</td>
|
||||
<td class="print_table_body">[% case.version FILTER html %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="print_table_bold">Status:</td>
|
||||
<td class="print_table_body">[% case.status FILTER html %]</td>
|
||||
<td class="print_table_bold">Category:</td>
|
||||
<td class="print_table_body">[% case.category.name FILTER html %]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Components</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[% FOREACH component = case.components %]
|
||||
[% component.name FILTER html %]</br>
|
||||
[% END %]
|
||||
</td>
|
||||
<td>
|
||||
[% FOREACH tag = case.tags %]
|
||||
[% tag.tag_name FILTER html %]</br>
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="3" >[% terms.Bugs %]</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ID</th><th>Summary</th>
|
||||
</tr>
|
||||
[% FOREACH bug = case.bugs %]
|
||||
<tr>
|
||||
<td>[% bug.bug_id FILTER bug_link(bug.bug_id) %] </td>
|
||||
<td>[% bug.short_desc FILTER html %]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
<hr>
|
||||
<h3>Statistics</h3>
|
||||
<img src="tr_case_reports.cgi?case_id=[% case.id FILTER none %]&type=status-breakdown">
|
||||
<hr>
|
||||
<h3>Setup</h3>
|
||||
[% case.text.setup FILTER html_light %]
|
||||
<h3>Action</h3>
|
||||
[% case.text.action FILTER html_light %]
|
||||
<h3>Expected Results</h3>
|
||||
[% case.text.effect FILTER html_light %]
|
||||
<h3>Breakdown</h3>
|
||||
[% case.text.breakdown FILTER html_light %]
|
|
@ -0,0 +1,26 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): David Koenig <dkoenig@novell.com>
|
||||
#%]
|
||||
|
||||
[%# Testopia Show Test Case XML export template #%]
|
||||
|
||||
[% PROCESS testopia/export/xml.header.tmpl %]
|
||||
[%+ PROCESS testopia/export/xml.case.tmpl %]
|
||||
|
||||
[% PROCESS testopia/export/xml.footer.tmpl %]
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<cases>
|
||||
<casetext>
|
||||
<setup><![CDATA[[% text.setup FILTER none %]]]></setup>
|
||||
<breakdown><![CDATA[[% text.breakdown FILTER none %]]]></breakdown>
|
||||
<version>[% text.version FILTER xml %]</version>
|
||||
<action><![CDATA[[% text.action FILTER none %]]]></action>
|
||||
<effect><![CDATA[[% text.effect FILTER none %]]]></effect>
|
||||
<case_id>[% text.case_id FILTER xml %]</case_id>
|
||||
<author>[% text.author FILTER xml %]</author>
|
||||
<notes>[% text.notes FILTER xml %]</notes>
|
||||
<summary><![CDATA[[% text.summary FILTER none %]]]></summary>
|
||||
</casetext>
|
||||
</cases>
|
|
@ -0,0 +1,132 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006 Novell.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
<div id="caserun-filter-div">
|
||||
<form method="GET" action="tr_list_caseruns.cgi" id="caserun_filter_form">
|
||||
<input type="hidden" name="run_id" value="[% run.id FILTER none %]" />
|
||||
<input type="hidden" name="current_tab" value="case_run" />
|
||||
[% query = table.get_query_part %]
|
||||
<table>
|
||||
<tr class="bz_row_header">
|
||||
<th>Status</th>
|
||||
<th>Category</th>
|
||||
<th>Build</th>
|
||||
<th>Priority</th>
|
||||
<th>Component</th>
|
||||
<th>Automatic</th>
|
||||
<td rowspan="2">
|
||||
<table>
|
||||
<tr>
|
||||
<th align="right">Assignee Contains</th>
|
||||
<td><input type="hidden" name="assignee_type" value="substring" />
|
||||
<input name="assignee">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Tested By Contains</th>
|
||||
<td><input type="hidden" name="testedby_type" value="substring" />
|
||||
<input name="testedby">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Summary Contains</th>
|
||||
<td><input type="hidden" name="case_summary_type" value="allwordssubstr" />
|
||||
<input name="case_summary">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Requirement</th>
|
||||
<td><input type="hidden" name="requirement_type" value="substring" />
|
||||
<input name="requirement">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Environment</th>
|
||||
<td><input type="hidden" name="environment_name_type" value="substring" />
|
||||
<input name="environment_name">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Case Tags</th>
|
||||
<td><input type="hidden" name="tags_type" value="anyexact" />
|
||||
<input id="tags" name="tags"
|
||||
dojoType="combobox"
|
||||
dataUrl="tr_quicksearch.cgi?action=gettag&product_id=[% run.plan.product_id FILTER none %]&search=${searchString}"
|
||||
mode="remote"
|
||||
searchDelay="1000"
|
||||
maxListLength="10">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => "case_run_status",
|
||||
byname => 1,
|
||||
list => caserun.get_status_list,
|
||||
elements => 6,
|
||||
mult => 1, }
|
||||
%]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => "category",
|
||||
list => run.filter_case_categories,
|
||||
byname => 1,
|
||||
elements => 6,
|
||||
mult => 1, }
|
||||
|
||||
%]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => "build",
|
||||
list => run.filter_builds,
|
||||
byname => 1,
|
||||
elements => 6,
|
||||
mult => 1, }
|
||||
%]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => "priority_id",
|
||||
list => case.get_priority_list,
|
||||
elements => 6,
|
||||
mult => 1, }
|
||||
%]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => "component",
|
||||
list => run.filter_components,
|
||||
byname => 1,
|
||||
elements => 6,
|
||||
mult => 1, }
|
||||
%]
|
||||
</td>
|
||||
<td valign="top">
|
||||
[% PROCESS select sel = { name => 'isautomated',
|
||||
accesskey => 'a',
|
||||
list =>
|
||||
[ { id => "0", name => "Manual" },
|
||||
{ id => "1", name => "Automatic" } ]
|
||||
elements => 5,
|
||||
mult => 1 } %]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,104 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Ed Fuentetaja <efuentetaja@acm.org>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/Portal.js") %]
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/PortalColumn.js") %]
|
||||
[% javascript_urls.push("testopia/extjs/examples/portal/Portlet.js") %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl %]
|
||||
|
||||
<script type="text/javascript">
|
||||
Ext.onReady(function(){
|
||||
Ext.QuickTips.init();
|
||||
var params = searchToJson(window.location.search);
|
||||
var list_grid = new CaseRunListGrid(params, {});
|
||||
var objPanel = new Ext.Panel({
|
||||
layout: 'border',
|
||||
height: 650,
|
||||
monitorResize: true,
|
||||
split: true,
|
||||
id:'list_view',
|
||||
applyTo: 'list_div',
|
||||
items: [{
|
||||
id: 'object_panel',
|
||||
region: 'center',
|
||||
xtype: 'tabpanel',
|
||||
enableTabScroll: true,
|
||||
activeTab: 0,
|
||||
items: [list_grid, new DashboardPanel({})]
|
||||
},{
|
||||
region: 'east',
|
||||
width: 250,
|
||||
split: true,
|
||||
enableTabScroll: true,
|
||||
activeTab: 2,
|
||||
xtype: 'tabpanel',
|
||||
items: [
|
||||
new ReportGrid({
|
||||
title: 'Dashboards',
|
||||
id: 'dashboard_grid',
|
||||
type: 3
|
||||
}),
|
||||
new ReportGrid({
|
||||
title: 'Reports',
|
||||
id: 'reports_grid',
|
||||
type: 1
|
||||
}),
|
||||
new ReportGrid({
|
||||
title: 'Searches',
|
||||
id: 'searches_grid',
|
||||
type: 0
|
||||
})
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
list_grid.store.baseParams = params;
|
||||
list_grid.store.baseParams.isactive = 0;
|
||||
list_grid.store.baseParams.ctype ='json';
|
||||
|
||||
list_grid.store.load();
|
||||
|
||||
Ext.getCmp('dashboardpanel').on('render', function(){
|
||||
Ext.getCmp('dashboardpanel').getTopToolbar().add({
|
||||
xtype: 'button',
|
||||
id: 'save_dashboard_btn',
|
||||
icon: 'testopia/img/save.png',
|
||||
iconCls: 'img_button_16x',
|
||||
tooltip: 'Save this dashboard',
|
||||
handler: function(b,e){
|
||||
saveSearch('dashboard', Testopia.Search.dashboard_urls);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<div id="list_div" style="height:650px"></div>
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,21 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%- json -%]
|
|
@ -0,0 +1,87 @@
|
|||
[%# 1.0@bugzilla.org %][%# 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>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Create New Test Run Environment" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/messages.html.tmpl %]
|
||||
|
||||
<form method="POST" action="tr_new_environment.cgi" name="form1">
|
||||
<input type="hidden" name="action" value="Add" />
|
||||
[%############################################################################%]
|
||||
[%# New Environment #%]
|
||||
[%############################################################################%]
|
||||
<h3>New Environment</h3>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<th class="bz_row_header" align="right">Name:</th>
|
||||
<td ><input name="name" size="30" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="bz_row_header" align="right">Product:</th>
|
||||
<td>[% PROCESS select sel = {
|
||||
name => 'product',
|
||||
accesskey => 'p',
|
||||
list => products,
|
||||
} %]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<input name="submit" type="submit" value="Create">
|
||||
<br />
|
||||
<br />
|
||||
</form>
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Upload XML #%]
|
||||
[%############################################################################%]
|
||||
<form action="tr_import_environment.cgi" method="POST">
|
||||
<h3>OR Upload XML</h3>
|
||||
<input type="file" size="40" accept="text/xml" name="xml"></input>
|
||||
|
||||
<br />
|
||||
|
||||
<a href="testopia/doc/Manual.pdf" target="_blank">Help</a><br>
|
||||
<p>
|
||||
<input type="submit" name="submit" value="Upload">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,39 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
# Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
||||
title = "Search by Test Run Environment Number"
|
||||
%]
|
||||
|
||||
<form method="get" action="tr_environments.cgi">
|
||||
<p>
|
||||
You may find a test environment by entering its id here:
|
||||
<input name="env_id" size="6">
|
||||
<input type="submit" value="Show Me This Test Environment">
|
||||
</p>
|
||||
Or you can search for environments <a href="tr_query.cgi?current_tab=environment">Here</a>.
|
||||
</form>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,61 @@
|
|||
[%# 1.0@bugzilla.org %][%# 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>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Delete Environment $environment.name" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% IF deleted %]
|
||||
Environment Deleted
|
||||
[% ELSE %]
|
||||
<p>You are about to permanantly delete this environment.</p>
|
||||
|
||||
[% IF environment.get_run_count OR environment.case_run_count %]
|
||||
|
||||
<b>This environment is being used by <a href="tr_list_runs.cgi?environment_id=[% environment.id FILTER none %]">[% environment.get_run_count FILTER none %]
|
||||
test runs</a> and <a href="tr_list_caseruns.cgi?environment_id=[% environment.id FILTER none %]">[% environment.case_run_count FILTER none %] case-runs</a>.
|
||||
|
||||
These will also be deleted.</b>
|
||||
[% END %]
|
||||
<p><span style="font-size:12pt; font-weight:bold; color:#cc0000;">Warning: This action cannot be undone</span></p>
|
||||
|
||||
<br>
|
||||
<form method="POST" action="tr_environments.cgi">
|
||||
<input type="hidden" name="action" value="do_delete" />
|
||||
<input type="hidden" name="env_id" value="[% environment.id FILTER none %]" />
|
||||
<input type="submit" value="Continue" />
|
||||
</form>
|
||||
<a href="tr_environments.cgi?env_id=[% environment.id FILTER none %]">Back to Envrionment - [% environment.name FILTER html %]</a>
|
||||
[% END %]
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,46 @@
|
|||
[%# 1.0@bugzilla.org %][%# 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): Garrett Braden <gbraden@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Export Environment XML" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/messages.html.tmpl %]
|
||||
|
||||
<h3><font color="red">Environment XML</font></h3>
|
||||
<textarea cols=40 rows=6 name="xml">[%- xml %]</textarea>
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,96 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
|
||||
<div style="background-color:#CCCCFF;padding:5px; width:600px">
|
||||
<table>
|
||||
<tr>
|
||||
<td width="70%" valign="top">
|
||||
This is the environment editor.
|
||||
To add elements to your environment drag them from the available items on the tree to your right and
|
||||
drop them on the environment tree on your left. Once you have chosen your elements, be sure to select
|
||||
values for each of the properties.
|
||||
</td>
|
||||
<td align="right">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="2">Legend</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="testopia/img/square.png"></td><td>Environment Category</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="testopia/img/circle.png"></td><td>Environment Element</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="testopia/img/triangle.png"></td><td>Element Property</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="testopia/img/selected_value.png" width="12" height="12"></td><td>Selected Property Value</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td><input name="env_name" id="env_name" value="[% environment.name FILTER html %]" size="40"></td>
|
||||
<td>
|
||||
[% IF user.in_group('Testers') %]
|
||||
<a href="tr_new_environment.cgi">Create a New Environment</a></td>
|
||||
[% END %]
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<td>
|
||||
[% IF environment.canedit %]
|
||||
[% PROCESS select sel = {
|
||||
name => 'product_id',
|
||||
list => user.get_selectable_products,
|
||||
default => environment.product_id,
|
||||
accesskey => 'p'} %]
|
||||
[% ELSE %]
|
||||
[% environment.product.name FILTER html %]
|
||||
[% END %]
|
||||
</td>
|
||||
<td>
|
||||
[% IF user.in_group('Testers') %]
|
||||
<a href="tr_admin_environment.cgi">Edit Environment Variables</a></td>
|
||||
[% END %]
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Archive</th><td><input type="checkbox" style="border: none" name="isactive" id="isactive" value="1" [% 'checked="checked"' IF NOT environment.isactive %]></td>
|
||||
[% IF environment.candelete %]
|
||||
<td><a href="tr_environments.cgi?env_id=[% environment.id FILTER none %]&action=delete">Delete this Environment</a></td>
|
||||
[% END %]
|
||||
</tr>
|
||||
[% IF environment.canedit %]
|
||||
<tr>
|
||||
<td colspan=2" align="right"><input type="button" onclick="update()" value="Commit"></td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
|
@ -0,0 +1,58 @@
|
|||
[%# 1.0@bugzilla.org %][%# 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): Garrett Braden <gbraden@novell.com>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Import XML Environment" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/messages.html.tmpl %]
|
||||
|
||||
[% IF action == "admin" %]
|
||||
<form action="tr_import_environment.cgi?action=admin" method="post">
|
||||
<h3><font color="red">Add the above new data?</font></h3>
|
||||
<input type="hidden" name="action" value="admin" />
|
||||
<input type="hidden" name="xml" value="[%- xml FILTER xml %]" />
|
||||
<input type="submit" name="submit" value="Add Now" /> <input type="submit" name="submit" value="Cancel" />
|
||||
</form>
|
||||
[% ELSE %]
|
||||
<form action="tr_import_environment.cgi?action=import" enctype="multipart/form-data" method="post">
|
||||
<p>Upload XML Environment: <input type="file" name="env_file" size="40" /></p>
|
||||
<input type="hidden" name="action" value="import" />
|
||||
<p><input type="submit" value="Import" /></p>
|
||||
</form>
|
||||
[% END %]
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,50 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# ...
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Template Initialization #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% title = "Test Environments" %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Page Header #%]
|
||||
[%############################################################################%]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
%]
|
||||
|
||||
[% PROCESS testopia/style.none.tmpl %]
|
||||
[% PROCESS testopia/blocks.html.tmpl %]
|
||||
[% PROCESS testopia/messages.html.tmpl %]
|
||||
|
||||
[% IF table.list.size > 0 %]
|
||||
[% PROCESS testopia/environment/table.html.tmpl %]
|
||||
[% ELSE %]
|
||||
No environments found.
|
||||
[% END %]
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
|
@ -0,0 +1,21 @@
|
|||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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 Testopia System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Greg Hendricks.
|
||||
# Portions created by Greg Hendricks are Copyright (C) 2006
|
||||
# Novell. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
||||
#%]
|
||||
|
||||
[%- json -%]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue