diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Attachment.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Attachment.pm index 0482513358e..975563bb0b4 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Attachment.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Attachment.pm @@ -163,7 +163,7 @@ sub store { $self->{'filename'}, $timestamp, $self->{'mime_type'}); my $key = $dbh->bz_last_key( 'test_attachments', 'attachment_id' ); - $dbh->do("INSERT INTO test_attachment_data VALUES(?,?)", + $dbh->do("INSERT INTO test_attachment_data (attachment_id, contents) VALUES(?,?)", undef, $key, $self->{'contents'}); return $key; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Classification.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Classification.pm new file mode 100644 index 00000000000..2053f96be52 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Classification.pm @@ -0,0 +1,85 @@ +# -*- 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 + +package Bugzilla::Testopia::Classification; + +use strict; + +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 (Param('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) = @_; + + $disable_move ||= ''; + $disable_move = ',"addChild","move"' if $disable_move; + my $products = $self->user_visible_products; + my $json = "["; + foreach my $obj (@{$products}){ + $json .= '{title:"' . $obj->name . '",'; + $json .= 'isFolder:' . (scalar @{$obj->environment_categories} > 0 ? "true" : "false") . ','; + $json .= 'objectId:"' . $obj->id . '",'; + $json .= 'widgetId:"product' . $obj->id . '",'; + $json .= 'actionsDisabled:["addElement","addProperty","addValue"'. $disable_move .'],'; + $json .= 'childIconSrc:"testopia/img/folder_red.gif"},'; + } + chop $json; + $json .= "]"; + return $json; +} +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment.pm index 60ccc492411..fd77a9cd113 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment.pm @@ -16,7 +16,9 @@ # Portions created by Maciej Maczynski are Copyright (C) 2001 # Maciej Maczynski. All Rights Reserved. # -# Contributor(s): Greg Hendricks +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden =head1 NAME @@ -26,11 +28,12 @@ Bugzilla::Testopia::Environment - A test environment Environments are a set of parameters dictating what conditions a test was conducted in. Each test run must have an environment. -Environments can be very simple or very complex. Since there is -no easy way of defining a limit for an environment, the bulk of -the information is captured as an XML document. At the very least -however, and environment consists of the OS and platform from -Bugzilla. +Environments can be very simple or very complex. + +Environments are comprised of Elemements, Properties, and Values. +Elements can be nested within other elements. Each element can have +zero or more properties. Each property can only have one value selected +of the possible values. =head1 SYNOPSIS @@ -54,23 +57,23 @@ use Bugzilla::User; =head1 FIELDS environment_id - op_sys_id - rep_platform_id + product_id name - xml + isactive =cut use constant DB_COLUMNS => qw( - test_environments.environment_id - test_environments.op_sys_id - test_environments.rep_platform_id - test_environments.name - test_environments.xml + environment_id + product_id + name + isactive ); our $columns = join(", ", DB_COLUMNS); +our constant $max_depth = 7; + ############################### #### Methods #### ############################### @@ -101,16 +104,18 @@ sub _init { my $self = shift; my ($param) = (@_); my $dbh = Bugzilla->dbh; - - my $id = $param unless (ref $param eq 'HASH'); + + my $id = $param unless (ref $param eq 'HASH' || ref $param eq 'Bugzilla::Testopia::Environment::Xml'); my $obj; - + if (defined $id && detaint_natural($id)) { $obj = $dbh->selectrow_hashref(qq{ - SELECT $columns FROM test_environments - WHERE environment_id = ?}, undef, $id); - } elsif (ref $param eq 'HASH'){ + SELECT $columns + FROM test_environments + WHERE environment_id = ?}, undef, ($id)); + + } elsif (ref $param eq 'HASH' || ref $param eq 'Bugzilla::Testopia::Environment::Xml'){ $obj = $param; } @@ -119,41 +124,263 @@ sub _init { foreach my $field (keys %$obj) { $self->{$field} = $obj->{$field}; } + + if(! ref $obj eq 'Bugzilla::Testopia::Environment::Xml'){ + + $self->get_environment_elements(); + my @elements = $self->{'elements'}; + + foreach my $elem (@elements) + { + foreach my $element (@$elem) + { + + my $elem_id = $element->{'element_id'}; + my @properties = $element->{'properties'}; + + foreach my $prop (@properties) + { + foreach my $property (@$prop) + { + my $prop_id = $property->{'property_id'}; + $property->{'value_selected'} = $self->get_value_selected($self->{'environment_id'},$elem_id,$prop_id); + } + } + } + } + } + return $self; } -=head2 get_op_sys_list -Returns the list of bugzilla op_sys entries +=head2 get element list for environment + +Returns an array of element objects for an environment =cut -sub get_op_sys_list{ - my $self = shift; +sub get_environment_elements{ my $dbh = Bugzilla->dbh; - my $ref = $dbh->selectall_arrayref( - "SELECT id, value AS name - FROM op_sys - ORDER BY sortkey", {'Slice'=>{}}); + my $self = shift; + + return $self->{'elements'} if exists $self->{'elements'}; + + my $id = $self->{'environment_id'}; + + my $ref = $dbh->selectcol_arrayref(qq{ + SELECT DISTINCT tee.element_id + FROM test_environment_map as tem + JOIN test_environment_element as tee + ON tem.element_id = tee.element_id + WHERE tem.environment_id = ?},undef,$id); + + my @elements; - return $ref; + foreach my $val (@$ref){ + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + $self->{'elements'} = \@elements; + + return \@elements; } -=head2 get_rep_platform_list +sub element_count { + my $self = shift; + + return scalar(@{$self->get_environment_elements}); +} -Returns the list of rep_platforms from bugzilla +sub elements_to_json { + my $self = shift; + + my $elements = $self->get_environment_elements; + my $json = '['; + + foreach my $element (@$elements) + { + $json .= '{title:"'. $element->{'name'} .'",'; + $json .= 'objectId:"'. $element->{'element_id'}. '",'; + $json .= 'widgetId:"element'. $element->{'element_id'} .'",'; + $json .= 'actionsDisabled:["addCategory","addValue","addChild"],'; + $json .= 'isFolder:true,' if($element->check_for_children || $element->check_for_properties); + $json .= 'childIconSrc:"testopia/img/circle.gif"},'; + } + chop $json; + $json .= ']'; + + return $json; +} + + +=head2 get_value_selected + +Returns a selected value for the specified environment element property instance. =cut -sub get_rep_platform_list{ +sub get_value_selected{ + my $dbh = Bugzilla->dbh; my $self = shift; - my $dbh = Bugzilla->dbh; - my $ref = $dbh->selectall_arrayref( - "SELECT id, value AS name - FROM rep_platform - ORDER BY sortkey", {'Slice'=>{}}); + + my ($environment,$element,$property) = (@_); + + my ($var) = $dbh->selectrow_array( + "SELECT value_selected + FROM test_environment_map + WHERE environment_id = ? + AND element_id = ? + AND property_id = ?", + undef,($environment,$element,$property)); + + return $var; +} - return $ref; +=head2 get environment names + +Returns the list of environment names and ids + +=cut + +sub get_environments{ + my $dbh = Bugzilla->dbh; + my $self = shift; + + my $ref = $dbh->selectall_arrayref( + "SELECT environment_id, name + FROM test_environments"); + + return $ref; +} + +sub get_all_env_categories { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'env_category_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name + FROM test_environment_category", + {'Slice' => {}}); + + return $ref; +} + +sub get_all_visible_elements { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'element_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name + FROM test_environment_element", + {'Slice' => {}}); + + return $ref; +} + +sub get_all_element_properties { + my $self = shift; + my ($byid) = @_; + my $dbh = Bugzilla->dbh; + my $idstr = $byid ? 'property_id' : 'DISTINCT name'; + my $ref = $dbh->selectall_arrayref( + "SELECT $idstr AS id, name, validexp + FROM test_environment_property", + {'Slice' => {}}); + + return $ref; +} + +sub get_distinct_property_values { + my $self = shift; + my @exps; + foreach my $prop (@{$self->get_all_element_properties}){ + push @exps, split(/\|/, $prop->{'validexp'}) + } + my %seen; + foreach my $v (@exps){ + $seen{$v} = $v; + } + my @values; + foreach my $v (keys %seen){ + my %p; + $p{'id'} = $v; + $p{'name'} = $v; + push @values, \%p; + } + return \@values; +} + +=head2 get all elements + +Returns the list of element names, ids and category names + +=cut + +sub get_all_elements{ + + my $dbh = Bugzilla->dbh; + my $self = shift; + + my $ref = $dbh->selectcol_arrayref( + "SELECT tee.element_id + FROM test_environment_map as tem + JOIN test_environment_element as tee + ON tem.element_id = tee.element_id", + undef); + + my @elements; + + foreach my $val (@$ref){ + push @elements, Bugzilla::Testopia::Environment::Element->new($val); + } + + return \@elements; +} + +=head2 check environment name + +Returns environment id if environment exists + +=cut + +sub check_environment{ + my $self = shift; + my ($name, $product_id) = (@_); + + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environments + WHERE name = ? AND product_id = ?", + undef, ($name, $product_id)); + + return $used; +} + + +=head2 Check Environment Element Property Value Selected + +Returns environment id if Environment Element Property Value Selected exists + +=cut + +sub check_value_selected { + my $self = shift; + my ($prop_id, $elem_id) = @_; + my $dbh = Bugzilla->dbh; + + my ($used) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environment_map + WHERE environment_id = ? + AND property_id = ? + AND element_id = ?", + undef, ($self->{'environment_id'}, $prop_id, $elem_id)); + + return $used; } =head2 store @@ -165,17 +392,64 @@ Serializes this environment to the database sub store { my $self = shift; my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); - # Verify name is available - return 0 if $self->check_name($self->{'name'}); - + + #Verify Environment isn't already in use. + return undef if $self->check_environment($self->{'name'}, $self->{'product_id'}); + my $dbh = Bugzilla->dbh; $dbh->do("INSERT INTO test_environments ($columns) - VALUES (?,?,?,?,?)", - undef, (undef, $self->{'op_sys_id'}, - $self->{'rep_platform_id'}, $self->{'name'}, - $self->{'xml'})); - my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' ); - return $key; + VALUES (?,?,?,?)", + undef, (undef, $self->{'product_id'}, $self->{'name'}, $self->{'isactive'})); + my $key = $dbh->bz_last_key( 'test_environments', 'environment_id' ); + + my $elements = $self->{'elements'}; + + foreach my $element (@$elements) + { + $self->persist_environment_element_and_children(1, $element, "store"); + } + + return $key; +} + + +=head2 store property values + +Serializes the property values to the database + +=cut + +sub store_property_value { + my $self = shift; + + my ($prop_id,$elem_id,$value_selected) = @_; + + return 0 if ($self->check_value_selected($prop_id, $elem_id, $value_selected)); + + my $dbh = Bugzilla->dbh; + $dbh->do("INSERT INTO test_environment_map (environment_id,property_id,element_id,value_selected) + VALUES (?,?,?,?)",undef, ($self->{'environment_id'}, $prop_id, $elem_id,$value_selected)); + return 1; +} + +=head2 store environment name + +Serializes the environment name to the database + +=cut + +sub store_environment_name { + my $self = shift; + my ($name, $product_id) = (@_); + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + + return undef if $self->check_environment($name, $product_id); + + my $dbh = Bugzilla->dbh; + $dbh->do("INSERT INTO test_environments ($columns) + VALUES (?,?,?,?)", undef, + (undef,$product_id,$name,1)); + return $dbh->bz_last_key( 'test_environments', 'environment_id' ); } =head2 update @@ -190,12 +464,18 @@ sub update { my $self = shift; my ($newvalues) = @_; my $dbh = Bugzilla->dbh; + my $product_id; $dbh->bz_lock_tables('test_environments WRITE'); foreach my $field (keys %{$newvalues}){ if ($self->{$field} ne $newvalues->{$field}){ # If the new name is already in use, return. - if ($field eq 'name' && $self->check_name($newvalues->{'name'})) { + $product_id = $newvalues->{'product_id'} || $self->{'product_id'}; + if ($product_id eq undef) { + $dbh->bz_unlock_tables; + return 0; + } + if ($field eq 'name' && $self->check_environment($newvalues->{'name'}, $product_id)) { $dbh->bz_unlock_tables; return 0; } @@ -203,15 +483,86 @@ sub update { $dbh->do("UPDATE test_environments SET $field = ? WHERE environment_id = ?", undef, $newvalues->{$field}, $self->{'environment_id'}); - $self->{$field} = $newvalues->{$field}; - + $self->{$field} = $newvalues->{$field}; } } $dbh->bz_unlock_tables; + my $elements = $self->{'elements'}; + + foreach my $element (@$elements) + { + $self->persist_environment_element_and_children(1, $element, "update"); + } + return 1; } + +=head2 persist_environment_element_and_children + +Persists Environment Element and Children Recursively. + +=cut + +sub persist_environment_element_and_children { + my $self = shift; + my ($depth, $element, $method) = @_; + if ($depth > $max_depth) { + return; + } + $depth++; + my $elem_id = $element->{'element_id'}; + my $properties = $element->{'properties'}; + foreach my $property (@$properties) + { + my $prop_id = $property->{'property_id'}; + my $value_selected = $property->{'value_selected'}; + my ($value_stored) = $self->get_value_selected($self->{'environment_id'},$prop_id,$elem_id); + if ($method eq "store" || $value_stored eq undef) { + $self->store_property_value($prop_id,$elem_id,$value_selected); + } + else { + $self->update_property_value($prop_id,$elem_id,$value_selected); + } + } + my $children = $element->{'children'}; + foreach my $child_element (@$children) { + $self->persist_environment_element_and_children($depth, $child_element, $method); + } +} + + +=head2 update property value + +Updates the property of the element in the database + +=cut + +sub update_property_value { + + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + my $self = shift; + my ($propID, $elemID, $valueSelected) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->do("UPDATE test_environment_map + SET value_selected = ? WHERE environment_id = ? AND property_id = ? AND element_id = ?" + ,undef, ($valueSelected,$self->{'environment_id'},$propID,$elemID)); + return 1; +} + +sub delete_element { + my $self = shift; + my ($element_id) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM test_environment_map + WHERE environment_id = ? AND element_id = ?", + undef,($self->id, $element_id)); + +} + =head2 obliterate Completely removes this environment from the database. @@ -222,19 +573,23 @@ sub obliterate { my $self = shift; my $dbh = Bugzilla->dbh; - $dbh->bz_lock_tables('test_runs READ', 'test_environments WRITE'); + $dbh->bz_lock_tables('test_runs READ', 'test_environments WRITE','test_environment_map WRITE'); my $used = $dbh->selectrow_array("SELECT 1 FROM test_runs - WHERE environment_id = ?", + WHERE environment_id = ?", undef, $self->{'environment_id'}); if ($used) { $dbh->bz_unlock_tables; ThrowUserError("testopia-non-zero-run-count", {'object' => 'Environment'}); } - $dbh->do("DELETE FROM test_environments + $dbh->do("UPDATE test_environments SET isactive = 0 WHERE environment_id = ?", undef, $self->{'environment_id'}); + $dbh->bz_unlock_tables; + + return 1; } + =head2 get_run_list Returns a list of run ids associated with this environment. @@ -245,7 +600,7 @@ sub get_run_list { my $self = shift; my $dbh = Bugzilla->dbh; my $ref = $dbh->selectcol_arrayref("SELECT run_id FROM test_runs - WHERE environment_id = ?", + WHERE environment_id = ?", undef, $self->{'environment_id'}); return join(",", @{$ref}); } @@ -260,29 +615,11 @@ sub get_run_count { my $self = shift; my $dbh = Bugzilla->dbh; my ($count) = $dbh->selectrow_array("SELECT COUNT(run_id) FROM test_runs - WHERE environment_id = ?", + WHERE environment_id = ?", undef, $self->{'environment_id'}); return $count; } -=head2 check_name - -Returns true if an environment of the specified name exists -in the database. - -=cut - -sub check_name { - my $self = shift; - my ($name) = @_; - my $dbh = Bugzilla->dbh; - my ($used) = $dbh->selectrow_array("SELECT 1 - FROM test_environments - WHERE name = ?", - undef, $name); - return $used; -} - =head2 canedit Returns true if the logged in user has rights to edit this environment. @@ -333,57 +670,32 @@ Returns the ID of this object Returns the name of this object -=head2 xml - -Returnst the xml associated with this environment - =cut sub id { return $_[0]->{'environment_id'}; } +sub product_id { return $_[0]->{'product_id'}; } +sub isactive { return $_[0]->{'isactive'}; } sub name { return $_[0]->{'name'}; } -sub xml { return $_[0]->{'xml'}; } -=head2 op_sys +=head2 product -Returns the bugzilla op_sys value +Returns the bugzilla product =cut -sub op_sys { +sub product { my $self = shift; my $dbh = Bugzilla->dbh; - return $self->{'op_sys'} if exists $self->{'op_sys'}; - my ($res) = $dbh->selectrow_array("SELECT value - FROM op_sys - WHERE id = ?", - undef, $self->{'op_sys_id'}); - $self->{'op_sys'} = $res; - return $self->{'op_sys'}; - -} - -=head2 rep_platform - -Returns the bugzilla rep_platform value - -=cut - -sub rep_platform { - my $self = shift; - my $dbh = Bugzilla->dbh; - return $self->{'rep_platform'} if exists $self->{'rep_platform'}; - my ($res) = $dbh->selectrow_array("SELECT value - FROM rep_platform - WHERE id = ?", - undef, $self->{'rep_platform_id'}); - $self->{'rep_platform'} = $res; - return $self->{'rep_platform'}; + return $self->{'product'} if exists $self->{'product'}; + + $self->{'product'} = Bugzilla::Product->new($self->{'product_id'}); + return $self->{'product'}; } =head1 TODO -Use Bugzilla methods for op_sys and rep_platforms in 2.22 + =head1 SEE ALSO diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Category.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Category.pm new file mode 100644 index 00000000000..2263a51ebf0 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Category.pm @@ -0,0 +1,543 @@ +# -*- 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 +# Michael Hight +# Garrett Braden + +=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; + +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + env_category_id + product_id + name + +=cut + +use constant DB_COLUMNS => qw( + env_category_id + product_id + name + ); + +our $columns = join(", ", DB_COLUMNS); + +############################### +#### 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 $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; + +################## original +# Matches null for parent id +# +# my $ref = $dbh->selectcol_arrayref(qq{ +# SELECT element_id +# FROM test_environment_element +# WHERE env_category_id = ? AND parent_id is null }, undef, $self->{'env_category_id'}); +# +############### end original delete when ready + + +################## edited original +# Matches 0 for parent id +# +# my $ref = $dbh->selectcol_arrayref(qq{ +# SELECT element_id +# FROM test_environment_element +# WHERE env_category_id = ? AND parent_id = 0 }, undef, $self->{'env_category_id'}); +# +############### end edited original keep this one when ready + + +########### temp fix to address issues with 0 or null in test_envrionment_element parent_id column +##### matching null OR 0 + 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'}); +########### end temp fix.... delete when ready + + 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; +} + +=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 (Param('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 .= "GROUP BY p.id + 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' => '--ALL--', '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, $disable_move) = @_; + detaint_natural($product_id); + $disable_move = ',"addChild","move"' if $disable_move; + my $json = "["; + foreach my $cat (@{$self->get_element_categories_by_product($product_id)}){ + $json .= '{title:"' . $cat->name . '",'; + $json .= 'isFolder:' . ($cat->check_for_elements() ? "true" : "false") . ','; + $json .= 'objectId:"' . $cat->id . '",'; + $json .= 'widgetId:"category' . $cat->id . '",'; + $json .= 'actionsDisabled:["addCategory","addProperty","addValue"'; + $json .= $disable_move if $disable_move; + $json .= ',"remove"' unless $cat->candelete; + $json .= '],'; + $json .= 'childIconSrc:"testopia/img/square.gif"},'; + } + chop $json; + $json .= "]"; + return $json; +} + +=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 ($disable_add) = @_; + $disable_add = ',"addChild"' if $disable_add; + + my $elements = $self->get_parent_elements; + my $json = '['; + + foreach my $element (@$elements) + { + $json .= '{title:"'. $element->{'name'} .'",'; + $json .= 'objectId:"'. $element->{'element_id'}. '",'; + $json .= 'widgetId:"element'. $element->{'element_id'} .'",'; + $json .= 'actionsDisabled:["addCategory","addValue"'; + $json .= $disable_add if $disable_add; + $json .= ',"remove"' unless $element->candelete; + $json .= '],'; + $json .= 'isFolder:true,' if($element->check_for_children || $element->check_for_properties); + $json .= 'childIconSrc:"testopia/img/circle.gif"},'; + } + chop $json; + $json .= ']'; + + return $json; +} + +=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 check_category_by_id + +Returns category name if a category id exists + +=cut + +sub check_category_by_id{ + my $dbh = Bugzilla->dbh; + my $self = shift; + my ($id) = (@_); + + my ($used) = $dbh->selectrow_arrayref(qq{ + SELECT name + FROM test_environment_category + WHERE env_category_id = ?},undef,$id); + + return $used; +} + + +=head2 store + +Serializes this category to the database and returns the key or 0 + +=cut + +sub store { + my $self = shift; + 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, (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; + + $dbh->bz_lock_tables('test_environment_element AS tee READ', 'test_environment_map AS tem READ', 'test_environment_category WRITE'); + + if (!$self->candelete) { + $dbh->bz_unlock_tables; + return 0; + } + $dbh->do("DELETE FROM test_environment_category + WHERE env_category_id = ?", undef, $self->id); + $dbh->bz_unlock_tables; + + return 1; + +} + +sub candelete { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $used = $dbh->selectrow_array( + "SELECT 1 FROM test_environment_map AS tem + JOIN test_environment_element AS tee ON tee.element_id = tem.element_id + WHERE tee.env_category_id = ?", + undef, $self->id); + return !$used; + +} + +############################### +#### 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'}; } + + +=head2 product_name + +Returns the name of the product this plan is associated with + +=cut + +sub product_name { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + return $self->{'product_name'} if exists $self->{'product_name'}; + + my ($name) = $dbh->selectrow_array( + "SELECT name FROM products + WHERE id = ?", + undef, $self->id); + $self->{'product_name'} = $name; + return $self->{'product_name'}; +} + +1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Element.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Element.pm new file mode 100644 index 00000000000..6d7c9db6757 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Element.pm @@ -0,0 +1,471 @@ +# -*- 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 +# Michael Hight +# Garrett Braden + +=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 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 $columns = join(", ", DB_COLUMNS); + +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 $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; +} + +=head2 get_children + +Returns an array of the children objects for an element and recursively +gets all of their children until exhausted. + +=cut + +sub get_children{ + 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_children('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 ($used) = $dbh->selectrow_array(qq{ + SELECT 1 + FROM test_environment_element + WHERE parent_id = ? },undef,$self->{'element_id'}); + + return $used; +} + +sub children_to_json{ + my $self = shift; + my ($disable_move) = @_; + + $disable_move = ',"addChild","move","remove"' if $disable_move; + my $disable_add = ',"addChild"' if $disable_move; + my $elements = $self->get_children; + my $properties = $self->get_properties; + my $json = '['; + + foreach my $elem (@$elements) + { + $json .= '{title:"' . $elem->{'name'} . '",'; + $json .= 'objectId:"' . $elem->{'element_id'} . '",'; + $json .= 'widgetId:"element' . $elem->{'element_id'} . '",'; + $json .= 'actionsDisabled:["addCategory","addValue"'; + $json .= $disable_add if $disable_add; + $json .=',"remove"' unless $elem->candelete; + $json .= '],'; + $json .= 'isFolder:true,' if $elem->check_for_children(); + $json .= 'childIconSrc:"testopia/img/circle.gif"},'; + } + foreach my $prop (@$properties) + { + $json .= '{title: "' . $prop->{'name'} .'",'; + $json .= 'objectId:"' . $prop->{'property_id'} . '",'; + $json .= 'widgetId:"property' . $prop->{'property_id'} . '",'; + $json .= 'actionsDisabled:["addCategory","addElement","addProperty"'; + $json .= $disable_move if $disable_move; + $json .= ',"remove"' unless $prop->candelete; + $json .= '],'; + $json .= 'isFolder:true,' if($prop->check_for_validexp($prop)); + $json .= 'childIconSrc:"testopia/img/triangle.gif"},'; + } + chop $json if ($json ne '['); + $json .= ']'; + + return $json; +} + +=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; +} + +=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; + 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, (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 update_element_name + +Updates the element in the database + +=cut + +sub update_element_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; + + $dbh->bz_lock_tables('test_environment_map READ', 'test_environment_element WRITE'); + if (!$self->candelete) { + $dbh->bz_unlock_tables; + return 0; + } + $dbh->do("DELETE FROM test_environment_element + WHERE element_id = ?", undef, $self->{'element_id'}); + $dbh->bz_unlock_tables; + + return 1; +} + +sub candelete { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $used = $dbh->selectrow_array("SELECT 1 FROM test_environment_map + WHERE element_id = ?", + undef, $self->id); + return !$used; + +} + +############################### +#### 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 { return $_[0]->new($_[0]->{'parent_id'}); } + +1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Property.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Property.pm new file mode 100644 index 00000000000..e09718dd1bf --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Property.pm @@ -0,0 +1,372 @@ +# -*- 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 +# Michael Hight +# Garrett Braden + +=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; +############################### +#### Initialization #### +############################### + +=head1 FIELDS + + property_id + element_id + name + validexp + +=cut + +use constant DB_COLUMNS => qw( + property_id + element_id + name + validexp + ); + +our $columns = join(", ", DB_COLUMNS); + +############################### +#### 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 $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; + + if ($name eq undef || $name eq '' || $element_id eq undef) { + 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_arrayref(qq{ + 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; + 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, (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 valid_exp_to_json { + my $self = shift; + my ($disable_move, $env_id) = @_; + my $env = Bugzilla::Testopia::Environment->new($env_id) if $env_id; + + $disable_move = ',"addChild","move","remove"' if $disable_move; + my $validexp = $self->get_validexp; + + my @validexpressions = split(/\|/, @$validexp[0]); + + my $json = '['; + + foreach (@validexpressions) + { + $json .= '{title: "' . $_ . '",'; + $json .= 'widgetId:"validexp' . $self->id . '~'. $_ .'",'; + $json .= 'actionsDisabled:["addCategory","addElement","addProperty","addValue"'. $disable_move .'],'; + $json .= 'objectId:"' . $self->id . '~' . $_ . '",'; + if ($env && $env->get_value_selected($env->id, $self->element_id, $self->id) eq $_){ + $json .= 'childIconSrc:"testopia/img/selected_value.png"},'; + } + else{ + $json .= 'childIconSrc:""},'; + } + } + chop $json; + $json .= ']'; + + return $json; +} + +=head2 obliterate + +Completely removes the element property entry from the database. + +=cut + +sub obliterate { + my $self = shift; + my $dbh = Bugzilla->dbh; + + $dbh->bz_lock_tables('test_environment_map READ', 'test_environment_property WRITE'); + if (!$self->candelete) { + $dbh->bz_unlock_tables; + return 0; + } + $dbh->do("DELETE FROM test_environment_property + WHERE property_id = ?", undef, $self->id); + $dbh->bz_unlock_tables; + + return 1; + +} + +sub candelete { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $used = $dbh->selectrow_array("SELECT 1 FROM test_environment_map + WHERE property_id = ?", + undef, $self->id); + return !$used; + +} + +############################### +#### 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'}; } + +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Xml.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Xml.pm new file mode 100644 index 00000000000..3b89a7b655d --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Environment/Xml.pm @@ -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 + + +=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...
"; + } + else { + $self->{'message'} = "Parsing and Validating XML Environment...
"; + } + $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 --ALL-- PRODUCT.
"; + $product_id = 0; + } + else { + $self->{'message'} .= "..Checking if $product_name PRODUCT already exists..."; + ($product_id) = Bugzilla::Testopia::Product->check_product_by_name($product_name); + if ($product_id) { + $self->{'message'} .= "EXISTS.
"; + } + else { + $self->{'message'} .= "DOESN'T EXIST.
Importing XML Environment Failed!
"; + $self->{'error'} .= "$product_name PRODUCT doesn't exist. Please be sure to use an existing product.
"; + 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 $environment_name ENVIRONMENT NAME already exists for the $product_name PRODUCT..."; + 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'} .= "DOESN'T EXIST
"; + # Storing New Environment if Admin + if ($admin) { + $self->{'message'} .= "....Storing new $environment_name ENVIRONMENT NAME in the $self->{'product_name'} PRODUCT..."; + $environment->{'name'} = $environment_name; + ($environment_id) = Bugzilla::Testopia::Environment->store_environment_name($self->{'name'}, $product_id); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($environment_id) = $env_id; + $self->{'message'} .= "EXISTS
Importing XML Environment Failed!
"; + $self->{'error'} .= "$environment_name ENVIRONMENT NAME already exists for the $product_name PRODUCT. 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 $category_name CATEGORY already exists..."; + if ($cat_id < 1) { + $self->{'message'} .= "DOESN'T EXIST.
"; + 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 $category_name CATEGORY in the $self->{'product_name'} PRODUCT..."; + ($category->{'product_id'}) = $product_id; + $category->{'name'} = $category_name; + ($category_id) = $category->store(); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($category_id) = $cat_id; + $self->{'message'} .= "EXISTS.
"; + } + 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!
"; + } + else { + $self->{'message'} .= "Finished Parsing and Validating XML Environment!
"; + } +} + + +=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 $element_name ELEMENT already exists in the $category_name CATEGORY..."; + 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'} .= "DOESN'T EXIST.
"; + 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 $element_name ELEMENT in the $category_name CATEGORY..."; + ($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.
"; + } + } + else { + ($element_id) = $elem_id; + $self->{'message'} .= "EXISTS.
"; + } + ($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 $property_name PROPERTY 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'} .= "DOESN'T EXIST.
"; + 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 $property_name PROPERTY..."; + $property->{'name'} = $property_name; + ($property->{'element_id'}) = $element_id; + ($property_id) = $property->store(); + $self->{'message'} .= "DONE.
"; + } + } + else { + ($property_id) = $prop_id; + $self->{'message'} .= "EXISTS.
"; + } + $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 $value VALUE exists in the list of selectable values..."; + if ( $validexp !~ m/$value/) { + $self->{'message'} .= "DOESN'T EXIST.
"; + if ($admin) { + if (!defined($validexp)) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "..........Setting $value VALID EXPRESSION equal to the VALUE for the first time..."; + $validexp = $value; + } + else { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "..........Adding $value VALUE to the VALID EXPRESSION..."; + $validexp = "$validexp | $value"; + } + $property->update_property_validexp($validexp); + $self->{'message'} .= "DONE.
"; + } + 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'} .= "VALID EXPRESSION DOESN'T EXIST YET.
"; + } + else { + $self->{'message'} .= "EXISTS.
"; + } + if ($property_id && $admin) { + for (my $i = 1; $i < $depth; $i++) { + $self->{'message'} .= "...."; + } + $self->{'message'} .= "............Storing new VALUE SELECTED $value..."; + my $environment = Bugzilla::Testopia::Environment->new($self->{'environment_id'}); + $environment->store_property_value($property_id, $element_id, $value); + $self->{'message'} .= "DONE.
"; + } + $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 $new_category_name CATEGORY.
"; + } + my $new_category_elements = $self->{'new_category_elements'}; + foreach my $new_category_element (@$new_category_elements) { + $report .= "New $new_category_element->{'element_name'} ELEMENT in the "; + if (!$new_category_element->{'env_category_id'}){ + $report .= "new "; + } + $report .= "$new_category_element->{'category_name'} CATEGORY.
"; + } + my $new_property_names = $self->{'new_property_names'}; + foreach my $new_property_name (@$new_property_names) { + $report .= "New $new_property_name PROPERTY.
"; + } + my $new_validexp_values = $self->{'new_validexp_values'}; + foreach my $new_validexp_value (@$new_validexp_values) { + $report .= "New $new_validexp_value->{'value'} VALUE for the "; + if (!$new_validexp_value->{'property_id'}){ + $report .= "new "; + } + $report .= "$new_validexp_value->{'property_name'} PROPERTY's selectable value list.
"; + } + 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!
"; + $self->{'error'} .= "Failed to store the Environment!
"; + return 0; + } + $self->{'message'} .= "DONE!
"; + 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 = + "" . + "" . + "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 .= ""; + + $elements = $categories->{$category_name}; + + + foreach my $element (@$elements) { + $element->get_children(); + $xml .= $self->export_element_and_children(1, $element, $environment->{'environment_id'}); + } + + $xml .= ""; + } + + return "$xml"; +} + + +=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) { + 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 = ""; + + 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 .= + "" . + "$value_selected"; + } + } + + my $children = $element->{'children'}; + foreach my $child_element (@$children) { + $child_element->get_children(); + $xml .= $self->export_element_and_children($depth, $child_element, $env_id); + } + $xml .= ""; + return $xml; +} + +1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Product.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Product.pm index 7779249a287..502546e79ea 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Product.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Product.pm @@ -50,16 +50,60 @@ sub builds { my $ref = $dbh->selectcol_arrayref( "SELECT build_id - FROM test_builds tb - JOIN test_plans tp ON tb.plan_id = tp.plan_id - WHERE tp.product_id = ?", + FROM test_builds + WHERE product_id = ?", undef, $self->{'id'}); my @objs; foreach my $id (@{$ref}){ push @objs, Bugzilla::Testopia::Build->new($id); } - $self->{'build'} = \@objs; - return $self->{'build'}; + $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 = ?", + undef, $self->{'id'}); + my @objs; + foreach my $id (@{$ref}){ + push @objs, Bugzilla::Testopia::Category->new($id); + } + $self->{'categories'} = \@objs; + return $self->{'categories'}; +} + +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; + 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; } 1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Schema.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Schema.pm deleted file mode 100644 index b3d8b167603..00000000000 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Schema.pm +++ /dev/null @@ -1,489 +0,0 @@ -# -*- 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 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): Justin C. De Vries - -package Bugzilla::Testopia::Schema; - -use base qw(Bugzilla::DB::Schema); - -use strict; -use Bugzilla::Error; -use Bugzilla::Util; -use Carp; -use Safe; - -# Historical, needed for SCHEMA_VERSION = '1.00' -use Storable qw(dclone freeze thaw); - -#New SCHEMA_Version (2.00) use this -use Data::Dumper; - -use constant SCHEMA_VERSION => '2.00'; -use constant ABSTRACT_SCHEMA => { - #Note: Most of types will need to be defined in a MySQL specific file some where. - test_category_templates => { - FIELDS => [ - # Note: UNSMALLSERIAL might need to be added to the MySQL.pm file. Its - # definition is UNSIGNED SMALLINT AUTO_INCREMENT. - category_template_id => {TYPE => 'UNSMALLSERIAL', NOTNULL => 1}, - name => {TYPE => 'varchar(255)', NOTNULL => 1}, - description => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - category_template_name_idx => ['name'], - ], - }, - - test_attachments => { - FIELDS => [ - # Note: Serial is a MySQL specific type. This will need to - # be written or modified to be database generic. - attachment_id => {TYPE => 'SERIAL'}, - # Note: UNBIGINT need to be added to the MySQL.pm file. Its - # definition is UNSIGNED BIGINT. - plan_id => {TYPE => 'UNBIGINT'}, - case_id => {TYPE => 'UNBIGINT'}, - submitter_id => {TYPE => 'INT3', NOTNULL => 1}, - description => {TYPE => 'MEDIUMTEXT'}, - filename => {TYPE => 'MEDIUMTEXT'}, - creation_ts => {TYPE => 'DATETIME'}, - mime_type => {TYPE => 'varchar(100)', NOTNULL => 1}, - ], - INDEXES => [ - attachment_plan_idx => ['plan_id'], - attachment_case_idx => ['case_id'], - ], - }, - - test_case_categories => { - FIELDS => [ - category_id => {TYPE => 'UNSMALLSERIAL', NOTNULL => 1}, - product_id => {TYPE => 'INT2', NOTNULL => 1}, - name => {TYPE => 'varchar(240)', NOTNULL => 1}, - description => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - category_name_idx => ['name'], - ], - }, - - test_cases => { - FIELDS => [ - case_id => {TYPE => 'SERIAL'}, - case_status_id => {TYPE => 'INT1', NOTNULL => 1}, - # Note: UNINT2 need to be added to the MySQL.pm file. Its - # definition is UNSIGNED SMALLINT. - category_id => {TYPE => 'UNINT2', NOTNULL => 1}, - priority_id => {TYPE => 'INT2'}, - author_id => {TYPE => 'INT3', NOTNULL => 1}, - creation_date => {TYPE => 'DATETIME', NOTNULL => 1}, - isautomated => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0'}, - sortkey => {TYPE => 'INT4'}, - script => {TYPE => 'MEDIUMTEXT'}, - arguments => {TYPE => 'MEDIUMTEXT'}, - summary => {TYPE => 'varchar(255)'}, - requirement => {TYPE => 'varchar(255)'}, - alias => {TYPE => 'varchar(255)'}, - ], - INDEXES => [ - test_case_category_idx => ['category_id'], - test_case_author_idx => ['author_id'], - test_case_creation_date_idx => ['creation_date'], - test_case_sortkey_idx => ['sortkey'], - test_case_requirement_idx => ['requirement'], - test_case_shortname_idx => ['alias'], - ], - }, - - test_case_bugs => { - FIELDS => [ - bug_id => {TYPE => 'INT3', NOTNUTLL => 1}, - case_run_id => {TYPE => 'UNBIGINT'}, - ], - INDEXES => [ - case_run_id_idx => [qw(case_run_id bug_id)], - case_run_bug_id_idx => ['bug_id'], - ], - }, - - test_case_runs => { - FIELDS => [ - case_run_id => {TYPE => 'SERIAL'}, - run_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - case_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - assignee => {TYPE => 'INT3'}, - testedby => {TYPE => 'INT3'}, - case_run_status_id => {TYPE => 'UNINT1', NOTNULL => 1}, - case_text_version => {TYPE => 'UNINT3', NOTNULL => 1}, - build_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - close_date => {TYPE => 'DATETIME'}, - notes => {TYPE => 'TEXT'}, - iscurrent => {TYPE => 'BOOLEAN', DEFAULT => '0'}, - sortkey => {TYPE => 'INT4'}, - ], - INDEXES => [ - case_run_run_id_idx => ['run_id'], - case_run_case_id_idx => ['case_id'], - case_run_assignee_idx => ['assignee'], - case_run_testedby_idx => ['testedby'], - case_run_close_date_idx => ['close_date'], - case_run_shortkey_idx => ['sortkey'], - ], - }, - - test_case_texts => { - FIELDS => [ - case_id => {TYPE => 'UNBIGINT', NOTNULL =>1}, - case_text_version => {TYPE => 'INT3', NOTNULL =>1}, - who => {TYPE => 'INT3', NOTNULL =>1}, - creation_ts => {TYPE => 'DATETIME', NOTNULL =>1}, - action => {TYPE => 'MEDIUMTEXT'}, - effect => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - case_versions_idx => {FIELDS => [qw(case_id case_text_version)], - TYPE => 'UNIQUE'}, - case_versions_who_idx => ['who'], - case_versions_creation_ts_idx => ['creation_ts'], - ], - }, - - test_case_tags => { - FIELDS => [ - tag_id => {TYPE => 'UNINT4', NOTNULL => 1}, - case_id => {TYPE => 'UNBIGINT'}, - ], - INDEXES => [ - case_tags_case_id_idx => [qw(case_id tag_id)], - case_tags_tag_id_idx => [qw(tag_id case_id)], - test_tag_name_indx => ['tag_name'], - ], - }, - - test_plan_testers => { - FIELDS => [ - tester_id => {TYPE => 'INT3', NOTNULL => 1}, - product_id => {TYPE => 'INT2'}, - read_only => {TYPE => 'BOOLEAN', DEFAULT => '1'}, - ], - }, - - test_tags => { - FIELDS => [ - # Note: UNINT4SERIAL need to be added to the MySQL.pm file. Its - # definition is UNSIGNED INTEGER AUTO_INCREMENT. - tag_id => {TYPE => 'UNINT4SERIAL', NOTNULL => 1}, - tag_name => {TYPE => 'varchar(255)', NOTNULL => 1}, - ], - }, - - test_plans => { - FIELDS => [ - plan_id => {TYPE => 'SERIAL'}, - product_id => {TYPE => 'INT2', NOTNULL => 1}, - author_id => {TYPE => 'INT3', NOTNULL => 1}, - editor_id => {TYPE => 'INT3', NOTNULL => 1}, - type_id => {TYPE => 'UNINT1', NOTNULL => 1}, - default_product_version => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, - name => {TYPE => 'varchar(255)', NOTNULL => 1}, - creation_date => {TYPE => 'DATETIME', NOTNULL => 1}, - isactive => {TYPE => 'BOOLEAN', NOTNULL =>, DEFAULT => '1'}, - ], - INDEXES => [ - plan_product_plan_id_idx => [qw(product_id plan_id)], - plan_author_idx => ['author_id'], - plan_type_idx => ['type_id'], - plan_editor_idx => ['editor_id'], - plan_isactive_idx => ['isactive'], - ], - }, - - text_plan_text => { - FIELDS => [ - plan_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - plan_text_version => {TYPE => 'INT4', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, - creation_ts => {TYPE => 'DATETIME', NOTNULL => 1}, - # Note: LONGTEXT needs to be added to the MySQL.pm file. Its - # definition is LONGTEXT. - plan_text => {TYPE => 'LONGTEXT'}, - ], - INDEXES => [ - test_plan_text_version_idx => [qw(plan_id plan_text_version)], - test_plan_text_who_idx => ['who'], - ], - }, - - test_plan_types => { - FIELDS => [ - # Note: UNTINYINTSERIAL needs to be added to the MySQL.pm file. Its - # definition is UNSIGNED TINYINT AUTO_INCREMENT. - type_id => {TYPE => 'UNTINYSERIAL', NOTNULL => 1}, - name => {TYPE => 'varchar(64)', NOTNULL => 1}, - ], - INDEXES => [ - plan_type_name_idx => ['name'], - ], - }, - - test_runs => { - FIELDS => [ - run_id => {TYPE => 'SERIAL'}, - plan_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - environment_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - product_version => {TYPE => 'MEDIUMTEXT'}, - build_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - plan_text_version => {TYPE => 'INT4', NOTNULL => 1}, - manager_id => {TYPE => 'INT3', NOTNULL => 1}, - default_tester_id => {TYPE => 'INT3'}, - start_date => {TYPE => 'DATETIME'}, - stop_date => {TYPE => 'DATETIME'}, - summary => {TYPE => 'TINYTEXT', NOTNULL => 1}, - notes => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - test_run_plan_id_run_id_idx => [qw(plan_id run_id)], - test_run_manager_idx => ['manager_id'], - test_run_start_date_idx => ['start_date'], - ], - }, - - test_case_plans => { - FIELDS => [ - plan_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - case_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - ], - INDEXES => [ - case_plans_plan_id_idx => ['plan_id'], - case_plans_case_id_idx => ['plan_id'], - ], - }, - - test_case_activity => { - FIELDS => [ - case_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - fieldid => {TYPE => 'UNINT2', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, - changed => {TYPE => 'DATETIME', NOTNULL => 1}, - oldvalue => {TYPE => 'MEDUIMTEXT'}, - newvalue => {TYPE => 'MEDUIMTEXT'}, - ], - INDEXES => [ - case_activity_case_id_idx => ['case_id'], - case_activity_who_idx => ['who'], - case_activity_when_idx => ['changed'], - case_activity_field_idx => ['fieldid'], - ], - }, - - test_fielddefs => { - FIELDS => [ - fieldid => {TYPE => 'UNSMALLSERIAL', NOTNULL => 1}, - name => {TPYE => 'varchar(100)', NOTNULL => 1}, - description => {TYPE => 'MEDIUMTEXT'}, - table_name => {TYPE => 'varchar(100)', NOTNULL => 1}, - ], - INDEXES => [ - fielddefs_name_idx => ['name'], - ], - }, - - test_plan_activity => { - FIELDS => [ - plan_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - fieldid => {TYPE => 'UNINT2', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, - changed => {TYPE => 'DATETIME', NOTNULL => 1}, - oldvalue => {TYPE => 'MEDIUMTEXT'}, - newvalue => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - plan_activity_who_idx => ['who'], - ], - }, - - test_case_components => { - FIELDS => [ - case_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - component_id => {TYPE => 'INT2', NOTNULL => 1}, - ], - INDEXES => [ - case_components_case_id_idx => ['case_id'], - case_commponents_component_id_idx => ['case_id'], - ], - }, - - test_run_activity => { - FIELDS => [ - run_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - fieldid => {TYPE => 'UNINT2', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, - changed => {TYPE => 'DATETIME', NOTNULL => 1}, - oldvalue => {TYPE => 'MEDIUMTEXT'}, - newvalue => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - run_activity_run_id_idx => ['run_id'], - run_activity_who_idx => ['who'], - run_activity_when_idx => ['changed'], - ], - }, - - test_run_cc => { - FIELDS => [ - run_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - who => {TYPE => 'INT3', NOTNULL => 1}, - ], - INDEXES => [ - run_cc_run_id_who_idx => [qw(run_id who)], - ], - }, - - test_email_settings => { - FIELDS => [ - userid => {TYPE => 'INT3', NOTNULL => 1}, - eventid => {TYPE => 'UNINT1', NOTNULL => 1}, - relationship_id => {TYPE => 'UNINT1', NOTNULL => 1}, - ], - INDEXES => [ - test_event_user_event_idx => [qw(userid eventid)], - test_event_user_relationship_idx => [qw(userid relationship_id)], - ], - }, - - test_events => { - FIELDS => [ - eventid => {TYPE => 'UNIN1', NOTNULL => 1}, - name => {TYPE => 'varchar(50)'}, - ], - INDEXES => [ - test_event_name_idx => ['name'], - ], - }, - - test_relationships => { - FIELDS => [ - relationship_id => {TYPE => 'UNINT1', NOTNULL => 1}, - name => {TYPE => 'varchar(50)'}, - ], - }, - - test_case_run_status => { - FIELDS => [ - case_run_status_id => {TYPE => 'UNTINYSERIAL', NOTNULL => 1}, - name => {TYPE => 'varchar(20)'}, - sortkey => {TYPE => 'INT4'}, - ], - INDEXES => [ - case_run_status_name_idx => ['name'], - case_run_status_sortkey_idx => ['sortkey'], - ], - }, - - test_case_status => { - FIELDS => [ - case_status_id => {TYPE => 'UNTINYSERIAL', NOTNULL => 1}, - name => {TYPE => 'varchar(255)'}, - ], - INDEXES => [ - test_case_status_name_idx => ['name'], - ], - }, - - test_case_dependencies => { - FIELDS => [ - dependson => {TYPE => 'UNBIGINT', NOTNULL => 1}, - blocks => {TYPE => 'UNBIGINT'}, - ], - }, - - test_case_group_map => { - FIELDS => [ - case_id => {TYPE => 'UNBIGINT', NOTNULL =>1}, - group_id => {TYPE => 'INT3'}, - ], - }, - - test_environments => { - FIELDS => [ - environment_id => {TYPE => 'SERIAL', NOTNULL => 1}, - op_sys_id => {TYPE => 'INT4'}, - rep_platform_id => {TYPE => 'INT4'}, - name => {TYPE => 'varchar(255)'}, - xml => {TYPE => 'MEDIUMTEXT'}, - ], - INDEXES => [ - environment_op_sys_idx => ['op_sys_id'], - environment_platform_idx => ['rep_platform_id'], - environment_name_idx => ['name'], - ], - }, - - test_run_tags => { - FIELDS => [ - tag_id => {TYPE => 'UNINT4', NOTNULL => 1}, - run_id => {TYPE => 'UNBIGINT'}, - ], - INDEXES => [ - run_tags_idx => ['qw(tag_id, run_id'], - ], - }, - - test_plan_tags => { - FIELDS => [ - tag_id => {TYPE => 'UNINT4', NOTNULL => 1}, - plan_id => {TYPE => 'UNBIGINT'}, - ], - INDEXES => [ - plan_tags_idx => ['qw(tag_id, plan_id'], - ], - }, - - test_build => { - FIELDS => [ - build_id => {TYPE => 'SERIAL', NOTNULL => 1}, - product_id => {TYPE => 'INT2', NOTNULL => 1}, - name => {TYPE => 'varchar(255)'}, - description => {TYPE => 'TEXT'}, - ], - }, - - test_attachment_data => { - FIELDS => [ - attachment_id => {TYPE => 'UNBIGINT', NOTNULL => 1}, - contents => {TYPE => 'LONGBLOB'}, - ], - }, - - test_named_queries => { - FIELDS => [ - userid => {TYPE => 'INT3', NOTNULL => 1}, - name => {TYPE => 'varchar(64)', NOTNULL => 1}, - isvisible => {TYEP => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1}, - query => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, - ], - INDEXES => [ - test_namedquery_name_idx => ['name'], - ], - }, - -}; - -1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm index af1ae436d5a..2e9de55c57d 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm @@ -133,9 +133,10 @@ sub init { my $type; my $obj = trim($cgi->param('current_tab')) || ThrowUserError('testopia-missing-parameter', {'param' => 'current_tab'}); - ThrowUserError('unknown-tab') if $obj !~ '^(case|plan|run|case_run)$'; + ThrowUserError('unknown-tab') if $obj !~ '^(case|plan|run|case_run|environment)$'; trick_taint($obj); + # Set up tables for field sort order my $order = $cgi->param('order') || ''; if ($order eq 'author') { push @supptables, "INNER JOIN profiles as map_author ON map_author.userid = test_". $obj ."s.author_id"; @@ -158,7 +159,7 @@ sub init { push @orderby, 'map_tester.login_name'; } elsif ($order eq 'product') { - push @supptables, "INNER JOIN products ON products.id = test_". $obj ."s.product_id"; + push @supptables, "LEFT JOIN products ON products.id = test_". $obj ."s.product_id"; push @orderby, 'products.name'; } elsif ($order eq 'build') { @@ -178,6 +179,9 @@ sub init { push @orderby, 'versions.value'; } elsif ($order eq 'priority') { + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases ON test_cases.case_id = test_case_runs.case_id"; + } push @supptables, "INNER JOIN priority ON priority.id = test_cases.priority_id"; push @orderby, 'test_cases.priority_id'; } @@ -201,7 +205,13 @@ sub init { push @orderby, 'case_status.name'; } elsif ($order eq 'summary') { - push @orderby, 'test_'. $obj .'s.summary'; + if ($obj eq 'case_run'){ + push @supptables, "INNER JOIN test_cases AS cases ON cases.case_id = test_case_runs.case_id"; + push @orderby, 'cases.summary'; + } + else{ + push @orderby, 'test_'. $obj .'s.summary'; + } } elsif ($order eq 'created') { push @orderby, 'test_'. $obj .'s.creation_date'; @@ -215,6 +225,7 @@ sub init { push @orderby, 'test_'. $obj .'s.' . $order; } } + my @funcdefs = ( "^category," => sub { @@ -250,12 +261,71 @@ sub init { $f = "plan_texts.plan_text"; }, "^environment," => sub { + if ($obj eq 'case_run'){ + $f = "environment_id"; + } + else{ + push(@supptables, + "LEFT JOIN test_environments AS env " . + "ON test_runs.environment_id = env.environment_id"); + $f = "env.xml"; + } + }, + "^env_products," => sub { + print STDERR "THIS IS HERE"; push(@supptables, - "LEFT JOIN test_environments AS env " . - "ON test_runs.environment_id = env.environment_id"); - $f = "env.xml"; + "INNER JOIN products as env_products + ON test_environments.product_id = env_products.id"); + $f = 'env_products.id' + }, + "^env_categories," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_categories + ON test_environments.environment_id = env_map_categories.environment_id"); + push(@supptables, + "INNER JOIN test_environment_element as env_element + ON env_map_categories.element_id = env_element.element_id"); + push(@supptables, + "INNER JOIN test_environment_category as env_categories + ON env_element.env_category_id = env_categories.env_category_id"); + $f = 'env_categories.env_category_id' + }, + "^env_elements," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_elements + ON test_environments.environment_id = env_map_elements.environment_id"); + push(@supptables, + "INNER JOIN test_environment_element as env_element + ON env_map_elements.element_id = env_element.element_id"); + $f = 'env_element.element_id' + }, + "^env_properties," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_properties + ON test_environments.environment_id = env_map_properties.environment_id"); + push(@supptables, + "INNER JOIN test_environment_property as env_property + ON env_map_properties.property_id = env_property.property_id"); + $f = 'env_property.property_id' + }, + "^env_expressions," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_value + ON test_environments.environment_id = env_map_value.environment_id"); + $f = 'env_map_value.value_selected' + }, + "^env_value_selected," => sub { + push(@supptables, + "INNER JOIN test_environment_map as env_map_value_selected + ON test_environments.environment_id = env_map_value_selected.environment_id"); + $f = 'env_map_value_selected.value_selected' }, "^component," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } push(@supptables, "INNER JOIN test_case_components AS tc_components " . "ON test_cases.case_id = tc_components.case_id"); @@ -264,6 +334,22 @@ sub init { "ON components.id = tc_components.component_id"); $f = "components.name"; }, + "^priority_id," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + $f = "test_cases.priority_id"; + }, + "^isautomated," => sub { + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases + ON test_cases.case_id = test_case_runs.case_id"); + } + $f = "test_cases.isautomated"; + }, "^milestone," => sub { push(@supptables, "INNER JOIN test_builds AS builds " . @@ -282,13 +368,33 @@ sub init { "ON case_bugs.bug_id = bugs.bug_id"); $f = "bugs.bug_id"; }, + "^case_summary," => sub { + push(@supptables, + "INNER JOIN test_cases AS cases " . + "ON cases.case_id = test_case_runs.case_id"); + $f = "cases.summary"; + }, + "^tags," => sub { - push(@supptables, - "INNER JOIN test_". $obj ."_tags AS ". $obj ."_tags " . - "ON test_". $obj ."s.". $obj ."_id = ". $obj ."_tags.". $obj ."_id"); - push(@supptables, - "INNER JOIN test_tags " . - "ON ". $obj ."_tags.tag_id = test_tags.tag_id"); + if ($obj eq 'case_run'){ + push(@supptables, + "INNER JOIN test_cases " . + "ON test_case_runs.case_id = test_cases.case_id"); + push(@supptables, + "INNER JOIN test_case_tags AS case_tags " . + "ON test_cases.case_id = case_tags.case_id"); + push(@supptables, + "INNER JOIN test_tags " . + "ON case_tags.tag_id = test_tags.tag_id"); + } + else{ + push(@supptables, + "INNER JOIN test_". $obj ."_tags AS ". $obj ."_tags " . + "ON test_". $obj ."s.". $obj ."_id = ". $obj ."_tags.". $obj ."_id"); + push(@supptables, + "INNER JOIN test_tags " . + "ON ". $obj ."_tags.tag_id = test_tags.tag_id"); + } $f = "test_tags.tag_name"; }, "^case_plan_id," => sub { @@ -324,17 +430,28 @@ sub init { "ON test_plans.product_id = products.id"); $f = "test_plans.product_id"; }, - "^(author|editor|manager|default_tester)," => sub { + "^(author|manager|default_tester)," => sub { push(@supptables, "INNER JOIN profiles AS map_$1 " . "ON test_". $obj ."s.". $1 ."_id = map_$1.userid"); - $f = "map_$1.login_name"; + $f = "map_$1.login_name"; }, "^(assignee|testedby)," => sub { - push(@supptables, + if ($obj eq 'run'){ + push(@supptables, + "LEFT JOIN test_case_runs AS case_run " . + "ON case_run.run_id = test_runs.run_id"); + push(@supptables, + "INNER JOIN profiles AS map_$1 " . + "ON case_run.". $1 ." = map_$1.userid"); + } + else { + push(@supptables, "INNER JOIN profiles AS map_$1 " . "ON test_". $obj ."s.". $1 ." = map_$1.userid"); + } $f = "map_$1.login_name"; + }, ",isnotnull" => sub { $term = "$ff is not null"; @@ -409,22 +526,46 @@ sub init { if ($cgi->param('case_id')) { my $type = "anyexact"; - if ($cgi->param('caseidtype') && $cgi->param('caseidtype') eq 'exclude') { - $type = "nowords"; + if ($cgi->param('caseidtype')) + { + if ($cgi->param('caseidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('caseidtype') + } } push(@specialchart, ["case_id", $type, join(',', $cgi->param('case_id'))]); } if ($cgi->param('run_id')) { my $type = "anyexact"; - if ($cgi->param('runidtype') && $cgi->param('runidtype') eq 'exclude') { - $type = "nowords"; + if ($cgi->param('runidtype')) + { + if ($cgi->param('runidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('runidtype') + } } push(@specialchart, ["run_id", $type, join(',', $cgi->param('run_id'))]); } if ($cgi->param('plan_id')) { my $type = "anyexact"; - if ($cgi->param('planidtype') && $cgi->param('planidtype') eq 'exclude') { - $type = "nowords"; + if ($cgi->param('planidtype')) + { + if ($cgi->param('planidtype') eq 'exclude') + { + $type = "nowords"; + } + else + { + $type = $cgi->param('planidtype') + } } if ($obj eq 'case'){ push(@specialchart, ["case_plan_id", $type, join(',', $cgi->param('plan_id'))]); @@ -469,7 +610,8 @@ sub init { my @legal_fields = ("case_status_id", "category", "priority_id", "component", "isautomated", "case_run_status_id", "default_product_version", "type_id", - "build", "environment_id", "milestone"); + "build", "environment_id", "milestone", "env_products", + "env_categories", "env_elements", "env_properties", "env_expressions"); foreach my $field ($cgi->param()) { if (lsearch(\@legal_fields, $field) != -1) { @@ -509,7 +651,7 @@ sub init { } # Check for author my @clist; - foreach my $profile ("author", "editor", "manager", "default_tester", + foreach my $profile ("author", "manager", "default_tester", "assignee", "testedby"){ $t = $cgi->param($profile . "_type") || ''; if ($t eq "exact") { @@ -535,9 +677,9 @@ sub init { } # check static text fields - foreach my $f ("summary", "tcaction", "tceffect", "script", + foreach my $f ("case_summary", "summary", "tcaction", "tceffect", "script", "requirement", "name", "plan_text", "environment", - "notes") { + "notes", "env_value_selected") { if (defined $cgi->param($f)) { my $s = trim($cgi->param($f)); if ($s ne "") { @@ -557,6 +699,7 @@ sub init { push @wherepart, 'test_case_runs.iscurrent = 1'; } } + my @funcnames; while (@funcdefs) { my $key = shift(@funcdefs); diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm index 5b4e27b7d94..219068a6460 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm @@ -31,7 +31,7 @@ arguments: =over -=item type - one of 'case', 'plan', 'run', or 'caserun' +=item type - one of 'case', 'plan', 'run', 'caserun', or 'environment' =item url - the cgi file that is calling this @@ -135,6 +135,9 @@ sub init { 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); } @@ -248,7 +251,7 @@ sub save_list { (join(",", $self->{'id_list'}), $self->{'user'}->id, "__". $self->{'type'} ."__")); } else { - $dbh->do("INSERT INTO test_named_queries VALUES(?,?,?,?)", undef, + $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_unlock_tables(); @@ -344,8 +347,10 @@ sub get_next{ ############################### sub list { return $_[0]->{'list'}; } +sub id_list { return $_[0]->{'id_list'}; } sub list_count { return $_[0]->{'list_count'}; } sub page { return $_[0]->{'page'}; } +sub url_loc { return $_[0]->{'url_loc'}; } =head2 page_size @@ -405,6 +410,21 @@ sub get_url { 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 ."=". $val ."&"; + } + } + chop $qstring; + return $qstring +} + =head2 page_count Returns a total count of the number of pages returned by a query. diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm index 524e26204bf..6bd90112afb 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm @@ -24,7 +24,7 @@ # Christopher Aillon # Myk Melez # Jeff Hedlund -# Fr��ic Buclin +# Frederic Buclin # # Contributor(s): Greg Hendricks @@ -193,41 +193,26 @@ sub get_selectable_components { my $self = shift; my ($byid) = @_; my $dbh = Bugzilla->dbh; - my @compids; my @exclusions; unless ($byid) { foreach my $e (@{$self->components}){ push @exclusions, $e->{'id'}; } } - my $query = "SELECT id FROM components - WHERE product_id = ?"; + my $query = "SELECT DISTINCT id FROM components + WHERE product_id IN (" . join(",", @{$self->get_product_ids}) . ") "; if (@exclusions){ - $query .= " AND id NOT IN(". join(",", @exclusions) .")"; + $query .= "AND id NOT IN(". join(",", @exclusions) .") "; } - foreach my $p (@{$self->plans}){ - my $ref = $dbh->selectcol_arrayref( - $query, undef, $p->{'product_id'}); - push @compids, @{$ref}; - } - my %compseen; - foreach (@compids){ - $compseen{$_} = 1; - } - @compids = keys %compseen; - #TODO: 2.22 use Bugzilla::Component + $query .= "ORDER BY name"; + + my $ref = $dbh->selectcol_arrayref($query); my @comps; push @comps, {'id' => '0', 'name' => '--Please Select--'} unless $byid; - foreach my $id (@compids){ - my $ref = $dbh->selectrow_hashref( - "SELECT id, name FROM components - WHERE id = ?", undef, $id); - - push @comps, $ref; - #push @comps, Bugzilla::Component->new($id); + foreach my $id (@$ref){ + push @comps, Bugzilla::Component->new($id); } return \@comps; - } =head2 get_available_components @@ -275,35 +260,25 @@ plans referenced by this case. #TODO: Move to Testopia::Product.pm in 2.22 sub get_category_list{ my $self = shift; + my ($by_name) = @_; my $dbh = Bugzilla->dbh; - my @categories; + + my @product_ids; foreach my $p (@{$self->plans}){ - if (Bugzilla::Testopia::Util::can_view_product($p->product_id)){ - push @categories, @{$p->categories}; - } + push @product_ids, $p->product_id; } - my %seen; - foreach my $c (@categories){ - $seen{$c->id} = $c; + my $id_part; + my $name_part; + if ($by_name){ + $id_part = "cat.name AS id"; + $name_part = ", cat.name as name"; } - @categories = values %seen; - @categories = sort @categories; - return \@categories; -} - -=head2 get_distinct_categories - -Returns a list of all user viewable categories for use in searches - -=cut - -#TODO: Move to Testopia::Product.pm in 2.22 -sub get_distinct_categories{ - my $self = shift; - my $dbh = Bugzilla->dbh; - my $query = "SELECT DISTINCT cat.name AS id, cat.name " . - "FROM test_case_categories AS cat " . - "JOIN products ON cat.product_id = products.id " . + else { + $id_part = "category_id"; + } + my $query = "SELECT DISTINCT $id_part $name_part "; + $query .= "FROM test_case_categories AS cat " . + "JOIN products ON cat.product_id = products.id " . "LEFT JOIN group_control_map " . "ON group_control_map.product_id = products.id "; if (Param('useentrygroupdefault')) { @@ -316,11 +291,50 @@ sub get_distinct_categories{ $query .= "AND group_id NOT IN(" . join(',', values(%{Bugzilla->user->groups})) . ") "; } - $query .= "WHERE group_id IS NULL ORDER BY cat.name"; - - my $ref = $dbh->selectall_arrayref($query, {'Slice'=>{}}); + $query .= "WHERE group_id IS NULL "; + $query .= "AND cat.product_id IN (". join(",", @product_ids) . ") " unless ($by_name || !@product_ids); + $query .= "ORDER BY cat.name"; - return $ref; + if ($by_name){ + my $ref = $dbh->selectall_arrayref($query, {'Slice'=>{}}); + return $ref; + } + my $ids = $dbh->selectcol_arrayref($query); + + my @categories; + foreach my $c (@$ids){ + push @categories, Bugzilla::Testopia::Category->new($c); + } + return \@categories; +} + +=head2 get_product_ids + +Returns the list of product ids that this case is associated with + +=cut + +sub get_product_ids { + my $self = shift; + + if ($self->id == 0){ + my @ids; + foreach my $plan (@{$self->plans}){ + push @ids, $plan->product_id if Bugzilla->user->can_see_product($plan->product_name); + } + return \@ids; + } + + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT products.id FROM products + JOIN test_plans AS plans ON plans.product_id = products.id + JOIN test_case_plans ON plans.plan_id = test_case_plans.plan_id + JOIN test_cases AS cases ON cases.case_id = test_case_plans.case_id + WHERE cases.case_id = ? + ORDER BY products.id", undef, $self->{'case_id'}); + return $ref; } =head2 get_status_list @@ -373,8 +387,8 @@ sub add_tag { $dbh->bz_unlock_tables(); return 1; } - $dbh->do("INSERT INTO test_case_tags(tag_id, case_id) VALUES(?,?)", - undef, $tag_id, $self->{'case_id'}); + $dbh->do("INSERT INTO test_case_tags(tag_id, case_id, userid) VALUES(?,?,?)", + undef, $tag_id, $self->{'case_id'}, Bugzilla->user->id); $dbh->bz_unlock_tables(); return 0; @@ -396,6 +410,51 @@ sub remove_tag { return; } +=head2 attach_bug + +Attaches the specified bug to this test case + +=cut + +sub attach_bug { + my $self = shift; + my ($bug, $case_id) = @_; + $case_id ||= $self->{'case_id'}; + my $dbh = Bugzilla->dbh; + + $dbh->bz_lock_tables('test_case_bugs WRITE'); + my ($exists) = $dbh->selectrow_array( + "SELECT bug_id + FROM test_case_bugs + WHERE case_id=? + AND bug_id=?", + undef, ($case_id, $bug)); + if ($exists) { + $dbh->bz_unlock_tables(); + return; + } + $dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id, case_id) + VALUES(?,?,?)", undef, ($bug, undef, $self->{'case_id'})); + $dbh->bz_unlock_tables(); +} + +=head2 detach_bug + +Removes the association of the specified bug from this test case-run + +=cut + +sub detach_bug { + my $self = shift; + my ($bug) = @_; + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM test_case_bugs + WHERE bug_id = ? + AND case_id = ?", + undef, ($bug, $self->{'case_id'})); +} + =head2 add_component Associates a component with this test case @@ -465,14 +524,17 @@ text has changed and thus requiring a new version be created. sub diff_case_doc { my $self = shift; - my ($newaction, $neweffect) = @_; + my ($newaction, $neweffect, $newsetup, $newbreakdown) = @_; my $dbh = Bugzilla->dbh; - my ($oldaction, $oldeffect) = $dbh->selectrow_array( - "SELECT action, effect FROM test_case_texts - WHERE case_id = ? AND case_text_version = ?", - undef, $self->{'case_id'}, $self->version); + my ($oldaction, $oldeffect, $oldsetup, $oldbreakdown) = $dbh->selectrow_array( + "SELECT action, effect, setup, breakdown + FROM test_case_texts + WHERE case_id = ? AND case_text_version = ?", + undef, ($self->{'case_id'}, $self->version)); my $diff = diff(\$newaction, \$oldaction); $diff .= diff(\$neweffect, \$oldeffect); + $diff .= diff(\$newsetup, \$oldsetup); + $diff .= diff(\$newbreakdown, \$oldbreakdown); return $diff } @@ -516,7 +578,8 @@ sub store { my $key = $dbh->bz_last_key( 'test_cases', 'case_id' ); - $self->store_text($key, $self->{'author_id'}, $self->{'action'}, $self->{'effect'}, $timestamp); + $self->store_text($key, $self->{'author_id'}, $self->{'action'}, $self->{'effect'}, + $self->{'setup'}, $self->{'breakdown'}, $timestamp); $self->update_deps($self->{'dependson'}, $self->{'blocks'}, $key); foreach my $p (@{$self->{'plans'}}){ $self->link_plan($p->id, $key); @@ -535,16 +598,16 @@ author id, action text, effect text, and an optional timestamp. sub store_text { my $self = shift; my $dbh = Bugzilla->dbh; - my ($key, $author, $action, $effect, $timestamp) = @_; + my ($key, $author, $action, $effect, $setup, $breakdown, $timestamp) = @_; if (!defined $timestamp){ ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); } - trick_taint($action); - trick_taint($effect); my $version = $self->version || 0; - $dbh->do("INSERT INTO test_case_texts VALUES(?,?,?,?,?,?)", + $dbh->do("INSERT INTO test_case_texts + (case_id, case_text_version, who, creation_ts, action, effect, setup, breakdown) + VALUES(?,?,?,?,?,?,?,?)", undef, $key, ++$version, $author, - $timestamp, $action, $effect); + $timestamp, $action, $effect, $setup, $breakdown); $self->{'version'} = $version; return $self->{'version'}; @@ -579,6 +642,43 @@ sub link_plan { $dbh->do("INSERT INTO test_case_plans (plan_id, case_id) VALUES (?,?)", undef, $plan_id, $case_id); $dbh->bz_unlock_tables(); + + # Update the plans array to include new plan added. + + push @{$self->{'plans'}}, Bugzilla::Testopia::TestPlan->new($plan_id); + +} + + +=head2 unlink_plan + +Removes the link to the specified plan id. + +=cut + +sub unlink_plan { + my $self = shift; + my $dbh = Bugzilla->dbh; + my ($plan_id) = @_; + + $dbh->bz_lock_tables('test_case_plans WRITE', 'test_case_runs WRITE', + 'test_runs READ', 'test_plans READ'); + + if (!$self->can_unlink_plan($plan_id)){ + $dbh->bz_unlock_tables(); + return 0; + } + + $dbh->do("DELETE FROM test_case_plans + WHERE plan_id = ? + AND case_id = ?", undef, $plan_id, $self->{'case_id'}); + + $dbh->bz_unlock_tables(); + + # Update the plans array. + delete $self->{'plans'}; + + return 1; } =head2 copy @@ -604,7 +704,9 @@ sub copy { my $key = $dbh->bz_last_key( 'test_cases', 'case_id' ); if ($copydoc){ - $self->store_text($key, Bugzilla->user->id, $self->text->{'action'}, $self->text->{'effect'}, $timestamp); + $self->store_text($key, Bugzilla->user->id, $self->text->{'action'}, + $self->text->{'effect'}, $self->text->{'setup'}, + $self->text->{'breakdown'}, $timestamp); } return $key; @@ -621,15 +723,37 @@ sub check_alias { my $self = shift; my ($alias) = @_; + return unless $alias; + my $id = $self->{'case_id'} || ''; + my $dbh = Bugzilla->dbh; + ($id) = $dbh->selectrow_array( + "SELECT case_id + FROM test_cases + WHERE alias = ? + AND case_id != ?", + undef, ($alias, $id)); + + return $id; +} + +=head2 class_check_alias + +Checks if the given alias exists already. Returns the case_id of +the matching test case if it does. + +=cut + +sub class_check_alias { + my ($alias) = @_; + return unless $alias; my $dbh = Bugzilla->dbh; my ($id) = $dbh->selectrow_array( "SELECT case_id FROM test_cases - WHERE alias = ? - AND case_id != ?", - undef, ($alias, $self->{'case_id'})); + WHERE alias = ?", + undef, ($alias)); return $id; } @@ -662,6 +786,7 @@ sub update { # Update the history my $field_id = Bugzilla::Testopia::Util::get_field_id($field, "test_cases"); $dbh->do("INSERT INTO test_case_activity + (case_id, fieldid, who, changed, oldvalue, newvalue) VALUES(?,?,?,?,?,?)", undef, $self->{'case_id'}, $field_id, Bugzilla->user->id, $timestamp, $self->{$field}, $newvalues->{$field}); @@ -711,6 +836,21 @@ sub history { return $ref; } +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_case_activity + WHERE case_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; + +} + =head2 lookup_status Takes an ID of the status field and returns the value @@ -764,6 +904,23 @@ sub lookup_category { return $value; } +=head2 lookup_category_by_name + +Returns the id of the category name passed. + +=cut + +sub lookup_category_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT category_id + FROM test_case_categories + WHERE name = ?", + undef, $name); + return $value; +} + =head2 lookup_priority Takes an ID of the priority field and returns the value @@ -782,6 +939,24 @@ sub lookup_priority { return $value; } +=head2 lookup_priority_by_name + +Returns the id of the priority name passed. + +=cut + +sub lookup_priority_by_value { + my ($value) = @_; + my $dbh = Bugzilla->dbh; + my ($id) = $dbh->selectrow_array( + "SELECT id + FROM priority + WHERE value = ?", + undef, $value); + return $id; +} + + =head2 lookup_default_tester Takes an ID of the default_tester field and returns the value @@ -991,11 +1166,37 @@ sub _generate_dep_tree { my $deps = _get_dep_lists("dependson", "blocked", $case_id); return unless scalar @$deps; foreach my $id (@$deps){ - #print "ID $id"; $self->_generate_dep_tree($id); push @{$self->{'dep_list'}}, $id } } + +=head2 can_unlink_plan + +Returns true if this test case can be unlinked from the given plan + +=cut + +sub can_unlink_plan { + my $self = shift; + my ($plan_id) = @_; + + return 0 if (!UserInGroup('managetestplans')); + + return 0 if scalar @{$self->plans} < 2; + + my $dbh = Bugzilla->dbh; + my ($res) = $dbh->selectrow_array( + "SELECT 1 + FROM test_case_runs + INNER JOIN test_runs ON test_case_runs.run_id = test_runs.run_id + WHERE test_runs.plan_id = ? + AND test_case_runs.case_id = ?", + undef, ($plan_id, $self->{'case_id'})); + + return !$res; +} + =head2 canedit Returns true if the logged in user has rights to edit this test case. @@ -1221,16 +1422,19 @@ sub components { my $dbh = Bugzilla->dbh; return $self->{'components'} if exists $self->{'components'}; #TODO: 2.22 use Bugzilla::Component - my $comps = $dbh->selectall_arrayref( - "SELECT comp.id, comp.name + my $comps = $dbh->selectcol_arrayref( + "SELECT comp.id FROM components AS comp JOIN test_case_components AS tcc ON tcc.component_id = comp.id JOIN test_cases ON tcc.case_id = test_cases.case_id WHERE test_cases.case_id = ?", {'Slice' => {}}, $self->{'case_id'}); - - $self->{'components'} = $comps; + my @comps; + foreach my $id (@$comps){ + push @comps, Bugzilla::Component->new($id); + } + $self->{'components'} = \@comps; return $self->{'components'}; } @@ -1293,8 +1497,7 @@ sub bugs { 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 + FROM test_case_bugs WHERE case_id = ?", undef, $self->{'case_id'}); my @bugs; @@ -1317,11 +1520,12 @@ sub text { my $self = shift; my $dbh = Bugzilla->dbh; return $self->{'text'} if exists $self->{'text'}; - my $text = $dbh->selectrow_hashref("SELECT action, effect - FROM test_case_texts - WHERE case_id=? AND case_text_version=?", - undef, $self->{'case_id'}, - $self->version); + my $text = $dbh->selectrow_hashref( + "SELECT action, effect, setup, breakdown, who author_id, case_text_version AS version + FROM test_case_texts + WHERE case_id=? AND case_text_version=?", + undef, $self->{'case_id'}, + $self->version); $self->{'text'} = $text; return $self->{'text'}; } @@ -1409,7 +1613,21 @@ sub blocked_list { my $ref = _get_dep_lists("dependson", "blocked", $self->{'case_id'}); $self->{'blocked_list'} = join(",", @$ref); return $self->{'blocked_list'}; -} +} + +=head2 blocked_list_uncached + +Returns a space separated list of test cases that are blocked by this test case. +This method does not cache the blocked test cases so each call will result +in a database read. + +=cut + +sub blocked_list_uncached { + my ($self) = @_; + my $ref = _get_dep_lists("dependson", "blocked", $self->{'case_id'}); + return join(" ", @$ref) +} =head2 dependson @@ -1439,6 +1657,20 @@ sub dependson_list { return $self->{'dependson_list'}; } +=head2 dependson_list_uncached + +Returns a space separated list of test cases that depend on this test case. +This method does not cache the dependent test cases so each call will result +in a database read. + +=cut + +sub dependson_list_uncached { + my ($self) = @_; + my $ref = _get_dep_lists("blocked", "dependson", $self->{'case_id'}); + return join(" ", @$ref) +} + =head1 TODO diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCaseRun.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCaseRun.pm index da8836507fa..320e5dbaa46 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCaseRun.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCaseRun.pm @@ -50,6 +50,7 @@ use Bugzilla::User; use Bugzilla::Config; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::Constants; +use Date::Format; ############################### #### Initialization #### @@ -81,6 +82,7 @@ use constant DB_COLUMNS => qw( test_case_runs.case_run_status_id test_case_runs.case_text_version test_case_runs.build_id + test_case_runs.environment_id test_case_runs.notes test_case_runs.close_date test_case_runs.iscurrent @@ -161,10 +163,11 @@ sub store { my $self = shift; my $dbh = Bugzilla->dbh; $dbh->do("INSERT INTO test_case_runs ($columns) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", undef, + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", undef, (undef, $self->{'run_id'}, $self->{'case_id'}, $self->{'assignee'}, undef, IDLE, $self->{'case_text_version'}, - $self->{'build_id'}, $self->{'notes'}, undef, 1, 0)); + $self->{'build_id'}, $self->{'environment_id'}, + undef, undef, 1, 0)); my $key = $dbh->bz_last_key( 'test_case_runs', 'case_run_id' ); return $key; @@ -180,7 +183,10 @@ sub clone { my $self = shift; my ($fields) = @_; my $dbh = Bugzilla->dbh; - $self->{'build_id'} = $fields->{'build_id'}; + my $note = "Build or Environment changed. Resetting to IDLE."; + $self->append_note($note); + $self->{'build_id'} = $fields->{'build_id'} if $fields->{'build_id'}; + $self->{'environment_id'} = $fields->{'environment_id'} if $fields->{'environment_id'}; my $entry = $self->store; $self->set_as_current($entry); return $entry; @@ -188,28 +194,54 @@ sub clone { =head2 check_exists -Checks for an existing entry with the same build for this case and run -and returns the id if it is found +Checks for an existing entry with the same build and environment for this +case and run and returns the id if it is found =cut sub check_exists { my $self = shift; - my ($run_id, $case_id, $build_id) = @_; + my ($run_id, $case_id, $build_id, $env_id) = @_; $run_id ||= $self->{'run_id'}; $case_id ||= $self->{'case_id'}; $build_id ||= $self->{'build_id'}; - + $env_id ||= $self->{'environment_id'}; + my $dbh = Bugzilla->dbh; my ($is) = $dbh->selectrow_array( + "SELECT case_run_id + FROM test_case_runs + WHERE run_id = ? + AND case_id = ? + AND build_id = ? + AND environment_id = ?", + undef, ($run_id, $case_id, $build_id, $env_id)); + + return $is; + +} + +=head2 lookup_case_run_id + +Checks for an existing entry based on run, case, and build, +and returns the id if it is found + +=cut + +sub lookup_case_run_id +{ + my ($run_id, $case_id, $build_id) = @_; + + my $dbh = Bugzilla->dbh; + + my ($case_run_id) = $dbh->selectrow_array( "SELECT case_run_id FROM test_case_runs WHERE run_id = ? AND case_id = ? AND build_id = ?", undef, ($run_id, $case_id, $build_id)); - return $is; - + return $case_run_id; } =head2 update @@ -227,9 +259,9 @@ sub update { if ($self->is_closed_status($fields->{'case_run_status_id'})){ $fields->{'close_date'} = Bugzilla::Testopia::Util::get_time_stamp(); } - my ($is) = $self->check_exists($self->run_id, $self->case_id, $fields->{'build_id'}); + my ($is) = $self->check_exists($self->run_id, $self->case_id, $fields->{'build_id'}, $fields->{'environment_id'}); - if ($fields->{'build_id'} != $self->{'build_id'}){ + if ($fields->{'build_id'} != $self->{'build_id'} || $fields->{'environment_id'} != $self->{'environment_id'}){ if ($is){ return $is; } @@ -311,6 +343,19 @@ sub set_build { $self->{'build_id'} = $build_id; } +=head2 set_environment + +Sets the environment on a case-run + +=cut + +sub set_environment { + my $self = shift; + my ($env_id) = @_; + $self->_update_fields({'environment_id' => $env_id}); + $self->{'environment_id'} = $env_id; +} + =head2 set_status Sets the status on a case-run and updates the close_date and testedby @@ -356,17 +401,56 @@ sub set_assignee { $self->_update_fields({'assignee' => $user_id}); } -=head2 set_note +=head2 lookup_status + +Returns the status name of the given case_run_status_id + +=cut + +sub lookup_status { + my $self = shift; + my ($status_id) = @_; + my $dbh = Bugzilla->dbh; + my ($status) = $dbh->selectrow_array( + "SELECT name + FROM test_case_run_status + WHERE case_run_status_id = ?", + undef, $status_id); + return $status; +} + +=head2 lookup_status_by_name + +Returns the id of the status name passed. + +=cut + +sub lookup_status_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + + my ($value) = $dbh->selectrow_array( + "SELECT case_run_status_id + FROM test_case_run_status + WHERE name = ?", + undef, $name); + return $value; +} + +=head2 append_note Updates the notes field for the case-run =cut -sub set_note { +sub append_note { my $self = shift; - my ($note, $append) = @_; - if ($append){ - my $note = $self->notes . "\n" . $note; + my ($note) = @_; + return unless $note; + my $timestamp = time2str("%c", time()); + $note = "$timestamp: $note"; + if ($self->{'notes'}){ + $note = $self->{'notes'} . "\n" . $note; } $self->_update_fields({'notes' => $note}); $self->{'notes'} = $note; @@ -456,18 +540,35 @@ sub attach_bug { my $dbh = Bugzilla->dbh; $dbh->bz_lock_tables('test_case_bugs WRITE'); - my ($is) = $dbh->selectrow_array( + my ($exists) = $dbh->selectrow_array( "SELECT bug_id FROM test_case_bugs WHERE case_run_id=? AND bug_id=?", undef, ($caserun_id, $bug)); - if ($is) { + if ($exists) { $dbh->bz_unlock_tables(); return; } - $dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id) - VALUES(?,?)", undef, ($bug, $self->{'case_run_id'})); + my ($check) = $dbh->selectrow_array( + "SELECT bug_id + FROM test_case_bugs + WHERE case_id=? + AND bug_id=? + AND case_run_id=?", + undef, ($caserun_id, $bug, undef)); + + if ($check){ + $dbh->do("UPDATE test_case_bugs + SET test_case_run_id = ? + WHERE case_id = ? + AND bug_id = ?", + undef, ($bug, $self->{'case_run_id'})); + } + else{ + $dbh->do("INSERT INTO test_case_bugs (bug_id, case_run_id, case_id) + VALUES(?,?,?)", undef, ($bug, $self->{'case_run_id'}, $self->{'case_id'})); + } $dbh->bz_unlock_tables(); } @@ -569,7 +670,6 @@ sub run_id { return $_[0]->{'run_id'}; } sub testedby { return Bugzilla::User->new($_[0]->{'testedby'}); } sub assignee { return Bugzilla::User->new($_[0]->{'assignee'}); } sub case_text_version { return $_[0]->{'case_text_version'}; } -sub notes { return $_[0]->{'notes'}; } sub close_date { return $_[0]->{'close_date'}; } sub iscurrent { return $_[0]->{'iscurrent'}; } sub status_id { return $_[0]->{'case_run_status_id'}; } @@ -577,6 +677,25 @@ sub sortkey { return $_[0]->{'sortkey'}; } sub isprivate { return $_[0]->{'isprivate'}; } sub updated_deps { return $_[0]->{'updated_deps'}; } +=head2 notes + +Returns the cumulative notes of all caserun records of this case and run. + +=cut + +sub notes { + my $self = shift; + my $dbh = Bugzilla->dbh; + my $notes = $dbh->selectcol_arrayref( + "SELECT notes + FROM test_case_runs + WHERE case_id = ? AND run_id = ? + ORDER BY case_run_id", + undef,($self->case_id, $self->run_id)); + + return join("\n", @$notes); +} + =head2 run Returns the TestRun object that this case-run is associated with @@ -618,6 +737,19 @@ sub build { return $self->{'build'}; } +=head2 environment + +Returns the Build object that this case-run is associated with + +=cut + +sub environment { + my $self = shift; + return $self->{'environment'} if exists $self->{'environment'}; + $self->{'environment'} = Bugzilla::Testopia::Environment->new($self->{'environment_id'}); + return $self->{'environment'}; +} + =head2 status Looks up the status name of the associated status_id for this object @@ -817,7 +949,17 @@ Returns true if the logged in user has rights to delete this case-run. sub candelete { my $self = shift; - return $self->canedit && Param('allow-test-deletion'); + + return ($self->canedit + && Param('allow-test-deletion') + && scalar @{$self->get_case_run_list} == 1 + && $self->status eq 'IDLE'); +} + +sub do_delete { + my $self = shift; + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM test_case_runs WHERE case_run_id = ?", undef, $self->id); } =head1 SEE ALSO diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm index 86d681e0327..c9225e9853f 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm @@ -66,7 +66,6 @@ use base qw(Exporter); plan_id product_id author_id - editor_id type_id default_product_version name @@ -79,7 +78,6 @@ use constant DB_COLUMNS => qw( test_plans.plan_id test_plans.product_id test_plans.author_id - test_plans.editor_id test_plans.type_id test_plans.default_product_version test_plans.name @@ -158,13 +156,13 @@ sub store { my $dbh = Bugzilla->dbh; my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); $dbh->do("INSERT INTO test_plans ($columns) - VALUES (?,?,?,?,?,?,?,?,?)", + VALUES (?,?,?,?,?,?,?,?)", undef, (undef, $self->{'product_id'}, $self->{'author_id'}, - $self->{'editor_id'}, $self->{'type_id'}, - $self->{'default_product_version'}, $self->{'name'}, + $self->{'type_id'}, $self->{'default_product_version'}, $self->{'name'}, $timestamp, 1)); my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' ); $self->store_text($key, $self->{'author_id'}, $self->text, $timestamp); + $self->{'plan_id'} = $key; return $key; } @@ -186,7 +184,9 @@ sub store_text { $text ||= ''; trick_taint($text); my $version = $self->version || 0; - $dbh->do("INSERT INTO test_plan_texts VALUES(?,?,?,?,?)", + $dbh->do("INSERT INTO test_plan_texts + (plan_id, plan_text_version, who, creation_ts, plan_text) + VALUES(?,?,?,?,?)", undef, $key, ++$version, $author, $timestamp, $text); $self->{'version'} = $version; @@ -208,10 +208,9 @@ sub clone { my $dbh = Bugzilla->dbh; my ($timestamp) = Bugzilla::Testopia::Util::get_time_stamp(); $dbh->do("INSERT INTO test_plans ($columns) - VALUES (?,?,?,?,?,?,?,?,?)", + VALUES (?,?,?,?,?,?,?,?)", undef, (undef, $self->{'product_id'}, $self->{'author_id'}, - $self->{'editor_id'}, $self->{'type_id'}, - $self->{'default_product_version'}, $name, + $self->{'type_id'}, $self->{'default_product_version'}, $name, $timestamp, 1)); my $key = $dbh->bz_last_key( 'test_plans', 'plan_id' ); if ($store_doc){ @@ -241,6 +240,7 @@ sub toggle_archive { WHERE plan_id = ?", undef, $newvalue, $self->{'plan_id'}); my $field_id = Bugzilla::Testopia::Util::get_field_id("isactive", "test_plans"); $dbh->do("INSERT INTO test_plan_activity + (plan_id, fieldid, who, changed, oldvalue, newvalue) VALUES(?,?,?,?,?,?)", undef, ($self->{'plan_id'}, $field_id, Bugzilla->user->id, $timestamp, $oldvalue, $newvalue)); @@ -270,8 +270,8 @@ sub add_tag { $dbh->bz_unlock_tables(); return 1; } - $dbh->do("INSERT INTO test_plan_tags VALUES(?,?)", - undef, ($tag_id, $self->{'plan_id'})); + $dbh->do("INSERT INTO test_plan_tags(tag_id, plan_id, userid) VALUES(?,?,?)", + undef, ($tag_id, $self->{'plan_id'}), Bugzilla->user->id); $dbh->bz_unlock_tables(); } @@ -450,6 +450,103 @@ sub get_plan_types { ORDER BY name", {"Slice"=>{}}); return $types; + +} + +=head2 last_changed + +Returns the date of the last change in the history table + +=cut + +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_plan_activity + WHERE plan_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; +} + +=head2 lookup_plan_type + +Returns a type name matching the given type id + +=cut + +sub lookup_plan_type { + my $self = shift; + my $type_id = shift; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->selectrow_hashref( + "SELECT type_id AS id, name + FROM test_plan_types + WHERE type_id = ?", + undef, $type_id); + + return $type; +} + +=head2 check_plan_type + +Returns true if a type with the given name exists + +=cut + +sub check_plan_type { + my $self = shift; + my $name = shift; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->selectrow_hashref( + "SELECT 1 + FROM test_plan_types + WHERE name = ?", + undef, $name); + + return $type; +} + +=head2 update_plan_type + +Update the given type + +=cut + +sub update_plan_type { + my $self = shift; + my ($type_id, $name) = @_; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->do( + "UPDATE test_plan_types + SET name = ? + WHERE type_id = ?", + undef, ($name,$type_id)); + +} + +=head2 add_plan_type + +Add the given type + +=cut + +sub add_plan_type { + my $self = shift; + my ($name) = @_; + my $dbh = Bugzilla->dbh; + + my $type = $dbh->do( + "INSERT INTO test_plan_types (type_id, name) + VALUES(?,?)", + undef, (undef, $name)); } =head2 get_fields @@ -551,6 +648,7 @@ sub update { # Update the history my $field_id = Bugzilla::Testopia::Util::get_field_id($field, "test_plans"); $dbh->do("INSERT INTO test_plan_activity + (plan_id, fieldid, who, changed, oldvalue, newvalue) VALUES(?,?,?,?,?,?)", undef, ($self->{'plan_id'}, $field_id, Bugzilla->user->id, $timestamp, $self->{$field}, $newvalues->{$field})); @@ -609,6 +707,25 @@ sub lookup_type { undef, $id); return $value; } + +=head2 lookup_type_by_name + +Returns the id of the type name passed. + +=cut + +sub lookup_type_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + + my ($value) = $dbh->selectrow_array( + "SELECT type_id + FROM test_plan_types + WHERE name = ?", + undef, $name); + return $value; +} + =head2 lookup_status Takes an ID of the status field and returns the value @@ -665,9 +782,8 @@ Returns true if the logged in user has rights to view this plan sub canview { my $self = shift; - return $self->{'canview'} if exists $self->{'canview'}; - $self->{'canview'} = Bugzilla::Testopia::Util::can_view_product($self->product_id); - return $self->{'canview'}; + return 1 if (Bugzilla->user->id == $self->author->id); + return Bugzilla::Testopia::Util::can_view_product($self->product_id); } =head2 delete @@ -703,10 +819,6 @@ Returns the product version for this object Returns the product id for this object -=head2 editor - -Returns a Bugzilla::User object representing the plan's last editor - =head2 author Returns a Bugzilla::User object representing the plan author @@ -729,7 +841,6 @@ sub id { return $_[0]->{'plan_id'}; } sub creation_date { return $_[0]->{'creation_date'}; } sub product_version { return $_[0]->{'default_product_version'}; } sub product_id { return $_[0]->{'product_id'}; } -sub editor { return Bugzilla::User->new($_[0]->{'editor_id'}); } sub author { return Bugzilla::User->new($_[0]->{'author_id'}); } sub name { return $_[0]->{'name'}; } sub type_id { return $_[0]->{'type_id'}; } diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestRun.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestRun.pm index 5e8f248072c..65138e2f2ed 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestRun.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestRun.pm @@ -161,7 +161,7 @@ sub calculate_percent_completed { my $total = $idle + $run; my $percent; if ($total == 0) { - $percent = 100; + $percent = 0; } else { $percent = $run*100/$total; $percent = int($percent + 0.5); @@ -226,8 +226,8 @@ sub add_tag { $dbh->bz_unlock_tables; return 1; } - $dbh->do("INSERT INTO test_run_tags VALUES(?,?)", - undef, $tag_id, $self->{'run_id'}); + $dbh->do("INSERT INTO test_run_tags(tag_id, run_id, userid) VALUES(?,?,?)", + undef, $tag_id, $self->{'run_id'}, Bugzilla->user->id); $dbh->bz_unlock_tables; return 0; @@ -259,6 +259,7 @@ the test_case_runs table sub add_case_run { my $self = shift; my ($case_id) = @_; + return 0 if $self->check_case($case_id); my $case = Bugzilla::Testopia::TestCase->new($case_id); my $caserun = Bugzilla::Testopia::TestCaseRun->new({ 'run_id' => $self->{'run_id'}, @@ -266,6 +267,7 @@ sub add_case_run { 'assignee' => $case->default_tester->id, 'case_text_version' => $case->version, 'build_id' => $self->build->id, + 'environment_id' => $self->environment_id, }); $caserun->store; } @@ -318,6 +320,7 @@ sub update { # Update the history my $field_id = Bugzilla::Testopia::Util::get_field_id($field, "test_runs"); $dbh->do("INSERT INTO test_run_activity + (run_id, fieldid, who, changed, oldvalue, newvalue) VALUES(?,?,?,?,?,?)", undef, ($self->{'run_id'}, $field_id, Bugzilla->user->id, $timestamp, $self->{$field}, $newvalues->{$field})); @@ -400,6 +403,25 @@ sub history { return $ref; } + +=head2 Check_case + +Checks if the given test case is already associated with this run + +=cut + +sub check_case { + my $self = shift; + my ($case_id) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT case_run_id + FROM test_case_runs + WHERE case_id = ? AND run_id = ?", + undef, ($case_id, $self->{'run_id'})); + return $value; +} + =head2 lookup_environment Takes an ID of the envionment field and returns the value @@ -418,6 +440,23 @@ sub lookup_environment { return $value; } +=head2 lookup_environment_by_name + +Takes the name of an envionment and returns its id + +=cut + +sub lookup_environment_by_name { + my ($name) = @_; + my $dbh = Bugzilla->dbh; + my ($value) = $dbh->selectrow_array( + "SELECT environment_id + FROM test_environments + WHERE name = ?", + undef, $name); + return $value; +} + =head2 lookup_build Takes an ID of the build field and returns the value @@ -454,6 +493,84 @@ sub lookup_manager { return $value; } +=head2 last_changed + +Returns the date of the last change in the history table + +=cut + +sub last_changed { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my ($date) = $dbh->selectrow_array( + "SELECT MAX(changed) + FROM test_run_activity + WHERE run_id = ?", + undef, $self->id); + + return $self->{'creation_date'} unless $date; + return $date; +} + +sub filter_case_categories { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT tcc.category_id + FROM test_case_categories AS tcc + JOIN test_cases ON test_cases.category_id = tcc.category_id + JOIN test_case_runs AS tcr ON test_cases.case_id = tcr.case_id + WHERE run_id = ?", + undef, $self->id); + + my @categories; + foreach my $id (@$ids){ + push @categories, Bugzilla::Testopia::Category->new($id); + } + + return \@categories; +} + +sub filter_builds { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT build_id + FROM test_case_runs + WHERE run_id = ?", + undef, $self->id); + + my @builds; + foreach my $id (@$ids){ + push @builds, Bugzilla::Testopia::Build->new($id); + } + return \@builds; +} + +sub filter_components { + my $self = shift; + my $dbh = Bugzilla->dbh; + + my $ids = $dbh->selectcol_arrayref( + "SELECT DISTINCT components.id + FROM components + JOIN test_case_components AS tcc ON tcc.component_id = components.id + JOIN test_cases ON test_cases.case_id = tcc.case_id + JOIN test_case_runs AS tcr ON test_cases.case_id = tcr.case_id + WHERE run_id = ?", + undef, $self->id); + + my @components; + foreach my $id (@$ids){ + push @components, Bugzilla::Component->new($id); + } + + return \@components; +} + =head2 environments Returns a reference to a list of Testopia::Environment objects. @@ -960,6 +1077,39 @@ sub percent_complete { return $self->{'percent_complete'}; } +sub percent_passed { + my $self = shift; + my $notrun = $self->idle_count + $self->running_count + $self->paused_count + $self->failed_count + $self->blocked_count; + my $run = $self->passed_count; + $self->{'percent_passed'} = calculate_percent_completed($notrun, $run); + return $self->{'percent_passed'}; +} + +sub percent_failed { + my $self = shift; + my $notrun = $self->idle_count + $self->running_count + $self->paused_count + $self->passed_count + $self->blocked_count; + my $run = $self->failed_count; + $self->{'percent_failed'} = calculate_percent_completed($notrun, $run); + return $self->{'percent_failed'}; +} + +sub percent_blocked { + my $self = shift; + my $notrun = $self->idle_count + $self->running_count + $self->paused_count + $self->passed_count + $self->failed_count; + my $run = $self->blocked_count; + $self->{'percent_blocked'} = calculate_percent_completed($notrun, $run); + return $self->{'percent_blocked'}; +} + +sub percent_not_run { + my $self = shift; + my $notrun = $self->failed_count + $self->running_count + $self->paused_count + $self->passed_count; + my $run = $self->idle_count + $self->blocked_count; + $self->{'percent_not_run'} = calculate_percent_completed($notrun, $run); + return $self->{'percent_not_run'}; +} + + =head2 current_caseruns Returns a reference to a list of TestCaseRun objects that are the diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestTag.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestTag.pm index 7b3b76df48e..87ccd878722 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestTag.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestTag.pm @@ -173,7 +173,8 @@ sub store { undef, (undef, $self->{'tag_name'})); $key = $dbh->bz_last_key( 'test_tags', 'tag_id' ); $dbh->bz_unlock_tables(); - + + $self->{'tag_id'} = $key; return $key; } diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm index 86b2a1d5081..a05d3651d69 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm @@ -38,9 +38,9 @@ package Bugzilla::Testopia::Xml; #use fields qw(testplans testcases tags categories builds); use strict; +#use base qw(Exporter); use Bugzilla::Config; -use Bugzilla::Error; use Bugzilla::Product; use Bugzilla::Testopia::Attachment; use Bugzilla::Testopia::Build; @@ -54,6 +54,27 @@ use Bugzilla::Testopia::XmlTestCase; use Bugzilla::User; use Bugzilla::Util; +use constant AUTOMATIC => "AUTOMATIC"; +use constant BLOCKS => "blocks"; +our $DATABASE_DESCRIPTION = "Database_descripiton"; +our $DATABASE_ID = "Database_id"; +use constant IGNORECASE => 1; +use constant DEPENDSON => "dependson"; +use constant PCDATA => "#PCDATA"; +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_descripiton 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. @@ -62,13 +83,22 @@ struct ( 'Bugzilla::Testopia::Xml', { + # Array of attachments read for xml source. attachments => '@', - builds => '@', +#TODO builds => '@', + # Array of categories read from xml source. Categories categories => '@', tags => '@', - testenvironments => '@', +#TODO testenvironments => '@', + # Array of testcases read from xml source. testcases => '@', + # Hash of testcase aliases indexed by testcase id. 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 => '$', } ); @@ -76,7 +106,6 @@ struct #TODO: Add this to checksetup use Text::Diff; -#use base qw(Exporter); ############################### @@ -91,35 +120,11 @@ use Text::Diff; #### Methods #### ############################### -=head2 new - -Instantiate a new xml object. This takes a single argument - -Instantiate a new test plan. This takes a single argument -either a test plan ID or a reference to a hash containing keys -identical to a test plan's fields and desired values. - -=cut - -#sub new { -# my Bugzilla::Testopia::Xml $self = fields::new(ref($invocant) || $invocant); -# $self->{testplans} = []; -# $self->{testcases} = []; -# $self->{tags} = []; -# $self->{categories} = []; -# $self->{builds} = []; -# return $self; -#} - sub debug_display() { my ($self) = @_; - my $attachments_array_ref = $self->attachments; - my $categories_array_ref = $self->categories; - my $testcases_array_ref = $self->testcases; - my $testplans_array_ref = $self->testplans; - foreach my $category ( @$categories_array_ref ) + foreach my $category ( @{$self->categories}) { bless($category,"Bugzilla::Testopia::Category"); @@ -131,7 +136,7 @@ sub debug_display() print STDERR " Description " . $category->description() . "\n"; } - foreach my $testplan ( @$testplans_array_ref ) + foreach my $testplan ( @{$self->testplans} ) { bless($testplan,"Bugzilla::Testopia::TestPlan"); @@ -147,10 +152,6 @@ sub debug_display() print STDERR " Description " . $testplan->text() . "\n"; print STDERR " Default Product Version " . $testplan->product_version() . "\n"; print STDERR " Document " . $testplan->text() . "\n"; - my %editor = %{$testplan->editor()}; - my $editor_id = $editor{"id"}; - my $editor_login = $editor{"login"}; - print STDERR " Editor " . $editor_login . " (id=" . $editor_id . ", hash=" . $testplan->editor() . ")\n"; print STDERR " Is Active " . $testplan->isactive() . "\n"; print STDERR " Product " . $testplan->product_id() . "\n"; print STDERR " Type " . $testplan->type_id() . "\n"; @@ -173,13 +174,68 @@ sub debug_display() } } - foreach my $testcase ( @$testcases_array_ref ) + foreach my $testcase ( @{$self->testplans} ) { bless($testcase,"Bugzilla::Testopia::XmlTestCase"); $testcase->debug_display(); } } +=head2 entity_replace_testopia + +Replace testopia entities in the string supplied. + +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 + +< + +=cut + +sub entity_replace_testopia +{ + my ($string) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/$TESTOPIA_GT/>/g; + $string =~ s/$TESTOPIA_LT//g; + + return $string; +} + +=head2 entity_replace_xml + +Replace xml entities in the string supplied. + +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 &; format. These entities need to be converted back to their +character representation. + +=cut + +sub entity_replace_xml +{ + my ($string) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/$XML_GT/>/g; + $string =~ s/$XML_LT/new(); + + my $twig = XML::Twig->new( load_DTD => 1, keep_encoding => 1 ); + $twig->parse($xml); my $root = $twig->root; - my $attachments_array_ref = $self->attachments; - my $categories_array_ref = $self->categories; - my $testplans_array_ref = $self->testplans; - my $testcases_array_ref = $self->testcases; - foreach my $twig_category ($root->children('category')) { my $product = new Bugzilla::Product({name => $twig_category->field('product')}); @@ -216,7 +269,7 @@ sub parse() product_id => $product->id(), description => $twig_category->field('description'), }); - push @$categories_array_ref, $category; + push @{$self->categories}, $category; } my $testplan = Bugzilla::Testopia::TestPlan->new({ 'name' => 'dummy' }); @@ -230,7 +283,7 @@ sub parse() foreach my $twig_testplan ($root->children('testplan')) { - my $author = $twig_testplan->field('author'); + 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); @@ -245,50 +298,43 @@ sub parse() bless($author_user,"Bugzilla::User"); $author_id = $author_user->id(); } - my $editor = $twig_testplan->field('editor'); - my $editor_ref = Bugzilla::User::match($editor, 1, 0); - my $editor_id = -1; - if ( ! $editor_ref->[0] ) + + my $product_id = Bugzilla::Testopia::TestPlan::lookup_product_by_name($twig_testplan->field('product')); + if ( ! defined($product_id) ) { - $self->error("Cannot find editor '" . $editor . "' in test plan '" . $twig_testplan->field('name') . "'."); - } - else - { - my $editor_user = $editor_ref->[0]; - bless($editor_user,"Bugzilla::User"); - $editor_id = $editor_user->id(); + $self->error("Cannot find product '" . $twig_testplan->field('product') . "' in test plan '" . $twig_testplan->field('name') . "'."); } + $testplan = Bugzilla::Testopia::TestPlan->new({ - 'name' => $twig_testplan->field('name'), - 'product_id' => Bugzilla::Testopia::TestPlan::lookup_product_by_name($twig_testplan->field('product')), - 'default_product_version' => $twig_testplan->field('productversion'), - 'type_id' => $plantype_ids{$twig_testplan->field('type')}, - 'text' => $twig_testplan->field('document'), - 'author_id' => $author_id, - 'editor_id' => $editor_id, - 'isactive' => $twig_testplan->field('archive'), - 'creation_date' => $twig_testplan->field('created') + 'name' => entity_replace_xml($twig_testplan->field('name')), + 'product_id' => $product_id, + 'default_product_version' => entity_replace_xml($twig_testplan->field('productversion')), + '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->field('archive')), + 'creation_date' => entity_replace_xml($twig_testplan->field('created')) }); - push @$testplans_array_ref, $testplan; + push @{$self->testplans}, $testplan; my @tags = $twig_testplan->children('tag'); foreach my $twig_tag (@tags) { - push @{$self->tags}, $twig_tag->text(); + push @{$self->tags}, entity_replace_xml($twig_tag->text()); } my @attachments = $twig_testplan->children('attachment'); foreach my $twig_attachments (@attachments) { my $attachment = Bugzilla::Testopia::Attachment->new({ - 'description' => $twig_attachments->field('description'), - 'filename' => $twig_attachments->field('filename'), - 'submitter_id' => $twig_attachments->field('submitter'), - 'mime_type' => $twig_attachments->field('mimetype'), - 'creation_ts' => $twig_attachments->field('created'), - 'data' => $twig_attachments->field('data') + 'description' => entity_replace_xml($twig_attachments->field('description')), + 'filename' => entity_replace_xml($twig_attachments->field('filename')), + 'submitter_id' => entity_replace_xml($twig_attachments->field('submitter')), + 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype')), + 'creation_ts' => entity_replace_xml($twig_attachments->field('created')), + 'data' => entity_replace_xml($twig_attachments->field('data')) }); - push @$attachments_array_ref, $attachment; + push @{$self->attachments}, $attachment; #$testplan->add_tag($tag->id()); } } @@ -309,14 +355,15 @@ sub parse() } foreach my $twig_testcase ($root->children('testcase')) { - my $author = $twig_testcase->field('author'); + my $summary = entity_replace_xml($twig_testcase->field('summary')) || undef; + 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 '" . $twig_testcase->field('summary') . "'."); + $self->error("Cannot find author '" . $author . "' in test case '" . $summary . "'."); } else { @@ -324,14 +371,14 @@ sub parse() bless($author_user,"Bugzilla::User"); $author_id = $author_user->id(); } - my $tester = $twig_testcase->field('defaulttester'); + my $tester = entity_replace_xml($twig_testcase->field('defaulttester')); # 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 '" . $twig_testcase->field('summary') . "'."); + $self->error("Cannot find default tester '" . $tester . "' in test case '" . $summary . "'."); } else { @@ -339,74 +386,158 @@ sub parse() bless($tester_user,"Bugzilla::User"); $tester_id = $tester_user->id(); } - my $status_id = Bugzilla::Testopia::TestCase::lookup_status_by_name($twig_testcase->field('status')); - $self->error("Cannot find status '" . $twig_testcase->field('status') . "' in test case '" . $twig_testcase->field('summary') . "'.") if ( ! defined($status_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')) || undef; + $xml_testcase->testcase(Bugzilla::Testopia::TestCase->new({ - 'action' => $twig_testcase->field('action'), - 'alias' => $twig_testcase->field('alias') || undef, - 'arguments' => $twig_testcase->field('arguments'), + 'action' => entity_replace_testopia($twig_testcase->field('action')), + 'alias' => $alias, + 'arguments' => entity_replace_xml($twig_testcase->field('arguments')), 'author_id' => $author_id, - #TODO: Blocks 'blocks' => undef, - + 'breakdown' => entity_replace_testopia($twig_testcase->field('breakdown')), 'case_status_id' => $status_id, 'category_id' => undef, - 'creation_date' => $twig_testcase->field('created'), + 'creation_date' => $twig_testcase->att('created'), 'default_tester_id' => $tester_id, - #TODO: Depends On 'dependson' => undef, - 'effect' => $twig_testcase->field('effect'), - 'expectedresults' => $twig_testcase->field('expectedresults'), - 'isautomated' => $twig_testcase->field('isautomated'), + 'effect' => entity_replace_testopia($twig_testcase->field('expectedresults')), + 'isautomated' => ( uc $twig_testcase->att('automated') ) eq AUTOMATIC, 'plans' => undef, - 'priority_id' => $priority_ids{$twig_testcase->field('priority')}, - 'requirement' => $twig_testcase->field('requirement'), - 'script' => $twig_testcase->field('script'), - 'summary' => $twig_testcase->field('summary'), + 'priority_id' => $priority_ids{$twig_testcase->att('priority')}, + 'requirement' => entity_replace_xml($twig_testcase->field('requirement')), + 'setup' => entity_replace_testopia($twig_testcase->field('setup')), + 'script' => entity_replace_xml($twig_testcase->field('script')), + 'summary' => $summary, })); - # Action field is html format. - my $action = $twig_testcase->field('action'); - $action =~ s/\n/
/g; - $xml_testcase->action($action); - $xml_testcase->category($twig_testcase->field('category')); - # Expectedresults field is html format. - my $expectedresults = $twig_testcase->field('expectedresults'); - $expectedresults =~ s/\n/
/g; - $xml_testcase->expectedresults($expectedresults); - push @$testcases_array_ref, $xml_testcase; - + 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 ( ! $xml_testcase->testplan->add($twig_testplan_reference->att('type'),entity_replace_xml($testplan_reference)) ) + { + $self->error("Do not know how to handle test plan of type '" + . $twig_testplan_reference->att('type') + . "' in test case '" . $twig_testcase->field('summary') . "'." + . "\nKnow 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')),$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. + $xml_testcase->category(entity_replace_xml($twig_testcase->field('categoryname'))); + my @tags = $twig_testcase->children('tag'); - foreach my $twig_tag (@tags) + foreach my $twig_tag ( @tags ) { - $xml_testcase->add_tag($twig_tag->text()); + $xml_testcase->add_tag(entity_replace_xml($twig_tag->text())); } - #my @components; - #foreach my $id (@comps){ - # detaint_natural($id); - # validate_selection($id, 'id', 'components'); - # push @components, $id; - #} - - #$case->add_component($_) foreach (@components); + my @components = $twig_testcase->children('component'); + foreach my $twig_component ( @components ) + { + $xml_testcase->add_component(entity_replace_xml($twig_component->text())); + } + + 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))) ) + { + $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))) ) + { + $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()) . "'.") + } + } + } + + # + # 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); + } } - #TODO Verfication of data. - #Test cases require a category or store will fail. - - # Don't really care about the value of parse_error. If it has been defined then a error occurred - # parsing the XML. + # + # 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 ) { -# $self->debug_display(); - my @testplanarray; - foreach my $testplan ( @{$self->testplans} ) { my $plan_id = $testplan->store(); $testplan->{'plan_id'} = $plan_id; + print "Created Test Plan $plan_id: " . $testplan->name() . "\n"; foreach my $asciitag ( @{$self->tags} ) { my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag}); @@ -414,20 +545,30 @@ sub parse() $testplan->{'tag_id'} = $tagid; $testplan->add_tag($tagid); } - - push @testplanarray, $testplan; } + # Store the testcases. foreach my $testcase ( @{$self->testcases} ) { bless($testcase,"Bugzilla::Testopia::XmlTestCase"); - my $result = $testcase->store($testplan->{'author_id'},@testplanarray); + my $result = $testcase->store(@{$self->testplans}); if ( $result ne "" ) { $self->error($result); } + else + { + print "Created Test Case " . $testcase->testcase->id() . ": " . $testcase->testcase->summary() . "\n"; + } + } + # 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; } diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm new file mode 100644 index 00000000000..27ac20acd36 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm @@ -0,0 +1,101 @@ +# -*- 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 + +=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; + + +=cut + +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 $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; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm index dc6c21f3726..b12b7280856 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm @@ -48,6 +48,7 @@ use Bugzilla::Testopia::TestPlan; use Bugzilla::Testopia::TestRun; use Bugzilla::Testopia::TestTag; use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::XmlReferences; use Bugzilla::User; use Bugzilla::Util; @@ -62,129 +63,224 @@ struct ( 'Bugzilla::Testopia::XmlTestCase', { - action => '$', - blocks => '@', - category => '$', - dependson => '@', - expectedresults => '$', - tags => '@', - testcase => 'Bugzilla::Testopia::TestCase', +# action => '$', + blocks => 'Bugzilla::Testopia::XmlReferences', + category => '$', + components => '@', + dependson => 'Bugzilla::Testopia::XmlReferences', +# expectedresults => '$', + tags => '@', + testcase => 'Bugzilla::Testopia::TestCase', + testplan => 'Bugzilla::Testopia::XmlReferences', } ); +sub add_tag() +{ + my ($self,$tag) = @_; + + push @{$self->tags}, $tag; +} + +sub add_component() +{ + my ($self,$component) = @_; + + push @{$self->components}, $component; +} + 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"; - my @results = split(/\n/,$self->action); + 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"; - foreach my $loop ( @{$self->blocks} ) - { - $display_variable .= $loop . ","; - } - chop $display_variable; - print STDERR " Blocks " . $display_variable . "\n"; - $display_variable = ""; - print STDERR " Category " . $self->category . "\n"; - print STDERR " Creation Date " . $self->testcase->creation_date() . "\n"; - foreach my $loop ( @{$self->blocks} ) - { - $display_variable .= $loop . ","; - } - chop $display_variable; - print STDERR " Dependson " . $display_variable . "\n"; - $display_variable = ""; - print STDERR " Expected Results\n"; - @results = split(/\n/,$self->expectedresults); + } + 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"; + $display_variable = $self->testcase->creation_date(); + if ( defined $display_variable ) + { + print STDERR " Creation Date " . $display_variable . "\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 " 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"; + 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"; - } + foreach my $tag (@{$self->tags}) + { + print STDERR " Tag " . $tag . "\n"; + } - my @attachments = @{$self->testcase->attachments()}; - foreach my $attachment (@attachments) - { - my %submitter = %{$self->testcase->submitter()}; - my $author_login = $author{"login"}; - print STDERR " Attachment " . $attachment->description() . "\n"; - print STDERR " Creation Date " . $attachment->creation_ts(). "\n"; - print STDERR " Filename " . $attachment->filename() . "\n"; - print STDERR " Mime Type " . $attachment->mime_type(). "\n"; - print STDERR " Submitter " . $author_login . "\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 " Creation Date " . $attachment->creation_ts(). "\n"; + print STDERR " Filename " . $attachment->filename() . "\n"; + print STDERR " Mime Type " . $attachment->mime_type(). "\n"; + print STDERR " Submitter " . $author_login . "\n"; + } } -sub add_tag() +sub get_testcase_ids() { - my ($self,$tag) = @_; - - push @{$self->tags}, $tag; + 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->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, $userid, @testplans) = @_; + my ($self, @new_testplans) = @_; + my $error_message = ""; + my @testplan_id = @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)}; - # - # Following code pulled from Bugzilla::Testopia::TestCase->get_category_list(). - # Cannot call Bugzilla::Testopia::TestCase->get_category_list() until we create a - # TestCase but cannot create a TestCase without a category. - # - my @categories; - foreach my $testplan (@testplans) + # 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)} ) { - push @categories, @{$testplan->categories}; - } - my $categoryid = -1; - foreach my $category (@categories) - { - my %temphash = %{$category}; - if ( $self->category eq $temphash{"name"} ) + foreach my $testplan (@new_testplans) { - $categoryid = $temphash{"category_id"}; - last; + push @testplan_id, $testplan->id() if ( $testplan->name() eq $testplan_name ); } - } - return "Cannot save test case '" . $self->testcase->summary . "', Category '" . $self->category . "' is not valid." if ( $categoryid == -1 ); - $self->testcase->{'category_id'} = $categoryid; + } + #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 $categoryid = -1; + + push my @categories, @{$testplan->categories}; + foreach my $category (@categories) + { + if ( $category->name eq $self->category ) + { + $categoryid = $category->id; + last; + } + } + if ( $categoryid == -1 ) + { + my $new_category = Bugzilla::Testopia::Category->new({ + product_id => $testplan->product_id, + name => $self->category, + description => "FIX ME. Created during Test Plan import." + }); + $categoryid = $new_category->store(); + } + $self->testcase->{'category_id'} = $categoryid if ( ! defined($self->testcase->{'category_id'}) ); + } my $case_id = $self->testcase->store(); $self->testcase->{'case_id'} = $case_id; foreach my $asciitag ( @{$self->tags} ) @@ -193,13 +289,33 @@ sub store() my $tagid = $classtag->store; $self->testcase->add_tag($tagid); } - foreach my $testplan ( @testplans ) + + # Link the testcase to each of it's testplans. + foreach my $testplan ( @testplan ) { $self->testcase->link_plan($testplan->id(),$case_id) } - $self->testcase->store_text($case_id,$userid,$self->action,$self->expectedresults); - return ""; + # Code below requires the testplans to be linked into testcases before being run. + foreach my $component ( @{$self->components} ) + { + foreach my $available_component ( @{$self->testcase->get_selectable_components()} ) + { + $self->testcase->add_component($available_component->{'id'}) if ( $component eq $available_component->{'name'} ); + } + } + + return $error_message; +} + +sub store_relationships() +{ + my ($self, @new_testcases) = @_; + + my $blocks = $self->testcase->blocked_list_uncached() . " " . join(' ',$self->get_testcase_ids("blocks",@new_testcases)); + my $dependson = $self->testcase->dependson_list_uncached() . " " . join(' ',$self->get_testcase_ids("dependson",@new_testcases)); + + $self->testcase->update_deps($dependson,$blocks); } 1; diff --git a/mozilla/webtools/testopia/Bugzilla/WebService/Constants.pm b/mozilla/webtools/testopia/Bugzilla/WebService/Constants.pm new file mode 100644 index 00000000000..df86986bd59 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/WebService/Constants.pm @@ -0,0 +1,36 @@ +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann + +package Bugzilla::WebService::Constants; + +use strict; +use base qw(Exporter); + +@Bugzilla::WebService::Constants::EXPORT = qw( + ERROR_AUTH_NODATA + ERROR_UNIMPLEMENTED + ERROR_GENERAL + ERROR_FAULT_SERVER +); + +use constant ERROR_AUTH_NODATA => 410; +use constant ERROR_UNIMPLEMENTED => 910; +use constant ERROR_GENERAL => 999; + +# RPC Fault Code must be an integer +use constant ERROR_FAULT_SERVER => 998; + +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCase.pm b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCase.pm new file mode 100644 index 00000000000..6fc93d4fa8b --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCase.pm @@ -0,0 +1,257 @@ +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +package Bugzilla::WebService::Testopia::TestCase; + +use strict; +use base qw(Bugzilla::WebService); +use Bugzilla::User; +use Bugzilla::Util qw(detaint_natural); +use Bugzilla::Testopia::TestCase; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + + +# Convert string field values to their respective integer id's +sub _convert_to_ids +{ + my ($hash) = @_; + + if (defined($$hash{"author"})) + { + $$hash{"author_id"} = login_to_id($$hash{"author"}); + } + delete $$hash{"author"}; + + if (defined($$hash{"case_status"})) + { + $$hash{"case_status_id"} = Bugzilla::Testopia::TestCase::lookup_status_by_name($$hash{"case_status"}); + } + delete $$hash{"case_status"}; + + if (defined($$hash{"category"})) + { + $$hash{"category_id"} = Bugzilla::Testopia::TestCase::lookup_category_by_name($$hash{"category"}); + } + delete $$hash{"category"}; + + if (defined($$hash{"default_tester"})) + { + $$hash{"default_tester_id"} = login_to_id($$hash{"default_tester"}); + } + delete $$hash{"default_tester"}; + + if (defined($$hash{"priority"})) + { + $$hash{"priority_id"} = Bugzilla::Testopia::TestCase::lookup_priority_by_value($$hash{"priority"}); + } + delete $$hash{"priority"}; + } + +# Convert fields with integer id's to their respective string values +sub _convert_to_strings +{ + my ($hash) = @_; + + $$hash{"author"} = ""; + if (defined($$hash{"author_id"})) + { + $$hash{"author"} = new Bugzilla::User($$hash{"author_id"})->login; + } + delete $$hash{"author_id"}; + + $$hash{"case_status"} = ""; + if (defined($$hash{"case_status_id"})) + { + $$hash{"case_status"} = Bugzilla::Testopia::TestCase->lookup_status($$hash{"case_status_id"}); + } + delete $$hash{"case_status_id"}; + + $$hash{"category"} = ""; + if (defined($$hash{"category_id"})) + { + $$hash{"category"} = Bugzilla::Testopia::TestCase->lookup_category($$hash{"category_id"}); + } + delete $$hash{"category_id"}; + + $$hash{"default_tester"} = ""; + if (defined($$hash{"default_tester_id"})) + { + $$hash{"default_tester"} = new Bugzilla::User($$hash{"default_tester_id"})->login; + } + delete $$hash{"default_tester_id"}; + + $$hash{"priority"} = ""; + if (defined($$hash{"priority_id"})) + { + $$hash{"priority"} = Bugzilla::Testopia::TestCase->lookup_priority($$hash{"priority_id"}); + } + delete $$hash{"priority_id"}; + } + +# Utility method called by the list method +sub _list +{ + my ($query) = @_; + + my $cgi = Bugzilla->cgi; + + $cgi->param("viewall", 1); + + $cgi->param("current_tab", "case"); + + foreach (keys(%$query)) + { + $cgi->param($_, $$query{$_}); + } + + my $search = Bugzilla::Testopia::Search->new($cgi); + + # Result is an array of test plan hash maps + return Bugzilla::Testopia::Table->new('case', + 'tr_xmlrpc.cgi', + $cgi, + undef, + $search->query() + )->list(); +} + +sub get +{ + my $self = shift; + my ($test_case_id) = @_; + + Bugzilla->login; + + # We can detaint immediately if what we get passed is fully numeric. + # We leave bug alias checks to Bugzilla::Testopia::TestCase::new. + + if ($test_case_id =~ /^[0-9]+$/) { + detaint_natural($test_case_id); + } + + #Result is a test case hash map + my $testcase = new Bugzilla::Testopia::TestCase($test_case_id); + + _convert_to_strings($testcase); + + Bugzilla->logout; + + return $testcase; +} + +sub list +{ + Bugzilla->login; + + my $self = shift; + my ($query) = @_; + + _convert_to_ids($query); + + my $list = _list($query); + + foreach (@$list) + { + _convert_to_strings($_); + } + + Bugzilla->logout; + + return $list; +} + +sub create +{ + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login; + + _convert_to_ids($new_values); + + my $test_case = new Bugzilla::Testopia::TestCase($new_values); + + Bugzilla->logout; + + # Result is new test plan id + return $test_case->store(); +} + +sub update +{ + my $self =shift; + my ($test_case_id, $new_values) = @_; + + Bugzilla->login; + + my $test_case = new Bugzilla::Testopia::TestCase($test_case_id); + + _convert_to_ids($new_values); + + $test_case->update($new_values); + + Bugzilla->logout; + + # Result is zero on success, otherwise an exception will be thrown + return 0; +} + +sub get_text +{ + my $self =shift; + my ($test_case_id) = @_; + + Bugzilla->login; + + my $test_case = new Bugzilla::Testopia::TestCase($test_case_id); + + my $doc = $test_case->text(); + + $$doc{"author"} = ""; + if (defined($$doc{"author_id"})) + { + $$doc{"author"} = new Bugzilla::User($$doc{"author_id"})->login; + } + delete $$doc{"author_id"}; + + Bugzilla->logout; + + #Result is the latest test case doc hash map + return $doc; +} + +sub store_text +{ + my $self =shift; + my ($test_case_id, $author, $action, $effect) = @_; + + Bugzilla->login; + + my $author_id = login_to_id($author); + + my $test_case = new Bugzilla::Testopia::TestCase($test_case_id); + + my $version = $test_case->store_text($test_case_id, $author_id, $action, $effect); + + Bugzilla->logout; + + # Result is new test case doc version on success, otherwise an exception will be thrown + return $version; +} + +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCaseRun.pm b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCaseRun.pm new file mode 100644 index 00000000000..0053371f0f8 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestCaseRun.pm @@ -0,0 +1,186 @@ +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +package Bugzilla::WebService::Testopia::TestCaseRun; + +use strict; +use base qw(Bugzilla::WebService); +use Bugzilla::Util qw(detaint_natural); +use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::User; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +# Convert string field values to their respective integer id's +sub _convert_to_ids +{ + my ($hash) = @_; + + if (defined($$hash{"assignee"})) + { + $$hash{"assignee"} = login_to_id($$hash{"assignee"}); + } + + if (defined($$hash{"testedby"})) + { + $$hash{"testedby"} = login_to_id($$hash{"testedby"}); + } + + if (defined($$hash{"case_run_status"})) + { + $$hash{"case_run_status_id"} = Bugzilla::Testopia::TestCaseRun::lookup_status_by_name($$hash{"case_run_status"}); + } + delete $$hash{"case_run_status"}; +} + +# Convert fields with integer id's to their respective string values +sub _convert_to_strings +{ + my ($hash) = @_; + + if (defined($$hash{"assignee"})) + { + $$hash{"assignee"} = new Bugzilla::User($$hash{"assignee"})->login; + } + + if (defined($$hash{"testedby"})) + { + $$hash{"testedby"} = new Bugzilla::User($$hash{"testedby"})->login; + } + + $$hash{"case_run_status"} = ""; + if (defined($$hash{"case_run_status_id"})) + { + $$hash{"case_run_status"} = Bugzilla::Testopia::TestCaseRun->lookup_status($$hash{"case_run_status_id"}); + } + delete $$hash{"case_run_status_id"}; +} + +# Utility method called by the list method +sub _list +{ + my ($query) = @_; + + my $cgi = Bugzilla->cgi; + + $cgi->param("viewall", 1); + + $cgi->param("current_tab", "case_run"); + + foreach (keys(%$query)) + { + $cgi->param($_, $$query{$_}); + } + + my $search = Bugzilla::Testopia::Search->new($cgi); + + # Result is an array of test case run hash maps + return Bugzilla::Testopia::Table->new('case_run', + 'tr_xmlrpc.cgi', + $cgi, + undef, + $search->query() + )->list(); +} + +sub get +{ + my $self = shift; + my ($test_case_run_id) = @_; + + Bugzilla->login; + + # We can detaint immediately if what we get passed is fully numeric. + # We leave bug alias checks to Bugzilla::Testopia::TestCaseRun::new. + + if ($test_case_run_id =~ /^[0-9]+$/) { + detaint_natural($test_case_run_id); + } + + #Result is a test case run hash map + my $test_case_run = new Bugzilla::Testopia::TestCaseRun($test_case_run_id); + + _convert_to_strings($test_case_run); + + Bugzilla->logout; + + return $test_case_run; + +} + +sub list +{ + Bugzilla->login; + + my $self = shift; + my ($query) = @_; + + _convert_to_ids($query); + + my $list = _list($query); + + foreach (@$list) + { + _convert_to_strings($_); + } + + Bugzilla->logout; + + return $list; +} + +sub create +{ + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login; + + _convert_to_ids($new_values); + + my $test_case_run = new Bugzilla::Testopia::TestCaseRun($new_values); + + Bugzilla->logout; + + # Result is new test case run id + return $test_case_run->store(); +} + +sub update +{ + my $self =shift; + my ($run_id, $case_id, $build_id, $new_values) = @_; + + Bugzilla->login; + + my $test_case_run_id = Bugzilla::Testopia::TestCaseRun::lookup_case_run_id($run_id, $case_id, $build_id); + + my $test_case_run = new Bugzilla::Testopia::TestCaseRun($test_case_run_id); + + _convert_to_ids($new_values); + + $$new_values{build_id} = $build_id; # Needed by TestCaseRun's update member function + + $test_case_run->update($new_values); + + Bugzilla->logout; + + # Result is zero on success, otherwise an exception will be thrown + return 0; +} + +1; diff --git a/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestPlan.pm b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestPlan.pm new file mode 100644 index 00000000000..e079b6fb5e2 --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestPlan.pm @@ -0,0 +1,190 @@ +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +package Bugzilla::WebService::Testopia::TestPlan; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Util qw(detaint_natural); +use Bugzilla::Product; +use Bugzilla::User; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +# Convert string field values to their respective integer id's +sub _convert_to_ids +{ + my ($hash) = @_; + + if (defined($$hash{"author"})) + { + $$hash{"author_id"} = login_to_id($$hash{"author"}); + } + delete $$hash{"author"}; + if (defined($$hash{"product"})) + { + my $product = Bugzilla::Product::check_product($$hash{"product"}); + $$hash{"product_id"} = $product->id; + } + delete $$hash{"product"}; + + if (defined($$hash{"type"})) + { + $$hash{"type_id"} = Bugzilla::Testopia::TestPlan::lookup_type_by_name($$hash{"type"}); + } + delete $$hash{"type"}; +} + +# Convert fields with integer id's to their respective string values +sub _convert_to_strings +{ + my ($hash) = @_; + + $$hash{"author"} = ""; + if (defined($$hash{"author_id"})) + { + $$hash{"author"} = new Bugzilla::User($$hash{"author_id"})->login; + } + delete $$hash{"author_id"}; + + $$hash{"product"} = ""; + if (defined($$hash{"product_id"})) + { + $$hash{"product"} = new Bugzilla::Product($$hash{"product_id"})->name; + } + delete $$hash{"product_id"}; + + $$hash{"type"} = ""; + if (defined($$hash{"type_id"})) + { + $$hash{"type"} = Bugzilla::Testopia::TestPlan->lookup_type($$hash{"type_id"}); + } + delete $$hash{"type_id"}; +} + +# Utility method called by the list method +sub _list +{ + my ($query) = @_; + + my $cgi = Bugzilla->cgi; + + $cgi->param("viewall", 1); + + $cgi->param("current_tab", "plan"); + + foreach (keys(%$query)) + { + $cgi->param($_, $$query{$_}); + } + + my $search = Bugzilla::Testopia::Search->new($cgi); + + # Result is an array of test plan hash maps + return Bugzilla::Testopia::Table->new('plan', + 'tr_xmlrpc.cgi', + $cgi, + undef, + $search->query() + )->list(); +} + +sub get +{ + my $self = shift; + my ($test_plan_id) = @_; + + Bugzilla->login; + + # We can detaint immediately if what we get passed is fully numeric. + # We leave bug alias checks to Bugzilla::Testopia::TestPlan::new. + + if ($test_plan_id =~ /^[0-9]+$/) { + detaint_natural($test_plan_id); + } + + #Result is a test plan hash map + my $testplan = new Bugzilla::Testopia::TestPlan($test_plan_id); + + _convert_to_strings($testplan); + + Bugzilla->logout; + + return $testplan; +} + +sub list +{ + Bugzilla->login; + + my $self = shift; + my ($query) = @_; + + _convert_to_ids($query); + + my $list = _list($query); + + foreach (@$list) + { + _convert_to_strings($_); + } + + Bugzilla->logout; + + return $list; +} + +sub create +{ + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login; + + _convert_to_ids($new_values); + + my $test_plan = new Bugzilla::Testopia::TestPlan($new_values); + + Bugzilla->logout; + + # Result is new test plan id + return $test_plan->store(); +} + +sub update +{ + my $self =shift; + my ($test_plan_id, $new_values) = @_; + + Bugzilla->login; + + my $test_plan = new Bugzilla::Testopia::TestPlan($test_plan_id); + + _convert_to_ids($new_values); + + $test_plan->update($new_values); + + Bugzilla->logout; + + # Result is zero on success, otherwise an exception will be thrown + return 0; +} + +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestRun.pm b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestRun.pm new file mode 100644 index 00000000000..23769ba137e --- /dev/null +++ b/mozilla/webtools/testopia/Bugzilla/WebService/Testopia/TestRun.pm @@ -0,0 +1,177 @@ +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +package Bugzilla::WebService::Testopia::TestRun; + +use strict; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Util qw(detaint_natural); +use Bugzilla::Product; +use Bugzilla::User; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; + +# Convert string field values to their respective integer id's +sub _convert_to_ids +{ + my ($hash) = @_; + + if (defined($$hash{"manager"})) + { + $$hash{"manager_id"} = login_to_id($$hash{"manager"}); + } + delete $$hash{"manager"}; + + if (defined($$hash{"environment"})) + { + $$hash{"environment_id"} = Bugzilla::Testopia::TestRun::lookup_environment_by_name($$hash{"environment"}); + } + delete $$hash{"environment"}; +} + +# Convert fields with integer id's to their respective string values +sub _convert_to_strings +{ + my ($hash) = @_; + + $$hash{"manager"} = ""; + if (defined($$hash{"manager_id"})) + { + $$hash{"manager"} = new Bugzilla::User($$hash{"manager_id"})->login; + } + delete $$hash{"manager_id"}; + + $$hash{"environment"} = ""; + if (defined($$hash{"environment_id"})) + { + $$hash{"environment"} = Bugzilla::Testopia::TestRun->lookup_environment($$hash{"environment_id"}); + } + delete $$hash{"environment_id"}; +} + +# Utility method called by the list method +sub _list +{ + my ($query) = @_; + + my $cgi = Bugzilla->cgi; + + $cgi->param("viewall", 1); + + $cgi->param("current_tab", "run"); + + foreach (keys(%$query)) + { + $cgi->param($_, $$query{$_}); + } + + my $search = Bugzilla::Testopia::Search->new($cgi); + + # Result is an array of test run hash maps + return Bugzilla::Testopia::Table->new('run', + 'tr_xmlrpc.cgi', + $cgi, + undef, + $search->query() + )->list(); +} + +sub get +{ + my $self = shift; + my ($test_run_id) = @_; + + Bugzilla->login; + + # We can detaint immediately if what we get passed is fully numeric. + # We leave bug alias checks to Bugzilla::Testopia::TestRun::new. + + if ($test_run_id =~ /^[0-9]+$/) { + detaint_natural($test_run_id); + } + + #Result is a test run hash map + my $testrun = new Bugzilla::Testopia::TestRun($test_run_id); + + _convert_to_strings($testrun); + + Bugzilla->logout; + + return $testrun; +} + +sub list +{ + Bugzilla->login; + + my $self = shift; + my ($query) = @_; + + _convert_to_ids($query); + + my $list = _list($query); + + foreach (@$list) + { + _convert_to_strings($_); + } + + Bugzilla->logout; + + return $list; +} + +sub create +{ + my $self =shift; + my ($new_values) = @_; + + Bugzilla->login; + + _convert_to_ids($new_values); + + my $test_run = new Bugzilla::Testopia::TestRun($new_values); + + Bugzilla->logout; + + # Result is new test run id + return $test_run->store(); +} + +sub update +{ + my $self =shift; + my ($test_run_id, $new_values) = @_; + + Bugzilla->login; + + my $test_run = new Bugzilla::Testopia::TestRun($test_run_id); + + _convert_to_ids($new_values); + + $test_run->update($new_values); + + Bugzilla->logout; + + # Result is zero on success, otherwise an exception will be thrown + return 0; +} + +1; \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl b/mozilla/webtools/testopia/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl index 292186dde15..93d82f8eb89 100644 --- a/mozilla/webtools/testopia/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/hook/global/useful-links.html.tmpl/end/tr.html.tmpl @@ -16,11 +16,24 @@ Testopia: diff --git a/mozilla/webtools/testopia/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl b/mozilla/webtools/testopia/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl index 414bb406d9e..666f0ca4d3a 100644 --- a/mozilla/webtools/testopia/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/hook/global/user-error.html.tmpl/errors/tr-user-error.html.tmpl @@ -88,7 +88,7 @@ [% ELSIF error == "testopia-create-environment" %] [% title = "No Environments Defined" %] No environments have been created yet. - You can create environments here. + You can create environments here. [% ELSIF error == "testopia-missing-required-field" %] [% title = "Missing Required Field" %] It seems there was no value entered for [% field FILTER none %]. diff --git a/mozilla/webtools/testopia/template/en/default/hook/index.html.tmpl/links/tr.html.tmpl b/mozilla/webtools/testopia/template/en/default/hook/index.html.tmpl/links/tr.html.tmpl index b0e956eaa4c..b2d3437f33d 100644 --- a/mozilla/webtools/testopia/template/en/default/hook/index.html.tmpl/links/tr.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/hook/index.html.tmpl/links/tr.html.tmpl @@ -15,7 +15,7 @@

Do some testing
Manage test plans
Search existing test cases
- Manage run environments

+ Manage run environments

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/add.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/add.html.tmpl new file mode 100644 index 00000000000..00976f08078 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/add.html.tmpl @@ -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 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 + #%] + + [% PROCESS global/header.html.tmpl + title = "Add a New Test Plan Type" + %] + +
+ + + Type Name: + +
+ +[% PROCESS global/footer.html.tmpl %] \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/delete.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/delete.html.tmpl new file mode 100644 index 00000000000..b22b9089014 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/delete.html.tmpl @@ -0,0 +1,23 @@ +[%# 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 + #%] + + [% PROCESS global/header.html.tmpl + title = "Test Plan Types" + %] \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/edit.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/edit.html.tmpl new file mode 100644 index 00000000000..a60878dfbea --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/edit.html.tmpl @@ -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 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 + #%] + + [% PROCESS global/header.html.tmpl + title = "Edit Test Plan Types" + %] + +
+ + + + Type Name: + +
+ +[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/show.html.tmpl new file mode 100644 index 00000000000..d13d874404c --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/admin/plantypes/show.html.tmpl @@ -0,0 +1,41 @@ +[%# 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 + #%] + + [% PROCESS global/header.html.tmpl + title = "Test Plan Types" + %] + + + + + + + [% FOREACH type = plan.get_plan_types %] + + + + + [% END %] + + + +
NameActions
[% type.name FILTER html %]Edit
Add
+ +[% PROCESS global/footer.html.tmpl %] \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/admin/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/admin/show.html.tmpl new file mode 100644 index 00000000000..b460dc1da0e --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/admin/show.html.tmpl @@ -0,0 +1,44 @@ +[%# 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 + #%] + +[%# 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 %] + +Plan Types + +[% PROCESS global/footer.html.tmpl %] \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl index bd2719ad9f3..e4b06d1088d 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl @@ -31,7 +31,7 @@ [%- sel.events IF sel.events %] [%- sel.style IF sel.style %]> [% FOREACH item = sel.list %] - [% END %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/add.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/add.html.tmpl index 96edfd6cb30..6841865aa18 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/case/add.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/add.html.tmpl @@ -35,7 +35,13 @@ title = title style = style %] - + + [% PROCESS testopia/style.none.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/bugs.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/bugs.html.tmpl new file mode 100644 index 00000000000..c7362d03621 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/bugs.html.tmpl @@ -0,0 +1,44 @@ +[%# 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 + #%] + + + + + + + + +[% FOREACH bug = case.bugs %] + + + + +[% END %] +
[% terms.Bugs %]
IDSummaryRemove
[% bug.bug_id FILTER bug_link(bug.bug_id) %] [% bug.short_desc FILTER html %]detach bug +
+ + + + + +
Attach [% terms.Bugs %]
+

+ +

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/filter.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/filter.html.tmpl new file mode 100644 index 00000000000..13ca6d71491 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/filter.html.tmpl @@ -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): Greg Hendricks + #%] + +

Filter

+
+ + + + + + + + + + + + + + + + + +
CategoryPriorityComponentsAutomatic
+ [% PROCESS select sel = { name => 'category', + accesskey => 't', + list => plan.get_product_categories, + elements => 5, + mult => 1 } %] + + [% PROCESS select sel = { name => 'priority_id', + accesskey => 'p', + list => case.get_priority_list + elements => 5, + mult => 1 } %] + + [% PROCESS select sel = { name => 'component', + accesskey => 'm', + list => plan.get_product_components + elements => 5, + mult => 1 } %] + + [% PROCESS select sel = { name => 'isautomated', + accesskey => 'a', + list => + [ { id => "0", name => "Manual" }, + { id => "1", name => "Automatic" } ] + elements => 5, + mult => 1 } %] + + + + + + + + + + + + + + +
Summary Contains: +
Tags: +
Default Tester Contains: +
+
+
diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/form.html.tmpl index c42238a9e63..d7569f200e2 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/case/form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/form.html.tmpl @@ -24,8 +24,9 @@ #%] [% IF case.canedit %] + [% END %] @@ -53,7 +62,7 @@ Default Tester - + Add Tags @@ -126,7 +135,8 @@ default => comps mult => 1 deflist => 1 - elements => 7 } %] + elements => 7 + events => "onChange=\"chgTester(this)\""} %] [% END %] @@ -173,23 +183,36 @@ - -[%# action, effect %] -

Action

-
- -
-

-

Expected Results

-
- -
+

+ +

+ +[%# action, effect %] +

Set Up

+ +

+ +

+

Break Down

+ +

+ +

+

Action

+ +

+ +

+

Expected Results

+

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/list.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/list.html.tmpl index 8e71ef25be2..cee87e0ae2b 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/case/list.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/list.html.tmpl @@ -43,6 +43,8 @@ +[% PROCESS testopia/case/filter.html.tmpl IF addrun %] +
[% PROCESS testopia/case/table.html.tmpl %] @@ -104,6 +106,7 @@ found. + [% IF NOT multiprod %] Category [% PROCESS select sel = { name => 'category', @@ -111,6 +114,9 @@ found. list => category_list default => "--Do Not Change--" } %] + [% ELSE %] + + [% END %] Arguments: @@ -123,6 +129,12 @@ found. (comma-separated list of ids): + + Link selected cases to the following plans + (comma-separated list of ids): + + +

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/show.html.tmpl index cf6ba74f3b0..f5a147883e4 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/case/show.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/show.html.tmpl @@ -41,17 +41,15 @@ - + + -Filter: -Show All |  -Assigned to Me |  -Tested by Me |  -Open |  -Closed |  -[% statuslist = run.current_caseruns.0.get_status_list %] -[% statuslist.unshift({id => 0, name => '--Status--'}) %] -[% PROCESS select sel = { name => "ftr_case_run_status_id", - list => statuslist, - events => 'onchange="filterTable(1)"' } -%] |  -[% categorylist = run.plan.get_product_categories %] -[% categorylist.unshift({id => 0, name => '--Category--'}) %] -[% PROCESS select sel = { name => "ftr_category_id", - list => categorylist, - events => 'onchange="filterTable(2)"' } -%] |  -[% buildlist = run.plan.get_product_builds %] -[% buildlist.unshift({id => 0, name => '--Build--'}) %] -[% PROCESS select sel = { name => "ftr_build_id", - list => buildlist, - events => 'onchange="filterTable(3)"' } -%] + + + + + + + + + + + + + + + + + + + + + + + + +
Filter
StatusCategoryBuildPriorityComponentAutomatic + + + + + + + + + + + + + + + + + + + + + +
Assignee Contains + +
Tested By Contains + +
Summary Contains + +
Environment + +
Case Tags + +
+
+ [% PROCESS select sel = { name => "case_run_status_id", + list => run.current_caseruns.0.get_status_list, + elements => 6, + mult => 1, } + %] + + [% PROCESS select sel = { name => "category", + list => run.filter_case_categories, + byname => 1, + elements => 6, + mult => 1, } + + %] + + [% PROCESS select sel = { name => "build", + list => run.filter_builds, + byname => 1, + elements => 6, + mult => 1, } + %] + + [% PROCESS select sel = { name => "priority_id", + list => run.current_caseruns.0.case.get_priority_list, + elements => 6, + mult => 1, } + %] + + [% PROCESS select sel = { name => "component", + list => run.filter_components, + byname => 1, + elements => 6, + mult => 1, } + %] + + [% PROCESS select sel = { name => 'isautomated', + accesskey => 'a', + list => + [ { id => "0", name => "Manual" }, + { id => "1", name => "Automatic" } ] + elements => 5, + mult => 1 } %] +
+

\ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/form.html.tmpl index 6baffd36a49..ebf2c946db1 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/form.html.tmpl @@ -83,6 +83,8 @@ default => default_build } %] + Environment + Assignee Attach [% terms.Bugs %] @@ -119,9 +121,15 @@ Notes - - - +
[% caserun.notes %]
+ + + Append a New Note + + + + + diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/list.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/list.html.tmpl index 9379c4853b9..362df81febc 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/list.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/list.html.tmpl @@ -43,8 +43,9 @@ -
[% PROCESS "testopia/caserun/filter.html.tmpl" %] + +
[% IF table.list_count %] [% PROCESS "testopia/caserun/case-history.html.tmpl" %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form-header.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form-header.html.tmpl index efee0a291be..f91b80d8d26 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form-header.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form-header.html.tmpl @@ -44,8 +44,9 @@ events => "onChange='chBld($index, this.value, \"$caserun.status\", $caserun.id)'"} %] +   - [% caserun.assignee.login %] + [% caserun.assignee.login %] [% caserun.testedby.login %] [% caserun.close_date FILTER time %] @@ -58,5 +59,6 @@ width="14" height="14"/> - [% caserun.case.category.name %] + [% caserun.case.priority FILTER html %] + [% caserun.case.category.name FILTER html %] [% IF caserun.bug_count %][% caserun.bug_count %][% ELSE %][% caserun.bug_count %][% END %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form.html.tmpl index adac9d2c90e..ab271b559a0 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/short-form.html.tmpl @@ -21,55 +21,72 @@ [%##### BEGIN Hidden Pane #####%] - +
- [%##### Sumarry, Action, Expected Results #####%] + [%##### Action, Expected Results #####%] - - - -
Summary:  - [% caserun.case.summary FILTER html %]
+ +
Action:
[% caserun.case.text.action %] +
- + +
Expected Results:
[% caserun.case.text.effect %] +
- [%##### Notes #####%] +
+ [%##### Status #####%] + + + + + + +
+
+ Status: +
+ [% FOREACH status = caserun.get_status_list %] +
+ [% IF NOT caserun.canedit %] + + [% status.name FILTER none %] (disabled) + [% ELSE %] + + [% status.name FILTER none %] + [% END %] +
+ [% END %] +
+ [%##### Notes #####%]
Notes:
- -
-
- - [%##### Status #####%] -
- Status: -
- [% FOREACH status = caserun.get_status_list %] -
- [% IF NOT caserun.canedit %] - - [% status.name FILTER none %] (disabled) - [% ELSE %] - - [% status.name FILTER none %] - [% END %] -
- [% END %] + style="height:100px; width:400px;" rows=""> + +
[% IF caserun.runningby != '' || caserun.canedit == 0 %] @@ -99,7 +116,7 @@ ]
- Log a New Bug  + Log a New Bug 
- +
+ [% IF caserun.candelete %] + Delete + [% END %] + Classic interface... +
diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/show.html.tmpl index 76f52a9b9fd..ed2f2fcb4dc 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/show.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/show.html.tmpl @@ -82,19 +82,23 @@ - +[% IF caserun.candelete %] +

+ Delete +

+[% END %]

[%##### Case Action & Expected Results #####%]

Case Action & Expected Results

- - + + - - + +
ActionExpected ResultsActionExpected Results
[% caserun.case.text.action FILTER none %][% caserun.case.text.effect FILTER none %]
[% caserun.case.text.action FILTER none %]
[% caserun.case.text.effect FILTER none %]

Attributes

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/caserun/table.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/caserun/table.html.tmpl index 6cd4720e148..e33cd6f4a08 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/caserun/table.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/caserun/table.html.tmpl @@ -41,10 +41,12 @@ Case ID Build + Environment Assigned Tested by Close date Status + Priority Category Bugs @@ -57,6 +59,9 @@ [% PROCESS "testopia/caserun/short-form-header.html.tmpl" %] + +
Summary:  + [% caserun.case.summary FILTER html %]
[% PROCESS "testopia/caserun/short-form.html.tmpl" %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/add.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/add.html.tmpl index fac9860e75d..abe7ab3961a 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/add.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/add.html.tmpl @@ -39,6 +39,48 @@ [% PROCESS testopia/blocks.html.tmpl %] [% PROCESS testopia/messages.html.tmpl %] -[% PROCESS testopia/environment/form.html.tmpl %] + + +[%############################################################################%] +[%# New Environment #%] +[%############################################################################%] +

New Environment

+ + + + + + + + + +
Name:
Product:[% PROCESS select sel = { + name => 'product', + accesskey => 'p', + list => products, + } %] +
+ +
+ + +
+
+ + +[%############################################################################%] +[%# Upload XML #%] +[%############################################################################%] +
+

OR Upload XML

+ + +
+ + Help
+

+ +

+
[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/category.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/category.html.tmpl new file mode 100644 index 00000000000..5f36792f23e --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/category.html.tmpl @@ -0,0 +1,65 @@ +[%# 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 + # Michael Hight + #%] + +[%# INTERFACE: + # ... + #%] + + +
+

Category Administration

+ + + + + + + + + + + + +
Parent Product: + +
Category Name:
+ +
+ + + + + + + + + + + diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/element.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/element.html.tmpl new file mode 100644 index 00000000000..847bee59e08 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/element.html.tmpl @@ -0,0 +1,128 @@ +[%# 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 + # Brian Kramer + # Michael Hight + #%] + +[%# INTERFACE: + # ... + #%] + + + + + +

Element Administration

+ + + + + + + + + + + + + + + + + + + + + + + + +
Change Element Parent
+ + + + + + +
Product + + +[%### Product ###################################################################################### %] + +
+
+ + + + + +
Category +[%### Category ###################################################################################### %] + +
+
+ + + + + +
Parent +[%### Element ###################################################################################### %] + +
+
+
Element Name
+ + + + + +
Name:
+
+
+ diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/property.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/property.html.tmpl new file mode 100644 index 00000000000..b76ed5230cd --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/property.html.tmpl @@ -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): Greg Hendricks + # Michael Hight + # Scott Sudweeks + #%] + +[%# INTERFACE: + # ... + #%] + + + +

Property Administration

+ + + + + + + + + + + + + + + +
Assiged to Element: + +
Property Name:
+ +
+ + \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/show.html.tmpl new file mode 100644 index 00000000000..60231ba2fa1 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/show.html.tmpl @@ -0,0 +1,75 @@ +[%# 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 + #%] + +[%# INTERFACE: + # ... + #%] + +[%############################################################################%] +[%# Template Initialization #%] +[%############################################################################%] + +[% PROCESS global/variables.none.tmpl %] + +[% title = "Environment Administration" %] + +[%############################################################################%] +[%# Page Header #%] +[%############################################################################%] + +[% PROCESS global/header.html.tmpl %] + +[% PROCESS testopia/style.none.tmpl %] +[% PROCESS testopia/blocks.html.tmpl %] +
+ + + + + +
+

This is the environment admin tool. +You can create, edit, and remove environment elements by right clicking on a node in the +tree. Changes are applied immediately unless prompted.

+

Before adding categories and elements to a product, be sure that they do not +already exist in the --ALL-- product.

+
+ + + + + + + + + + + + + +
Legend
Environment Category
Environment Element
Element Property
+
+ +
+
[% PROCESS testopia/messages.html.tmpl %]
+ +[% PROCESS testopia/environment/admin/tree.html.tmpl %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/tree.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/tree.html.tmpl new file mode 100644 index 00000000000..515f36c7983 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/tree.html.tmpl @@ -0,0 +1,460 @@ +[%# 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 + # Brian Kramer + # Michael Hight + #%] + +[%# INTERFACE: + # ... + #%] + + + + + + + +
+
+
+
+
+
+
+
+ + + + + + + + + +

Environment Variables

+
+ +
+ +
+ [% IF type == 'classification' %] +
+ [% END %] + [% FOREACH item = toplevel %] +
0) %] >
+ [% END %] +
+
+
diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/valid_exp.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/valid_exp.html.tmpl new file mode 100644 index 00000000000..c2800511496 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/admin/valid_exp.html.tmpl @@ -0,0 +1,70 @@ +[%# 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 + # Michael Hight + # Scott Sudweeks + #%] + +[%# INTERFACE: + # ... + #%] + + +

Property Value Administration

+ + + + + + + + + + + + + + + + + +
Values For:
[% property.name %]
+ + + +
+ +
+ Value: + + +
+ +
+ + +

You must click Save Changes for your changes to take effect

\ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/choose.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/choose.html.tmpl new file mode 100644 index 00000000000..331885effd5 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/choose.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 + # Greg Hendricks + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "Search by Test Case Number" + %] + + +

+ You may find a test environment by entering its id here: + + +

+ Or you can search for environments Here. + + +[% PROCESS global/footer.html.tmpl %] \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/delete.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/delete.html.tmpl index cf07136ef50..ebca42bd2d0 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/delete.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/delete.html.tmpl @@ -47,7 +47,7 @@ test runs. Please reassign these runs to a different Environment before deleting [% ELSE %] You are about to permanantly delete this environment.
-
+ diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/export.xml.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/export.xml.tmpl new file mode 100644 index 00000000000..8bf97bcf78b --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/export.xml.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): Garrett Braden + #%] + +[%# 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 %] + +

Environment XML

+ + +[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/form.html.tmpl index 86b62800b7d..1687ea2a61e 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/form.html.tmpl @@ -22,41 +22,56 @@ # ... #%] - - - -

Attributes

- + +
+
- - - - - - - -
Name:
OS: - [% PROCESS select sel = { name => 'op_sys', - list => environment.get_op_sys_list, - accesskey => 'o' - default => environment.op_sys } %] + +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. Platform: - [% PROCESS select sel = { name => 'rep_platform', - list => environment.get_rep_platform_list, - accesskey => 'f' - default => environment.rep_platform } %] + + + + + + + + + + + + + + + + + +
Legend
Environment Category
Environment Element
Element Property
Selected Property Value
-
-

XML

- -
-Help
-

- -

- -[% IF Param("allow-test-deletion") %] -Delete Attachment -[% END %] + +
+ + + + + + + + + + + + + + +
NameCreate a New Environment
Product[% PROCESS select sel = { + name => 'product_id', + list => user.get_selectable_products, + default => environment.product_id, + accesskey => 'p'} %] + Edit Environment Variables
\ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/import.xml.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/import.xml.tmpl new file mode 100644 index 00000000000..63756ad1e93 --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/import.xml.tmpl @@ -0,0 +1,57 @@ +[%# 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 + #%] + +[%# 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" %] +
+

Add the above new data?

+ + + +
+[% ELSE %] +
+

Upload XML Environment:

+ +

+
+[% END %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/list.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/list.html.tmpl index be48bd32fd9..b506e9117da 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/list.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/list.html.tmpl @@ -41,10 +41,5 @@ [% PROCESS testopia/messages.html.tmpl %] [% PROCESS testopia/environment/table.html.tmpl %] -
-
- - -
[% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/show.html.tmpl index e953a6d966d..0cbb0785429 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/show.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/show.html.tmpl @@ -16,19 +16,20 @@ # Maciej Maczynski. All Rights Reserved. # # Contributor(s): Greg Hendricks + # Scott Sudweeks #%] [%# INTERFACE: # ... #%] - + [%############################################################################%] [%# Template Initialization #%] [%############################################################################%] [% PROCESS global/variables.none.tmpl %] -[% title = "Edit Test Environment: $environment.name" %] +[% title = "Environment Editor" %] [%############################################################################%] [%# Page Header #%] @@ -38,8 +39,203 @@ [% PROCESS testopia/style.none.tmpl %] [% PROCESS testopia/blocks.html.tmpl %] -[% PROCESS testopia/messages.html.tmpl %] + + + +
+
+
+ + +[% PROCESS testopia/messages.html.tmpl %] [% PROCESS testopia/environment/form.html.tmpl %] +
+ + + + + + +
+ + + + +

Environment: [% environment.name %]

+
+ +
+ +
+ +
+
+
+
+ + + + +

Environment Items

+
+ +
+ +
+ [% IF type == 'classification' %] +
+ [% END %] + [% FOREACH item = toplevel %] +
0) %] >
+ [% END %] +
+
+
+ [% PROCESS global/footer.html.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/environment/table.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/environment/table.html.tmpl index fef02be957d..15cf7e940b7 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/environment/table.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/environment/table.html.tmpl @@ -21,26 +21,28 @@ [%# INTERFACE: # ... #%] + +[% link = "${table.get_order_url}&order=" %] - +[% DECORATIVE_BORDER_BEGIN %] +
- - - - - - + + + -[% FOREACH env = environments %] - - - - - - - - -[% END %] + [% FOREACH env = table.list %] + + + + + + [% END %]
IDNameOSPlatformRunsActionsIDNameProduct
[% env.id FILTER html %][% env.name FILTER html %][% env.op_sys FILTER html %][% env.rep_platform FILTER html %][% env.get_run_count(1) FILTER none %] - Delete -
[% env.id FILTER none %][% env.name FILTER none %][% env.product.name FILTER none %]
+ [% IF NOT table.viewall %] + [% PROCESS navigation %] + [% END %] +[% DECORATIVE_BORDER_END %] + +
+Create a New Environment \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/percent_bar.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/percent_bar.html.tmpl index 140c79532a8..d555c184ee7 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/percent_bar.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/percent_bar.html.tmpl @@ -23,12 +23,22 @@ #%]
- + [%# IFs needed for Opera browser #%] - [% IF percent > 0 %][% END %] - [% IF percent < 100 %][% END %] + + + + -
[% percent %]
+
[% run.percent_complete %]
+ \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/plan/add.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/plan/add.html.tmpl index 958eae1ddb7..6d926802e3c 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/plan/add.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/plan/add.html.tmpl @@ -35,7 +35,14 @@ title = title style = style %] - + + + [% PROCESS testopia/style.none.tmpl %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/plan/form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/plan/form.html.tmpl index 38c5a46d5b1..21d8cb40cdb 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/plan/form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/plan/form.html.tmpl @@ -19,18 +19,10 @@ # Greg Hendricks #%] - @@ -76,7 +68,13 @@ [% END %] - Type + + [% IF UserInGroup('admin') %] + Type + [% ELSE %] + Type + [% END %] + [% PROCESS select sel = { name => 'type', list => plan.get_plan_types, accesskey => 't' @@ -97,12 +95,9 @@ [% END %]

Plan Document

-
- -

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/plan/list.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/plan/list.html.tmpl index 2b89119839f..1c8b206edd9 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/plan/list.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/plan/list.html.tmpl @@ -42,23 +42,12 @@ [% PROCESS testopia/messages.html.tmpl %] - + - + [% PROCESS testopia/style.none.tmpl %] +[% PROCESS testopia/search/variables.none.tmpl %] [% PROCESS testopia/messages.html.tmpl %] + +[% PROCESS testopia/case/filter.html.tmpl %] +

Select Test Cases

[% PROCESS testopia/case/table.html.tmpl diff --git a/mozilla/webtools/testopia/template/en/default/testopia/run/clone.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/run/clone.html.tmpl index 9767edcf560..9c8523c333e 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/run/clone.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/run/clone.html.tmpl @@ -45,6 +45,7 @@ + @@ -78,7 +79,7 @@ - +[% IF NOT case_list %] - +[% END %]
Yes No
Copy test cases: Yes @@ -95,7 +96,7 @@ default => 'all' } %]
diff --git a/mozilla/webtools/testopia/template/en/default/testopia/run/form.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/run/form.html.tmpl index bb87f0d6608..f12161522f4 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/run/form.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/run/form.html.tmpl @@ -44,12 +44,10 @@ Manager - Environment - - [% PROCESS select sel = { name => "environment", - list => run.get_environments, - default => run.environment.id} - %] + Environment + + + New @@ -70,12 +68,15 @@ list => run.plan.builds, default => run.build.id } %] + [% IF action == 'Add' %] + or New: + [% END %] Summary - + diff --git a/mozilla/webtools/testopia/template/en/default/testopia/run/list.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/run/list.html.tmpl index e9e2f7b0a5b..a2ca605ca49 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/run/list.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/run/list.html.tmpl @@ -42,7 +42,7 @@ [% PROCESS testopia/messages.html.tmpl %] - + [% PROCESS testopia/run/table.html.tmpl %] @@ -82,11 +82,8 @@ found. Environment - - [% PROCESS select sel = { name => 'environment', - accesskey => 'e', - list => env_list - default => "--Do Not Change--" } %] + + [% IF NOT multiprod %] @@ -98,11 +95,14 @@ found. default => "--Do Not Change--" } %] +[% ELSE %] + [% END %]

+

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/run/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/run/show.html.tmpl index 00738260377..0bffbfcae94 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/run/show.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/run/show.html.tmpl @@ -42,10 +42,18 @@ [% PROCESS testopia/messages.html.tmpl %] [% IF run.canedit %] + - + [% IF dotweak %] Select: All, @@ -41,6 +44,7 @@ Select: Started[% table.arrow IF table.last_sort == 'alias' %] Finished[% table.arrow IF table.last_sort == 'alias' %] % Completed + Out of [% IF NOT plan %]Plan[% table.arrow IF table.last_sort == 'alias' %][% END %] @@ -62,9 +66,9 @@ Select: [% END %] - [% percent = run.percent_complete %] [% PROCESS testopia/percent_bar.html.tmpl %] + [% run.case_count FILTER html %] [% IF NOT plan %][% run.plan.id FILTER html %][% END %] [% END %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/search/advanced.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/search/advanced.html.tmpl index ebc25539af1..7090e48b1bf 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/search/advanced.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/search/advanced.html.tmpl @@ -40,19 +40,13 @@ [% tabs = [ { name => 'case', description => "Find Test Cases" }, { name => 'run', description => "Find Test Runs" }, - { name => 'plan', description => "Find Test Plans" } ] %] - - + + + + + +[%#######################################%] +[%############## HTML #################%] +[%#######################################%] +

Search for Environments

+ + + + + +[%###### Bugzilla Products #########%] + + +[%############ CATEGORY ############%] + + + + +[%############ ELEMENTS ############%] + + +[%############ PROPERTIES ############%] + + + +[%######### VALID EXPRESSIONS ##########%] + + + +
+ [% IF Param('useclassification') %] +
Classification
+ [% PROCESS select sel = { + name => 'classification', + accesskey => 'c', + list => classifications, + elements => 5, + mult => 1, + events => 'onchange="env_class_change(this);"' } %] + [% END %] +
+
Products:
+ [% PROCESS select sel = { + name => 'env_products', + accesskey => 'c', + list => products, + elements => 5, + mult => 1, + events => 'onchange="env_products_change(this);"' } %] +
+
Category:
+ [% PROCESS select sel = { + name => 'env_categories', + accesskey => 'c', + list => env.get_all_env_categories(1), + elements => 5, + mult => 1, + events => 'onchange="env_categories_change(this);"' } %] +
+
Element:
+ [% PROCESS select sel = { + name => 'env_elements', + accesskey => 'c', + list => env.get_all_visible_elements(1), + elements => 5, + mult => 1, + events => 'onchange="env_elements_change(this);"' } %] + +
+
Properties:
+ [% PROCESS select sel = { + name => 'env_properties', + accesskey => 'c', + list => env.get_all_element_properties(1), + elements => 5, + mult => 1, + events => 'onchange="env_properties_change(this);"' } %] +
+
Property Value
+ [% PROCESS select sel = { + name => 'env_expressions', + accesskey => 'c', + list => env.get_distinct_property_values, + elements => 5, + mult => 1, } %] +
+
+ + + + + + + + + + + +
Environment Name + [% PROCESS select sel = { name => 'name_type', + list => query_variants } %]
Selected Property Value + [% PROCESS select sel = { name => 'env_value_selected_type', + list => query_variants } %]
+ + + \ No newline at end of file diff --git a/mozilla/webtools/testopia/template/en/default/testopia/search/plan.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/search/plan.html.tmpl index 40b00b49868..984140bc31b 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/search/plan.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/search/plan.html.tmpl @@ -84,17 +84,7 @@ [% PROCESS select sel = { name => 'author_type', list => email_variants } %] - AND - OR - - - Editor: - - [% PROCESS select sel = { name => 'editor_type', - list => email_variants } %] - - + + + + + + + image/svg+xml + + + + + + + + + diff --git a/mozilla/webtools/testopia/testopia/img/env_lookup.png b/mozilla/webtools/testopia/testopia/img/env_lookup.png new file mode 100644 index 00000000000..c5a9cc77e66 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/env_lookup.png differ diff --git a/mozilla/webtools/testopia/testopia/img/env_lookup.svg b/mozilla/webtools/testopia/testopia/img/env_lookup.svg new file mode 100644 index 00000000000..93dfb24ab34 --- /dev/null +++ b/mozilla/webtools/testopia/testopia/img/env_lookup.svg @@ -0,0 +1,86 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/mozilla/webtools/testopia/testopia/img/error.gif b/mozilla/webtools/testopia/testopia/img/error.gif new file mode 100644 index 00000000000..87029e3e8ed Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/error.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/folder.gif b/mozilla/webtools/testopia/testopia/img/folder.gif new file mode 100644 index 00000000000..346c1e2c821 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/folder.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/folder_blue.gif b/mozilla/webtools/testopia/testopia/img/folder_blue.gif new file mode 100644 index 00000000000..55447c25f1c Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/folder_blue.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/folder_red.gif b/mozilla/webtools/testopia/testopia/img/folder_red.gif new file mode 100644 index 00000000000..925eddbfde7 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/folder_red.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/green_bar.gif b/mozilla/webtools/testopia/testopia/img/green_bar.gif new file mode 100644 index 00000000000..319103f30ae Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/green_bar.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/orange_bar.gif b/mozilla/webtools/testopia/testopia/img/orange_bar.gif new file mode 100644 index 00000000000..5da8da4818c Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/orange_bar.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/red_bar.gif b/mozilla/webtools/testopia/testopia/img/red_bar.gif new file mode 100644 index 00000000000..2e9d16c6831 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/red_bar.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/selected_value.png b/mozilla/webtools/testopia/testopia/img/selected_value.png new file mode 100644 index 00000000000..4b01e526878 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/selected_value.png differ diff --git a/mozilla/webtools/testopia/testopia/img/square.gif b/mozilla/webtools/testopia/testopia/img/square.gif new file mode 100644 index 00000000000..eaa4e6b9b82 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/square.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/square.png b/mozilla/webtools/testopia/testopia/img/square.png new file mode 100644 index 00000000000..ac81e57e926 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/square.png differ diff --git a/mozilla/webtools/testopia/testopia/img/triangle.gif b/mozilla/webtools/testopia/testopia/img/triangle.gif new file mode 100644 index 00000000000..c72960a26d8 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/triangle.gif differ diff --git a/mozilla/webtools/testopia/testopia/img/triangle.png b/mozilla/webtools/testopia/testopia/img/triangle.png new file mode 100644 index 00000000000..bc452a57097 Binary files /dev/null and b/mozilla/webtools/testopia/testopia/img/triangle.png differ diff --git a/mozilla/webtools/testopia/testopia/js/caserun.js b/mozilla/webtools/testopia/testopia/js/caserun.js index 4495094f992..cc90f6a1dd6 100755 --- a/mozilla/webtools/testopia/testopia/js/caserun.js +++ b/mozilla/webtools/testopia/testopia/js/caserun.js @@ -84,6 +84,29 @@ function chBld(idx, bid, sid, cid){ mimetype: "text/plain" }); } +//chEnv Updates the caserun environment +function chEnv(idx, eid, sid, cid, oldid){ + if (oldid == eid) + return; + disableAllButtons(true); + dojo.io.bind({ + url: "tr_show_caserun.cgi", + content: { caserun_id: cid, index: idx, caserun_env: eid, action: 'update_environment'}, + load: function(type, data, evt){ + fillrow(data, idx); + var fields = data.split("|~+"); + document.getElementById('head_caserun_'+idx).innerHTML = fields[0]; + document.getElementById('body_caserun_'+idx).innerHTML = fields[1]; + document.getElementById('ra'+idx).style.display='block'; + document.getElementById('id'+idx).src='testopia/img/td.gif'; + displayMsg('pp'+ idx, 1, MSG_TESTLOG_UPDATED); + setTimeout("clearMsg('pp"+ idx +"')",OK_TIMEOUT); + disableAllButtons(false); + }, + error: function(type, error){ alert("ERROR");}, + mimetype: "text/plain" + }); +} //chStat updates the status function chStat(idx, sid, cid){ displayMsg('pp'+idx, 3, MSG_WAIT.blink()); @@ -127,6 +150,8 @@ function chNote(idx, cid, note){ url: "tr_show_caserun.cgi", content: { caserun_id: cid, index: idx, note: note, action: 'update_note'}, load: function(type, data, evt){ fillrow(data, idx); + document.getElementById('notes'+idx).value = ''; + document.getElementById('old_notes'+idx).innerHTML = data; displayMsg('pp'+ idx, 1, MSG_TESTLOG_UPDATED); setTimeout("clearMsg('pp"+ idx +"')",OK_TIMEOUT); disableAllButtons(false); @@ -144,6 +169,10 @@ function chOwn(idx, cid, owner){ url: "tr_show_caserun.cgi", content: { caserun_id: cid, index: idx, assignee: owner, action: 'update_assignee'}, load: function(type, data, evt){ fillrow(data, idx); + if (data.substring(0,5) == 'Error'){ + displayMsg('pp'+ idx, 2, data); + return; + } document.getElementById('own'+idx).innerHTML = owner; displayMsg('pp'+ idx, 1, MSG_TESTLOG_UPDATED); setTimeout("clearMsg('pp"+ idx +"')",OK_TIMEOUT); diff --git a/mozilla/webtools/testopia/testopia/js/util.js b/mozilla/webtools/testopia/testopia/js/util.js index ef6252450ba..25fa2bfe4d6 100755 --- a/mozilla/webtools/testopia/testopia/js/util.js +++ b/mozilla/webtools/testopia/testopia/js/util.js @@ -1,5 +1,15 @@ var _disable_sr=false; +function addOption(selectElement,newOption) { + try { + selectElement.add(newOption,null); + } + + catch (e) { + selectElement.add(newOption,selectElement.length); + } +} + function coal() { for(var i=1;;i++) { var ra = document.getElementById('ra'+i); @@ -189,4 +199,3 @@ function nb(obj) { //obj.rows=1; obj.style.height='20px'; } - diff --git a/mozilla/webtools/testopia/testopia/patch-2.22 b/mozilla/webtools/testopia/testopia/patch-2.22 index b9d46b454aa..ef84da58a66 100644 --- a/mozilla/webtools/testopia/testopia/patch-2.22 +++ b/mozilla/webtools/testopia/testopia/patch-2.22 @@ -1,18 +1,7 @@ -diff -u -r workspace/bmo2-20-2-tp10/enter_bug.cgi temp/bbmo-2.20.2-clean/enter_bug.cgi ---- workspace/bmo2-20-2-tp10/enter_bug.cgi 2005-10-23 15:50:34.000000000 -0600 -+++ temp/bbmo-2.20.2-clean/enter_bug.cgi 2006-06-01 16:54:24.000000000 -0600 -@@ -590,6 +590,8 @@ - - $vars->{'default'} = \%default; - -+$vars->{'caserun_id'} = $cgi->param('caserun_id'); -+ - my $format = $template->get_format("bug/create/create", - scalar $cgi->param('format'), - scalar $cgi->param('ctype')); -diff -u -r workspace/bmo2-20-2-tp10/post_bug.cgi temp/bbmo-2.20.2-clean/post_bug.cgi ---- workspace/bmo2-20-2-tp10/post_bug.cgi 2006-01-08 12:54:34.000000000 -0700 -+++ temp/bbmo-2.20.2-clean/post_bug.cgi 2006-06-01 16:54:24.000000000 -0600 +Index: /bmo-2.22/post_bug.cgi +=================================================================== +--- /bmo-2.22/post_bug.cgi 2006-01-08 12:56:03.000000000 -0700 ++++ /bmo-2.22-testopia/post_bug.cgi 2006-09-29 13:55:02.000000000 -0600 @@ -32,6 +32,8 @@ use Bugzilla::Util; use Bugzilla::Bug; @@ -41,15 +30,567 @@ diff -u -r workspace/bmo2-20-2-tp10/post_bug.cgi temp/bbmo-2.20.2-clean/post_bug print $cgi->header(); $template->process("bug/create/created.html.tmpl", $vars) || ThrowTemplateError($template->error()); -diff -u -r workspace/bmo2-20-2-tp10/template/en/default/bug/create/created.html.tmpl temp/bbmo-2.20.2-clean/template/en/default/bug/create/created.html.tmpl ---- workspace/bmo2-20-2-tp10/template/en/default/bug/create/created.html.tmpl 2005-07-27 14:56:09.000000000 -0600 -+++ temp/bbmo-2.20.2-clean/template/en/default/bug/create/created.html.tmpl 2006-06-01 16:55:06.000000000 -0600 -@@ -51,7 +51,7 @@ - [% END %] - -
-- -+[% Hook.process("message") %] -
- - [% PROCESS bug/edit.html.tmpl %] +Index: /bmo-2.22/enter_bug.cgi +=================================================================== +--- /bmo-2.22/enter_bug.cgi 2006-01-05 07:54:52.000000000 -0700 ++++ /bmo-2.22-testopia/enter_bug.cgi 2006-09-29 13:55:02.000000000 -0600 +@@ -590,6 +590,8 @@ + + $vars->{'default'} = \%default; + ++$vars->{'caserun_id'} = $cgi->param('caserun_id'); ++ + my $format = $template->get_format("bug/create/create", + scalar $cgi->param('format'), + scalar $cgi->param('ctype')); +Index: /bmo-2.22/checksetup.pl +=================================================================== +--- /bmo-2.22/checksetup.pl 2006-01-12 00:03:46.000000000 -0700 ++++ /bmo-2.22-testopia/checksetup.pl 2006-10-04 16:06:13.000000000 -0600 +@@ -313,7 +313,7 @@ + }, + { + name => 'Template', +- version => '2.08' ++ version => '2.12' + }, + { + name => 'Text::Wrap', +@@ -376,6 +376,10 @@ + } + } + ++print "\nThe following Perl modules are Required for Testopia:\n" unless $silent; ++$missing{'Text::Diff'} = '0.35' unless have_vers("Text::Diff","0.35"); ++$missing{'JSON'} = '1.07' unless have_vers("JSON","1.07"); ++ + print "\nThe following Perl modules are optional:\n" unless $silent; + my $gd = have_vers("GD","1.20"); + my $chartbase = have_vers("Chart::Base","1.0"); +@@ -1305,7 +1309,7 @@ + # These are the files which need to be marked executable + my @executable_files = ('whineatnews.pl', 'collectstats.pl', + 'checksetup.pl', 'importxml.pl', 'runtests.pl', 'testserver.pl', +- 'whine.pl'); ++ 'whine.pl', 'build_bugmail.pl', 'tr_install.pl', 'tr_importxml.pl'); + + # tell me if a file is executable. All CGI files and those in @executable_files + # are executable +Index: /bmo-2.22/Bugzilla.pm +=================================================================== +--- /bmo-2.22/Bugzilla.pm 2006-02-07 15:46:28.000000000 -0700 ++++ /bmo-2.22-testopia/Bugzilla.pm 2006-10-04 16:24:33.000000000 -0600 +@@ -46,6 +46,7 @@ + use constant SHUTDOWNHTML_EXEMPT => [ + 'editparams.cgi', + 'checksetup.pl', ++ 'tr_install.pl', + ]; + + # Non-cgi scripts that should silently exit. +Index: /bmo-2.22/Bugzilla/DB/Schema.pm +=================================================================== +--- /bmo-2.22/Bugzilla/DB/Schema.pm 2006-01-06 07:38:42.000000000 -0700 ++++ /bmo-2.22-testopia/Bugzilla/DB/Schema.pm 2006-10-04 16:28:11.000000000 -0600 +@@ -1039,6 +1039,483 @@ + version => {TYPE => 'decimal(3,2)', NOTNULL => 1}, + ], + }, ++ ++ # TESTOPIA TABLES ++ # --------------- ++ test_attachments => { ++ FIELDS => [ ++ attachment_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1}, ++ case_id => {TYPE => 'INT4', UNSIGNED => 1}, ++ submitter_id => {TYPE => 'INT3', NOTNULL => 1}, ++ description => {TYPE => 'MEDIUMTEXT'}, ++ filename => {TYPE => 'MEDIUMTEXT'}, ++ creation_ts => {TYPE => 'DATETIME', NOTNULL => 1}, ++ mime_type => {TYPE => 'varchar(100)', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ attachment_plan_idx => ['plan_id'], ++ attachment_case_idx => ['case_id'], ++ ], ++ }, ++ ++ test_case_categories => { ++ FIELDS => [ ++ category_id => {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ product_id => {TYPE => 'INT2', NOTNULL => 1}, ++ name => {TYPE => 'varchar(240)', NOTNULL => 1}, ++ description => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ category_name_idx => ['name'], ++ ], ++ }, ++ ++ test_cases => { ++ FIELDS => [ ++ case_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ case_status_id => {TYPE => 'INT1', NOTNULL => 1}, ++ category_id => {TYPE => 'INT2', NOTNULL => 1, UNSIGNED =>1}, ++ priority_id => {TYPE => 'INT2'}, ++ author_id => {TYPE => 'INT3', NOTNULL => 1}, ++ default_tester_id => {TYPE => 'INT3'}, ++ creation_date => {TYPE => 'DATETIME', NOTNULL => 1}, ++ isautomated => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0'}, ++ sortkey => {TYPE => 'INT4'}, ++ script => {TYPE => 'MEDIUMTEXT'}, ++ arguments => {TYPE => 'MEDIUMTEXT'}, ++ summary => {TYPE => 'varchar(255)'}, ++ requirement => {TYPE => 'varchar(255)'}, ++ alias => {TYPE => 'varchar(255)'}, ++ ], ++ INDEXES => [ ++ test_case_category_idx => ['category_id'], ++ test_case_author_idx => ['author_id'], ++ test_case_creation_date_idx => ['creation_date'], ++ test_case_sortkey_idx => ['sortkey'], ++ test_case_requirement_idx => ['requirement'], ++ test_case_shortname_idx => ['alias'], ++ ], ++ }, ++ ++ test_case_bugs => { ++ FIELDS => [ ++ bug_id => {TYPE => 'INT3', NOTNULL => 1}, ++ case_run_id => {TYPE => 'INT4', UNSIGNED => 1}, ++ case_id => {TYPE => 'INT4', UNSIGNED => 1}, ++ ], ++ INDEXES => [ ++ case_run_id_idx => [qw(case_run_id bug_id)], ++ case_run_bug_id_idx => ['bug_id'], ++ ], ++ }, ++ ++ test_case_runs => { ++ FIELDS => [ ++ case_run_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ run_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ assignee => {TYPE => 'INT3'}, ++ testedby => {TYPE => 'INT3'}, ++ case_run_status_id => {TYPE => 'INT1', NOTNULL => 1, UNSIGNED => 1}, ++ case_text_version => {TYPE => 'INT3', NOTNULL => 1}, ++ build_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ close_date => {TYPE => 'DATETIME'}, ++ notes => {TYPE => 'TEXT'}, ++ iscurrent => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '0'}, ++ sortkey => {TYPE => 'INT4'}, ++ environment_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ case_run_run_id_idx => ['run_id'], ++ case_run_case_id_idx => ['case_id'], ++ case_run_assignee_idx => ['assignee'], ++ case_run_testedby_idx => ['testedby'], ++ case_run_close_date_idx => ['close_date'], ++ case_run_shortkey_idx => ['sortkey'], ++ case_run_build_idx => [qw(case_run_id build_id)], ++ case_run_env_idx => [qw(case_run_id environment_id)], ++ ], ++ }, ++ ++ test_case_texts => { ++ FIELDS => [ ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL =>1}, ++ case_text_version => {TYPE => 'INT3', NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ creation_ts => {TYPE => 'DATETIME', NOTNULL => 1}, ++ action => {TYPE => 'MEDIUMTEXT'}, ++ effect => {TYPE => 'MEDIUMTEXT'}, ++ setup => {TYPE => 'MEDIUMTEXT'}, ++ breakdown => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ case_versions_idx => {FIELDS => [qw(case_id case_text_version)], ++ TYPE => 'UNIQUE'}, ++ case_versions_who_idx => ['who'], ++ case_versions_creation_ts_idx => ['creation_ts'], ++ ], ++ }, ++ ++ test_case_tags => { ++ FIELDS => [ ++ tag_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ userid => {TYPE => 'INT3', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ case_tags_case_id_idx => {FIELDS => [qw(tag_id case_id)], TYPE => 'UNIQUE'}, ++ case_tags_tag_id_idx => [qw(tag_id case_id)], ++ case_tags_user_idx => [qw(tag_id userid)], ++ ], ++ }, ++ ++ test_tags => { ++ FIELDS => [ ++ tag_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ tag_name => {TYPE => 'varchar(255)', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ test_tag_name_indx => {FIELDS => ['tag_name'], TYPE => 'UNIQUE'}, ++ ], ++ }, ++ ++ test_plans => { ++ FIELDS => [ ++ plan_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ product_id => {TYPE => 'INT2', NOTNULL => 1}, ++ author_id => {TYPE => 'INT3', NOTNULL => 1}, ++ type_id => {TYPE => 'INT1', NOTNULL => 1, UNSIGNED => 1}, ++ default_product_version => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)', NOTNULL => 1}, ++ creation_date => {TYPE => 'DATETIME', NOTNULL => 1}, ++ isactive => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, ++ ], ++ INDEXES => [ ++ plan_product_plan_id_idx => [qw(product_id plan_id)], ++ plan_author_idx => ['author_id'], ++ plan_type_idx => ['type_id'], ++ plan_isactive_idx => ['isactive'], ++ plan_name_idx => ['name'], ++ ], ++ }, ++ ++ test_plan_texts => { ++ FIELDS => [ ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ plan_text_version => {TYPE => 'INT4', NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ creation_ts => {TYPE => 'DATETIME', NOTNULL => 1}, ++ plan_text => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ test_plan_text_version_idx => [qw(plan_id plan_text_version)], ++ test_plan_text_who_idx => ['who'], ++ ], ++ }, ++ ++ test_plan_types => { ++ FIELDS => [ ++ type_id => {TYPE => 'TINYSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(64)', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ plan_type_name_idx => ['name'], ++ ], ++ }, ++ ++ test_runs => { ++ FIELDS => [ ++ run_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ environment_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ product_version => {TYPE => 'MEDIUMTEXT'}, ++ build_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ plan_text_version => {TYPE => 'INT4', NOTNULL => 1}, ++ manager_id => {TYPE => 'INT3', NOTNULL => 1}, ++ default_tester_id => {TYPE => 'INT3'}, ++ start_date => {TYPE => 'DATETIME', NOTNULL => 1}, ++ stop_date => {TYPE => 'DATETIME'}, ++ summary => {TYPE => 'TINYTEXT', NOTNULL => 1}, ++ notes => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ test_run_plan_id_run_id_idx => [qw(plan_id run_id)], ++ test_run_manager_idx => ['manager_id'], ++ test_run_start_date_idx => ['start_date'], ++ test_run_stop_date_idx => ['stop_date'], ++ test_runs_summary_idx => {FIELDS => ['summary'], ++ TYPE => 'FULLTEXT'}, ++ ], ++ }, ++ ++ test_case_plans => { ++ FIELDS => [ ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ case_plans_plan_id_idx => [qw(plan_id case_id)], ++ case_plans_case_id_idx => [qw(case_id plan_id)], ++ ], ++ }, ++ ++ test_case_activity => { ++ FIELDS => [ ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ fieldid => {TYPE => 'INT2', UNSIGNED => 1, NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ changed => {TYPE => 'DATETIME', NOTNULL => 1}, ++ oldvalue => {TYPE => 'MEDIUMTEXT'}, ++ newvalue => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ case_activity_case_id_idx => ['case_id'], ++ case_activity_who_idx => ['who'], ++ case_activity_when_idx => ['changed'], ++ case_activity_field_idx => ['fieldid'], ++ ], ++ }, ++ ++ test_fielddefs => { ++ FIELDS => [ ++ fieldid => {TYPE => 'SMALLSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(100)', NOTNULL => 1}, ++ description => {TYPE => 'MEDIUMTEXT'}, ++ table_name => {TYPE => 'varchar(100)', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ fielddefs_name_idx => ['name'], ++ ], ++ }, ++ ++ test_plan_activity => { ++ FIELDS => [ ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ fieldid => {TYPE => 'INT2', UNSIGNED => 1, NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ changed => {TYPE => 'DATETIME', NOTNULL => 1}, ++ oldvalue => {TYPE => 'MEDIUMTEXT'}, ++ newvalue => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ plan_activity_who_idx => ['who'], ++ ], ++ }, ++ ++ test_case_components => { ++ FIELDS => [ ++ case_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ component_id => {TYPE => 'INT2', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ case_components_case_id_idx => ['case_id'], ++ case_commponents_component_id_idx => ['component_id'], ++ ], ++ }, ++ ++ test_run_activity => { ++ FIELDS => [ ++ run_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ fieldid => {TYPE => 'INT2', UNSIGNED => 1, NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ changed => {TYPE => 'DATETIME', NOTNULL => 1}, ++ oldvalue => {TYPE => 'MEDIUMTEXT'}, ++ newvalue => {TYPE => 'MEDIUMTEXT'}, ++ ], ++ INDEXES => [ ++ run_activity_run_id_idx => ['run_id'], ++ run_activity_who_idx => ['who'], ++ run_activity_when_idx => ['changed'], ++ ], ++ }, ++ ++ test_run_cc => { ++ FIELDS => [ ++ run_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ who => {TYPE => 'INT3', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ run_cc_run_id_who_idx => [qw(run_id who)], ++ ], ++ }, ++ ++ test_email_settings => { ++ FIELDS => [ ++ userid => {TYPE => 'INT3', NOTNULL => 1}, ++ eventid => {TYPE => 'INT1', UNSIGNED => 1, NOTNULL => 1}, ++ relationship_id => {TYPE => 'INT1', UNSIGNED => 1, NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ test_event_user_event_idx => [qw(userid eventid)], ++ test_event_user_relationship_idx => [qw(userid relationship_id)], ++ ], ++ }, ++ ++ test_events => { ++ FIELDS => [ ++ eventid => {TYPE => 'INT1', UNSIGNED => 1, PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(50)'}, ++ ], ++ INDEXES => [ ++ test_event_name_idx => ['name'], ++ ], ++ }, ++ ++ test_relationships => { ++ FIELDS => [ ++ relationship_id => {TYPE => 'INT1', UNSIGNED => 1, PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(50)'}, ++ ], ++ }, ++ ++ test_case_run_status => { ++ FIELDS => [ ++ case_run_status_id => {TYPE => 'TINYSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(20)'}, ++ sortkey => {TYPE => 'INT4'}, ++ ], ++ INDEXES => [ ++ case_run_status_name_idx => ['name'], ++ case_run_status_sortkey_idx => ['sortkey'], ++ ], ++ }, ++ ++ test_case_status => { ++ FIELDS => [ ++ case_status_id => {TYPE => 'TINYSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ test_case_status_name_idx => ['name'], ++ ], ++ }, ++ ++ test_case_dependencies => { ++ FIELDS => [ ++ dependson => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ blocked => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ ], ++ }, ++ ++ test_plan_group_map => { ++ FIELDS => [ ++ group_id => {TYPE => 'INT3'}, ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL =>1}, ++ ], ++ }, ++ ++ test_environments => { ++ FIELDS => [ ++ environment_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ product_id => {TYPE => 'INT2', NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)'}, ++ isactive => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => '1'}, ++ ], ++ INDEXES => [ ++ environment_name_idx => ['name'], ++ ], ++ }, ++ ++ test_run_tags => { ++ FIELDS => [ ++ tag_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ run_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ userid => {TYPE => 'INT3', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ run_tags_idx => {FIELDS => [qw(tag_id run_id)], TYPE => 'UNIQUE'}, ++ run_tags_user_idx => [qw(tag_id userid)], ++ ++ ], ++ }, ++ ++ test_plan_tags => { ++ FIELDS => [ ++ tag_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ plan_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ userid => {TYPE => 'INT3', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ plan_tags_idx => {FIELDS => [qw(tag_id plan_id)], TYPE => 'UNIQUE'}, ++ plan_tags_user_idx => [qw(tag_id userid)], ++ ], ++ }, ++ ++ test_builds => { ++ FIELDS => [ ++ build_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ product_id => {TYPE => 'INT2', NOTNULL => 1}, ++ milestone => {TYPE => 'varchar(20)'}, ++ name => {TYPE => 'varchar(255)'}, ++ description => {TYPE => 'TEXT'}, ++ ], ++ }, ++ ++ test_attachment_data => { ++ FIELDS => [ ++ attachment_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ contents => {TYPE => 'LONGBLOB'}, ++ ], ++ }, ++ ++ test_named_queries => { ++ FIELDS => [ ++ userid => {TYPE => 'INT3', NOTNULL => 1}, ++ name => {TYPE => 'varchar(64)', NOTNULL => 1}, ++ isvisible => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 1}, ++ query => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}, ++ ], ++ INDEXES => [ ++ test_namedquery_name_idx => ['name'], ++ ], ++ }, ++ ++ test_environment_map => { ++ FIELDS => [ ++ environment_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ property_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ element_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ value_selected => {TYPE => 'TINYTEXT'}, ++ ], ++ INDEXES => [ ++ env_map_env_element_idx => [qw(environment_id element_id)], ++ env_map_property_idx => [qw(environment_id property_id)], ++ ], ++ }, ++ test_environment_element => { ++ FIELDS => [ ++ element_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ env_category_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)'}, ++ parent_id => {TYPE => 'INT4', UNSIGNED => 1}, ++ isprivate => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0}, ++ ], ++ INDEXES => [ ++ env_element_category_idx => [qw(env_category_id name)], ++ ], ++ }, ++ test_environment_category => { ++ FIELDS => [ ++ env_category_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ product_id => {TYPE => 'INT2', NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)'}, ++ ], ++ INDEXES => [ ++ env_category_idx => [qw(name product_id)], ++ ], ++ }, ++ test_environment_property => { ++ FIELDS => [ ++ property_id => {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}, ++ element_id => {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}, ++ name => {TYPE => 'varchar(255)'}, ++ validexp => {TYPE => 'TEXT'}, ++ ], ++ INDEXES => [ ++ env_element_property_idx => [qw(element_id name)], ++ ], ++ }, + + }; + #-------------------------------------------------------------------------- +Index: /bmo-2.22/Bugzilla/DB/Schema/Mysql.pm +=================================================================== +--- /bmo-2.22/Bugzilla/DB/Schema/Mysql.pm 2005-12-18 11:53:00.000000000 -0700 ++++ /bmo-2.22-testopia/Bugzilla/DB/Schema/Mysql.pm 2006-09-29 14:08:38.000000000 -0600 +@@ -103,9 +103,10 @@ + INT3 => 'mediumint', + INT4 => 'integer', + +- SMALLSERIAL => 'smallint auto_increment', +- MEDIUMSERIAL => 'mediumint auto_increment', +- INTSERIAL => 'integer auto_increment', ++ TINYSERIAL => 'tinyint unsigned auto_increment', ++ SMALLSERIAL => 'smallint unsigned auto_increment', ++ MEDIUMSERIAL => 'mediumint unsigned auto_increment', ++ INTSERIAL => 'integer unsigned auto_increment', + + TINYTEXT => 'tinytext', + MEDIUMTEXT => 'mediumtext', diff --git a/mozilla/webtools/testopia/testopia/testopia.create.sql b/mozilla/webtools/testopia/testopia/testopia.create.sql deleted file mode 100644 index 4352ab7a7fe..00000000000 --- a/mozilla/webtools/testopia/testopia/testopia.create.sql +++ /dev/null @@ -1,365 +0,0 @@ -/* -Created 7/28/2005 -Modified 5/12/2006 -Project Testrunner -Model Based on Bugzilla 2.20 -Company Novell -Author Jim Stutz -Version 12 -Database mySQL 4.0 -*/ - - - - -Create table test_category_templates ( - category_template_id Smallint UNSIGNED NOT NULL AUTO_INCREMENT, - name Varchar(255) NOT NULL, - description Mediumtext, - UNIQUE (category_template_id), - UNIQUE (name), - Index AI_category_template_id (category_template_id)) TYPE = MyISAM -COMMENT = 'Category templates store suggested values for categories. Users, when creating categories, will have the option to use an existing template from this entity class or can save an existing category as a template. Category templates are not associated with specific plans or products and are global to the system.'; - -Create table test_attachments ( - attachment_id Serial, - plan_id Bigint UNSIGNED, - case_id Bigint UNSIGNED, - submitter_id Mediumint NOT NULL, - description Mediumtext, - filename Mediumtext, - creation_ts Timestamp(14), - mime_type Varchar(100) NOT NULL, - Index AI_attachment_id (attachment_id)) TYPE = MyISAM -COMMENT = 'The test_attachment entity contains attachments that have been uploaded and can be associated with either test cases or test plans but not both. In other words, either a case_id or a plan_id will be stored but not both.'; - -Create table test_case_categories ( - category_id Smallint UNSIGNED NOT NULL AUTO_INCREMENT, - product_id Smallint NOT NULL, - name Varchar(240) NOT NULL, - description Mediumtext, - UNIQUE (category_id), - Index AI_category_id (category_id)) TYPE = MyISAM -COMMENT = 'Categories are designed to group test cases in a test plan. Categories can be anything the user wants to be. For example you could create an acceptance category to help identify acceptance test cases. Categories are plan specific and can be copied with a plan when a plan is cloned. Categories can also be based off category templates.'; - -Create table test_cases ( - case_id Serial, - case_status_id Tinyint NOT NULL, - category_id Smallint UNSIGNED NOT NULL, - priority_id Smallint, - author_id Mediumint NOT NULL, - default_tester_id Mediumint, - creation_date Datetime NOT NULL, - isautomated Bool NOT NULL DEFAULT 0, - sortkey Int, - script Mediumtext, - arguments Mediumtext, - summary Varchar(255), - requirement Varchar(255), - alias Varchar(255), - UNIQUE (case_id), - UNIQUE (alias), - Index AI_case_id (case_id)) TYPE = MyISAM -COMMENT = 'Test cases are the core of testrunner. A test case is essentially a checklist of things to check to determine whether the object under scrutiny passes or fails. A test case is associated with one or more test plans and is represented in a case run as a log entry (test_case_run). Cases consist of actions and effects which determine the passage or failure of the case in a run.'; - -Create table test_case_bugs ( - bug_id Mediumint NOT NULL, - case_run_id Bigint UNSIGNED) TYPE = MyISAM -COMMENT = 'The test_case_bugs table is a junction and lists bugs found in test case runs. Each test case run can have multiple bugs associated with it.'; - -Create table test_case_runs ( - case_run_id Serial, - run_id Bigint UNSIGNED NOT NULL, - case_id Bigint UNSIGNED NOT NULL, - assignee Mediumint, - testedby Mediumint, - case_run_status_id Tinyint UNSIGNED NOT NULL, - case_text_version Mediumint NOT NULL, - build_id Bigint UNSIGNED NOT NULL, - close_date Datetime, - notes Text, - iscurrent Bool DEFAULT 0, - sortkey Int, - UNIQUE (case_run_id), - Index AI_case_run_id (case_run_id)) TYPE = MyISAM -COMMENT = 'When test cases are run in a test run, a row for each test case will be created in the test_case_runs table. Test_case_runs are logs of whether a particular test case passed or failed during a particular run. Previously known as test_case_logs.'; - -Create table test_case_texts ( - case_id Bigint UNSIGNED NOT NULL, - case_text_version Mediumint NOT NULL, - who Mediumint NOT NULL, - creation_ts Timestamp(14) NOT NULL, - action Mediumtext, - effect Mediumtext) TYPE = MyISAM -COMMENT = 'As a test case document is changed the text is stored in a new row in this table. This increments the version of the testcase. It is important to note that changes to other fields in a test case besides the action and effect, are logged in the case activity table and do not increment the case version.'; - -Create table test_case_tags ( - tag_id Int UNSIGNED NOT NULL, - case_id Bigint UNSIGNED) TYPE = MyISAM -COMMENT = 'A tag is a way of organizing information in a many to many relationship. Tags are associated with test cases via this junction table.'; - -Create table test_plan_testers ( - tester_id Mediumint NOT NULL, - product_id Smallint, - read_only Bool DEFAULT 1) TYPE = MyISAM -COMMENT = 'The testers who can edit or view the test_case, test_run, test_case_text for a given plan.'; - -Create table test_tags ( - tag_id Int UNSIGNED NOT NULL AUTO_INCREMENT, - tag_name Varchar(255) NOT NULL, - UNIQUE (tag_id), - UNIQUE (tag_name), - Index AI_tag_id (tag_id)) TYPE = MyISAM -COMMENT = 'Test tags are used to classify test cases in a many to many relationship. Tags are similar to keywords in bugzilla and are global to the testrunner system.'; - -Create table test_plans ( - plan_id Serial, - product_id Smallint NOT NULL, - author_id Mediumint NOT NULL, - editor_id Mediumint NOT NULL, - type_id Tinyint UNSIGNED NOT NULL, - default_product_version Mediumtext NOT NULL, - name Varchar(255) NOT NULL, - creation_date Datetime NOT NULL, - isactive Bool NOT NULL DEFAULT 1, - UNIQUE (plan_id), - Index AI_plan_id (plan_id)) TYPE = MyISAM -COMMENT = 'A test plan is the parent of all other entites within testrunner. The test plan details how a set of tests should be approached for a given product and contains cases and runs.'; - -Create table test_plan_texts ( - plan_id Bigint UNSIGNED NOT NULL, - plan_text_version Int NOT NULL, - who Mediumint NOT NULL, - creation_ts Timestamp(14) NOT NULL, - plan_text Longtext) TYPE = MyISAM -COMMENT = 'Each revision to the document of a test plan creates an entry in test_plan_texts. The document details the overarching plan for testing a product.'; - -Create table test_plan_types ( - type_id Tinyint UNSIGNED NOT NULL AUTO_INCREMENT, - name Varchar(64) NOT NULL, - UNIQUE (type_id), - Index AI_type_id (type_id)) TYPE = MyISAM -COMMENT = 'The type of plan this is (e.g unit or integration)'; - -Create table test_runs ( - run_id Serial, - plan_id Bigint UNSIGNED NOT NULL, - environment_id Bigint UNSIGNED NOT NULL, - product_version Mediumtext, - build_id Bigint UNSIGNED NOT NULL, - plan_text_version Int NOT NULL, - manager_id Mediumint NOT NULL, - start_date Datetime, - stop_date Datetime, - summary Tinytext NOT NULL, - notes Mediumtext, - UNIQUE (run_id), - Index AI_run_id (run_id)) TYPE = MyISAM -COMMENT = 'Test runs are created from a plan and a set of testcases associated with that plan. The tes run entity tracks information about the run itself such as when it was started etc.'; - -Create table test_case_plans ( - plan_id Bigint UNSIGNED NOT NULL, - case_id Bigint UNSIGNED NOT NULL) TYPE = MyISAM -COMMENT = 'The test_case_plans table is junction between test cases and test plans. Each test plan can have many test cases but likewise each case can be associated with multiple plans.'; - -Create table test_case_activity ( - case_id Bigint UNSIGNED NOT NULL, - fieldid Smallint UNSIGNED NOT NULL, - who Mediumint NOT NULL, - changed Datetime NOT NULL, - oldvalue Mediumtext, - newvalue Mediumtext) TYPE = MyISAM -COMMENT = 'The test_case_activity table tracks changes made to test cases. Fields are mapped to the fielddefs table.'; - -Create table test_fielddefs ( - fieldid Smallint UNSIGNED NOT NULL AUTO_INCREMENT, - name Varchar(100) NOT NULL, - description Mediumtext, - table_name Varchar(100) NOT NULL, - UNIQUE (fieldid), - Index AI_fieldid (fieldid)) TYPE = MyISAM -COMMENT = 'Test_fielddefs will contain a list of fields used in test cases, test plans, and test runs for these entities respective activity tables. This is patterned after Bugzilla''s fielddefs table. All fields in any of these entites which you wish to track activity on should have an entry in this table, the ID of which will then be used to track activity in the activty tables.'; - -Create table test_plan_activity ( - plan_id Bigint UNSIGNED NOT NULL, - fieldid Smallint UNSIGNED NOT NULL, - who Mediumint NOT NULL, - changed Datetime NOT NULL, - oldvalue Mediumtext, - newvalue Mediumtext) TYPE = MyISAM -COMMENT = 'Test plan activitiy tracks changes to non text fields of test plans.'; - -Create table test_case_components ( - case_id Bigint UNSIGNED NOT NULL, - component_id Smallint NOT NULL) TYPE = MyISAM -COMMENT = 'The test_case_component table is a junction between test cases and components. Each test case can have zero or more components associated with it.'; - -Create table test_run_activity ( - run_id Bigint UNSIGNED NOT NULL, - fieldid Smallint UNSIGNED NOT NULL, - who Mediumint NOT NULL, - changed Datetime NOT NULL, - oldvalue Mediumtext, - newvalue Mediumtext) TYPE = MyISAM -COMMENT = 'Tracks changes to a test run. Note that this is not the same as changes to a case run (or run log) entity of class test_case_run.'; - -Create table test_run_cc ( - run_id Bigint UNSIGNED NOT NULL, - who Mediumint NOT NULL) TYPE = MyISAM -COMMENT = 'This is a list of people who should be notified about run status chages who may not otherwise be associated with the plan or it''s runs'; - -Create table test_email_settings ( - userid Mediumint NOT NULL, - eventid Tinyint UNSIGNED NOT NULL, - relationship_id Tinyint UNSIGNED NOT NULL) TYPE = MyISAM -COMMENT = 'A mapping of user email settings. Patterned after bugzilla email settings table. This allows users to select which events will trigger email notification to themselves depending on the roles they assume in the system.'; - -Create table test_events ( - eventid Tinyint UNSIGNED NOT NULL, - name Varchar(50), - UNIQUE (eventid)) TYPE = MyISAM -COMMENT = 'events table stores a list of events which should trigger email notification.'; - -Create table test_relationships ( - relationship_id Tinyint UNSIGNED NOT NULL, - name Varchar(50), - UNIQUE (relationship_id)) TYPE = MyISAM -COMMENT = 'A list of relationships for use in tracking notifications. Who should be notified.'; - -Create table test_case_run_status ( - case_run_status_id Tinyint UNSIGNED NOT NULL AUTO_INCREMENT, - name Varchar(20), - sortkey Int, - UNIQUE (sortkey), - Index AI_case_run_status_id (case_run_status_id)) TYPE = MyISAM -COMMENT = 'The test_case_run_status entity describes values that can be used as statuses in test_case_runs. Previously and enum field.'; - -Create table test_case_status ( - case_status_id Tinyint NOT NULL AUTO_INCREMENT, - name Varchar(255) NOT NULL, - UNIQUE (case_status_id), - Index AI_case_status_id (case_status_id)) TYPE = MyISAM -COMMENT = 'Test_case_status is a lookup of status on a test case. Replaces enum'; - -Create table test_case_dependencies ( - dependson Bigint UNSIGNED NOT NULL, - blocked Bigint UNSIGNED) TYPE = MyISAM -COMMENT = 'Track case dependenncies. Often test cases must be run in a specific order. This table will track which cases have dependencies on other cases.'; - -Create table test_case_group_map ( - case_id Bigint UNSIGNED NOT NULL, - group_id Mediumint) TYPE = MyISAM; - -Create table test_environments ( - environment_id Serial NOT NULL, - op_sys_id Int, - rep_platform_id Int, - name Varchar(255), - xml Mediumtext) TYPE = MyISAM; - -Create table test_run_tags ( - tag_id Int UNSIGNED NOT NULL, - run_id Bigint UNSIGNED) TYPE = MyISAM; - -Create table test_plan_tags ( - tag_id Int UNSIGNED NOT NULL, - plan_id Bigint UNSIGNED) TYPE = MyISAM; - -Create table test_builds ( - build_id Serial NOT NULL, - product_id Smallint NOT NULL, - milestone Varchar(20), - name Varchar(255), - description Text) TYPE = MyISAM; - -Create table test_attachment_data ( - attachment_id Bigint UNSIGNED NOT NULL, - contents Longblob) TYPE = MyISAM; - -Create table test_named_queries ( - userid Mediumint NOT NULL, - name Varchar(64) NOT NULL, - isvisible Bool NOT NULL, - query Mediumtext NOT NULL) TYPE = MyISAM; - - - -Create Index category_template_name_idx ON test_category_templates (name); -Create Index attachment_plan_idx ON test_attachments (plan_id); -Create Index attachment_case_idx ON test_attachments (case_id); -Create Index category_name_indx ON test_case_categories (name); -Create Index test_case_category_idx ON test_cases (category_id); -Create Index test_case_author_idx ON test_cases (author_id); -Create Index test_case_creation_date_idx ON test_cases (creation_date); -Create Index test_case_sortkey_idx ON test_cases (sortkey); -Create Index test_case_requirment_idx ON test_cases (requirement); -Create Index test_case_shortname_idx ON test_cases (alias); -Create UNIQUE Index case_run_id_idx ON test_case_bugs (case_run_id,bug_id); -Create Index case_run_bug_id_idx ON test_case_bugs (bug_id); -Create Index case_run_run_id_idx ON test_case_runs (run_id); -Create Index case_run_case_id_idx ON test_case_runs (case_id); -Create Index case_run_assignee_idx ON test_case_runs (assignee); -Create Index case_run_testedby_idx ON test_case_runs (testedby); -Create Index case_run_close_date_idx ON test_case_runs (close_date); -Create Index case_run_sortkey_idx ON test_case_runs (sortkey); -Create UNIQUE Index case_versions_idx ON test_case_texts (case_id,case_text_version); -Create Index case_versions_who_idx ON test_case_texts (who); -Create Index case_versions_creation_ts_idx ON test_case_texts (creation_ts); -Create Index case_tags_case_id_idx ON test_case_tags (case_id,tag_id); -Create Index case_tags_tag_id_idx ON test_case_tags (tag_id,case_id); -Create Index test_tag_name_idx ON test_tags (tag_name); -Create Index plan_product_plan_id_idx ON test_plans (product_id,plan_id); -Create Index plan_author_idx ON test_plans (author_id); -Create Index plan_type_idx ON test_plans (type_id); -Create Index plan_editor_idx ON test_plans (editor_id); -Create Index plan_isactive_idx ON test_plans (isactive); -Create Index plan_name_idx ON test_plans (name); -Create Index test_plan_text_version_idx ON test_plan_texts (plan_id,plan_text_version); -Create Index test_plan_text_who_idx ON test_plan_texts (who); -Create Index plan_type_name_idx ON test_plan_types (name); -Create Index test_run_plan_id_run_id__idx ON test_runs (plan_id,run_id); -Create Index test_run_manager_idx ON test_runs (manager_id); -Create Index test_run_start_date_idx ON test_runs (start_date); -Create Index test_run_stop_date_idx ON test_runs (stop_date); -Create Index case_plans_plan_id_idx ON test_case_plans (plan_id); -Create Index case_plans_case_id_idx ON test_case_plans (case_id); -Create Index case_activity_case_id_idx ON test_case_activity (case_id); -Create Index case_activity_who_idx ON test_case_activity (who); -Create Index case_activity_when_idx ON test_case_activity (changed); -Create Index case_activity_field_idx ON test_case_activity (fieldid); -Create Index fielddefs_name_idx ON test_fielddefs (name); -Create Index plan_activity_who_idx ON test_plan_activity (who); -Create Index case_components_case_id_idx ON test_case_components (case_id); -Create Index case_components_component_id_idx ON test_case_components (component_id); -Create Index run_activity_run_id_idx ON test_run_activity (run_id); -Create Index run_activity_who_idx ON test_run_activity (who); -Create Index run_activity_when_idx ON test_run_activity (changed); -Create Index run_cc_run_id_who_idx ON test_run_cc (run_id,who); -Create Index test_event_user_event_dx ON test_email_settings (userid,eventid); -Create Index test_event_user_relationship_idx ON test_email_settings (userid,relationship_id); -Create Index test_event_name_idx ON test_events (name); -Create Index case_run_status_name_idx ON test_case_run_status (name); -Create Index case_run_status_sortkey_idx ON test_case_run_status (sortkey); -Create Index test_case_status_name_idx ON test_case_status (name); -Create Index environment_op_sys_idx ON test_environments (op_sys_id); -Create Index environment_platform_idx ON test_environments (rep_platform_id); -Create UNIQUE Index environment_name_idx ON test_environments (name); -Create UNIQUE Index run_tags_idx ON test_run_tags (tag_id,run_id); -Create UNIQUE Index plan_tags_idx ON test_plan_tags (tag_id,plan_id); -Create Index test_namedquery_name_idx ON test_named_queries (name); - -LOCK TABLES `test_case_run_status` WRITE; -INSERT INTO `test_case_run_status` VALUES (1,'IDLE',1),(2,'PASSED',2),(3,'FAILED',3),(4,'RUNNING',4),(5,'PAUSED',5),(6,'BLOCKED',6); -UNLOCK TABLES; - -LOCK TABLES `test_case_status` WRITE; -INSERT INTO `test_case_status` VALUES (1,'PROPOSED'),(2,'CONFIRMED'),(3,'DISABLED'); -UNLOCK TABLES; - -LOCK TABLES `test_plan_types` WRITE; -INSERT INTO `test_plan_types` VALUES (1,'Unit'),(2,'Integration'),(3,'Function'),(4,'System'),(5,'Acceptance'),(6,'Installation'),(7,'Performance'),(8,'Product'),(9,'Interoperability'); -UNLOCK TABLES; - -LOCK TABLES `test_fielddefs` WRITE; -INSERT INTO `test_fielddefs` VALUES (1,'isactive','Archived','test_plans'),(2,'name','Plan Name','test_plans'),(3,'type_id','Plan Type','test_plans'),(4,'case_status_id','Case Status','test_cases'),(5,'category_id','Category','test_cases'),(6,'priority_id','Priority','test_cases'),(7,'summary','Run Summary','test_cases'),(8,'isautomated','Automated','test_cases'),(9,'alias','Alias','test_cases'),(10,'requirement','Requirement','test_cases'),(11,'script','Script','test_cases'),(12,'arguments','Argument','test_cases'),(13,'product_id','Product','test_plans'),(14,'default_product_version','Default Product Version','test_plans'),(15,'environment_id','Environment','test_runs'),(16,'product_version','Product Version','test_runs'),(17,'build_id','Default Build','test_runs'),(18,'plan_text_version','Plan Text Version','test_runs'),(19,'manager_id','Manager','test_runs'),(20,'default_tester_id','Default Tester','test_cases'),(21,'stop_date','Stop Date','test_runs'),(22,'summary','Run Summary','test_runs'),(23,'notes','Notes','test_runs'); -UNLOCK TABLES; diff --git a/mozilla/webtools/testopia/testopia/testopia.drop.sql b/mozilla/webtools/testopia/testopia/testopia.drop.sql index 8cb3ff6940d..b0d231d3d96 100644 --- a/mozilla/webtools/testopia/testopia/testopia.drop.sql +++ b/mozilla/webtools/testopia/testopia/testopia.drop.sql @@ -1,11 +1,37 @@ drop table if exists - test_attachment_data, test_attachments, test_builds, - test_case_activity, test_case_bugs, test_case_categories, - test_case_components, test_case_dependencies, test_case_group_map, - test_case_plans, test_case_run_status, test_case_runs, - test_case_status, test_case_tags, test_case_texts, test_cases, - test_category_templates, test_email_settings, test_environments, - test_events, test_fielddefs, test_named_queries, test_plan_activity, - test_plan_tags, test_plan_testers, test_plan_texts, test_plan_types, - test_plans, test_relationships, test_run_activity, test_run_cc, - test_run_tags, test_runs, test_tags; + test_attachment_data, + test_attachments, + test_builds, + test_case_activity, + test_case_bugs, + test_case_categories, + test_case_components, + test_case_dependencies, + test_case_plans, + test_case_run_status, + test_case_runs, + test_case_status, + test_case_tags, + test_case_texts, + test_cases, + test_email_settings, + test_environment_category, + test_environment_element, + test_environment_map, + test_environment_property, + test_environments, + test_events, + test_fielddefs, + test_named_queries, + test_plan_activity, + test_plan_group_map, + test_plan_tags, + test_plan_texts, + test_plan_types, + test_plans, + test_relationships, + test_run_activity, + test_run_cc, + test_run_tags, + test_runs, + test_tags; diff --git a/mozilla/webtools/testopia/testopia/testopia.insert.sql b/mozilla/webtools/testopia/testopia/testopia.insert.sql new file mode 100644 index 00000000000..8fdd5efde1b --- /dev/null +++ b/mozilla/webtools/testopia/testopia/testopia.insert.sql @@ -0,0 +1,41 @@ +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (1, 'IDLE', 1) +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (2, 'PASSED', 2) +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (3, 'FAILED', 3) +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (4, 'RUNNING', 4) +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (5, 'PAUSED', 5) +INSERT INTO test_case_run_status (case_run_status_id, name, sortkey) VALUES (6, 'BLOCKED', 6) +INSERT INTO test_case_status (case_status_id, name) VALUES (1, 'PROPOSED') +INSERT INTO test_case_status (case_status_id, name) VALUES (2, 'CONFIRMED') +INSERT INTO test_case_status (case_status_id, name) VALUES (3, 'DISABLED') +INSERT INTO test_plan_types (type_id, name) VALUES (1, 'Unit') +INSERT INTO test_plan_types (type_id, name) VALUES (2, 'Integration') +INSERT INTO test_plan_types (type_id, name) VALUES (3, 'Function') +INSERT INTO test_plan_types (type_id, name) VALUES (4, 'System') +INSERT INTO test_plan_types (type_id, name) VALUES (5, 'Acceptance') +INSERT INTO test_plan_types (type_id, name) VALUES (6, 'Installation') +INSERT INTO test_plan_types (type_id, name) VALUES (7, 'Performance') +INSERT INTO test_plan_types (type_id, name) VALUES (8, 'Product') +INSERT INTO test_plan_types (type_id, name) VALUES (9, 'Interoperability') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (1, 'isactive', 'Archived', 'test_plans') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (2, 'name', 'Plan Name', 'test_plans') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (3, 'type_id', 'Plan Type', 'test_plans') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (4, 'case_status_id', 'Case Status', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (5, 'category_id', 'Category', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (6, 'priority_id', 'Priority', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (7, 'summary', 'Run Summary', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (8, 'isautomated', 'Automated', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (9, 'alias', 'Alias', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (10, 'requirement', 'Requirement', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (11, 'script', 'Script', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (12, 'arguments', 'Argument', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (13, 'product_id', 'Product', 'test_plans') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (14, 'default_product_version', 'Default Product Version', 'test_plans') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (15, 'environment_id', 'Environment', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (16, 'product_version', 'Product Version', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (17, 'build_id', 'Default Build', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (18, 'plan_text_version', 'Plan Text Version', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (19, 'manager_id', 'Manager', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (20, 'default_tester_id', 'Default Tester', 'test_cases') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (21, 'stop_date', 'Stop Date', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (22, 'summary', 'Run Summary', 'test_runs') +INSERT INTO test_fielddefs (fieldid, name, description, table_name) VALUES (23, 'notes', 'Notes', 'test_runs') diff --git a/mozilla/webtools/testopia/tr_admin.cgi b/mozilla/webtools/testopia/tr_admin.cgi new file mode 100755 index 00000000000..d1a46a1054c --- /dev/null +++ b/mozilla/webtools/testopia/tr_admin.cgi @@ -0,0 +1,102 @@ +#!/usr/bin/perl -wT +# -*- 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) 2001 +# Greg Hendricks. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; +use lib "."; + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::Util; + +use vars qw($vars); +my $template = Bugzilla->template; + +Bugzilla->login(LOGIN_REQUIRED); + +print Bugzilla->cgi->header(); +ThrowUserError("testopia-read-only", {'object' => 'plan type'}) unless Bugzilla->user->in_group('admin'); + +my $dbh = Bugzilla->dbh; +my $cgi = Bugzilla->cgi; + +my $plan = Bugzilla::Testopia::TestPlan->new({'plan_id' => 0}); +my $action = $cgi->param('action') || ''; +my $item = $cgi->param('item') || ''; + +if ($item eq 'plan_type'){ + if( $action eq 'edit'){ + my $type_id = $cgi->param('type_id'); + detaint_natural($type_id); + $vars->{'type'} = $plan->lookup_plan_type($type_id) + || ThrowUserError("invalid-test-id-non-existent", {'id' => $type_id, 'type' => 'Plan Type'}); + $template->process("testopia/admin/plantypes/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($action eq 'doedit'){ + my $type_id = $cgi->param('type_id'); + my $type_name = $cgi->param('name') || ''; + + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}) if $type_name eq ''; + detaint_natural($type_id); + ThrowUserError("invalid-test-id-non-existent", + {'id' => $type_id, 'type' => 'Plan Type'}) unless $plan->lookup_plan_type($type_id); + trick_taint($type_name); + + $plan->update_plan_type($type_id, $type_name); + display(); + } + elsif ($action eq 'add'){ + $template->process("testopia/admin/plantypes/add.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + elsif ($action eq 'doadd'){ + my $type_name = $cgi->param('name') || ''; + + ThrowUserError('testopia-missing-required-field', {'field' => 'name'}) if $type_name eq ''; + trick_taint($type_name); + ThrowUserError('testopia-name-not-unique', + {'object' => 'Plan Type', 'name' => $type_name}) if $plan->check_plan_type($type_name); + + $plan->add_plan_type($type_name); + display(); + } + else{ + display(); + } + +} + +else { + $template->process("testopia/admin/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} + +sub display { + $vars->{'plan'} = $plan; + $template->process("testopia/admin/plantypes/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} diff --git a/mozilla/webtools/testopia/tr_admin_environment.cgi b/mozilla/webtools/testopia/tr_admin_environment.cgi new file mode 100755 index 00000000000..bd169da06aa --- /dev/null +++ b/mozilla/webtools/testopia/tr_admin_environment.cgi @@ -0,0 +1,557 @@ +#!/usr/bin/perl -wT +# -*- 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) 2001 +# Greg Hendricks. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden +# Scott Sudweeks +# Brian Kramer + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Classification; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Property; +use Bugzilla::Testopia::Environment::Category; + +use JSON; +use Data::Dumper; + +Bugzilla->login(LOGIN_REQUIRED); +Bugzilla->batch(1); + +my $cgi = Bugzilla->cgi; + +use vars qw($vars $template); +my $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $env_id = $cgi->param('env_id') || 0; + +if($action eq 'getChildren'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + + my $node = $data->{'node'}; + + my $id = $node->{'objectId'}; + my $type = $node->{'widgetId'}; + + detaint_natural($id); + trick_taint($type); + + + for ($type){ + /classification/ && do { get_products($id); }; + /product/ && do { get_categories($id); }; + /category/ && do { get_category_element($id) }; + /element/ && do { get_element_children($id); }; + /property/ && do { get_validexp_json($id) }; + } +} + +elsif($action eq 'edit'){ + my $type = $cgi->param('type'); + my $id = $cgi->param('id'); + + for ($type){ + /category/ && do { edit_category($id); }; + /element/ && do { edit_element($id); }; + /property/ && do { edit_property($id); }; + /validexp/ && do { edit_validexp($id); }; + } +} + +elsif($action eq 'do_edit'){ + my $type = $cgi->param('type'); + my $id = $cgi->param('id'); + + for ($type){ + /category/ && do { do_edit_category($id); }; + /element/ && do { do_edit_element($id); }; + /property/ && do { do_edit_property($id); }; + /validexp/ && do { do_edit_validexp($id); }; + } +} + +elsif($action eq 'createChild'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + + my $node = $data->{'parent'}; + + my $id = $node->{'objectId'}; + my $type = $node->{'widgetId'}; + + my $create_type = $data->{'data'}->{'createType'}; + $type = 'child' if ($create_type && $type !~ /category/); + + detaint_natural($id); + trick_taint($type); + + + for ($type){ + /product/ && do { add_category($id); }; + /category/ && do { add_element($id); }; + /child/ && do { add_element($id,1); }; + /element/ && do { add_property($id); }; + /property/ && do { add_validexp($id); }; + } + +} + +elsif($action eq 'removeNode'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + my $node = $data->{'node'}; + + my $id = $node->{'objectId'}; + my $type = $node->{'widgetId'}; + + detaint_natural($id) unless $type =~ /validexp/; + trick_taint($type); + + for ($type){ + /category/ && do { delete_category($id); }; + /element/ && do { delete_element($id); }; + /property/ && do { delete_property($id); }; + /validexp/ && do { delete_validexp($id); }; + } +} + +elsif ($action eq 'getcategories'){ + my $product_id = $cgi->param('product_id'); + detaint_natural($product_id); + my $cat = Bugzilla::Testopia::Environment::Category({}); + my $categories = $cat->get_element_categories_by_product($product_id); + my $ret; + foreach my $c (@{$categories}){ + $c->name =~ s/|<\/span>//g; + $ret .= $c->id.'||'.$c->name.'|||'; + } + chop($ret); + print $ret; +} + +elsif ($action eq 'getelements'){ + my $cat_name = $cgi->param('cat_name'); + my $cat = Bugzilla::Testopia::Environment::Category->new($cat_name); + + my $elements = $cat->get_elements_by_category(); + my $ret; + foreach my $e (@{$elements}){ + my $elem = Bugzilla::Testopia::Environment::Element->new(@$e{'element_id'}); + $elem->{'name'} =~ s/|<\/span>//g; + $ret .= $elem->{'element_id'}.'||'.$elem->{'name'}.'|||'; + } + $ret = substr($ret, 0, length($ret) - 3); + print $ret; +} + +elsif ($action eq 'getproperties'){ + my $env = Bugzilla::Testopia::Environment->new({}); + my $elem_id = $cgi->param('elem_id'); + + + detaint_natural($elem_id); + + my $properties = $env->get_properties_by_element_id($elem_id); + my $ret; + foreach my $p (@{$properties}){ + @$p[1] =~ s/|<\/span>//g; + $ret .= @$p[0].'||'.@$p[1].'|||'; + } + chop($ret); + print $ret; +} + +else{ + &display; +} + +sub display{ + my $category = Bugzilla::Testopia::Environment::Category->new({'id' => 0}); + if (Param('useclassification')){ + $vars->{'allhaschild'} = $category->get_all_child_count; + $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; + $vars->{'type'} = 'classification'; + } + else { + $vars->{'toplevel'} = $category->get_env_product_list; + $vars->{'type'} = 'product'; + } + + $template->process("testopia/environment/admin/show.html.tmpl", $vars) + || print $template->error(); +} + +########################### +### Tree Helper Methods ### +########################### +sub get_products{ + my ($class_id) = (@_); + my $class = Bugzilla::Testopia::Classification->new($class_id); + print $class->products_to_json; +} + +sub get_categories{ + my ($product_id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new({}); + print $category->product_categories_to_json($product_id); +} + +sub get_category_element{ + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + print $category->elements_to_json; +} + +sub get_element_children { + my ($id) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + print $element->children_to_json; +} + +sub get_validexp_json { + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + print $property->valid_exp_to_json; +} + + +########################### +### Edit Helper Methods ### +########################### +sub edit_category{ + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + my $product = Bugzilla::Testopia::Product->new($category->product_id()); + $category->{'name'} =~ s/|<\/span>//g; + + $vars->{'category'} = $category; + $vars->{'products'} = $category->get_env_product_list; + $vars->{'currentproduct'} = $product ? $product : {'id' => 0, 'name' => '--ALL--'}; + $template->process("testopia/environment/admin/category.html.tmpl", $vars) + || print $template->error(); +} + +sub edit_element{ + my ($id) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + my $category = Bugzilla::Testopia::Environment::Category->new($element->env_category_id()); + my $product = Bugzilla::Testopia::Product->new($category->product_id()); + $element->{'name'} =~ s/|<\/span>//g; + + $vars->{'element'} = $element; + $vars->{'products'} = $category->get_env_product_list; + $vars->{'currentproduct'} = $product ? $product : {'id' => 0, 'name' => '--ALL--'}; + $vars->{'currentcategory'} = $category; + $vars->{'categories'} = $category->get_element_categories_by_product($product->{'id'}); + unless($element->parent_id() == 0){ + my $parent = Bugzilla::Testopia::Environment::Element->new($element->parent_id()); + $vars->{'currentelement'} = $parent; + } + $vars->{'elements'} = $category->get_elements_by_category(); + + $template->process("testopia/environment/admin/element.html.tmpl", $vars) + || print $template->error(); +} + +sub edit_property{ + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + my $element = Bugzilla::Testopia::Environment::Element->new($property->element_id()); + my $cat_id = $element->env_category_id(); + my $elmnts = Bugzilla::Testopia::Environment::Category->new($cat_id)->get_elements_by_category(); + + $vars->{'property'} = $property; + $vars->{'elements'} = \@$elmnts; + $vars->{'currentelement'} = $element; + $template->process("testopia/environment/admin/property.html.tmpl", $vars); +} + +sub edit_validexp{ + my ($id) = (@_); + $id =~ /^(\d+)~/; + + my $property = Bugzilla::Testopia::Environment::Property->new($1); + + my @expressions = split /\|/, $property->validexp(); + + $vars->{'property'} = $property; + $vars->{'expressions'} = \@expressions; + $template->process("testopia/environment/admin/valid_exp.html.tmpl", $vars); +} + + +############################## +### Do_Edit Helper Methods ### +############################## +sub do_edit_category{ + my $name = $cgi->param('name'); + my $product_id = $cgi->param('product'); + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + + trick_taint($name); + detaint_natural($product_id); + + eval{ + validate_selection($product_id, 'id', 'products'); + }; + if ($@ && $product_id != 0){ + print '{error:"Invalid product"}'; + exit; + } + $category->set_product($product_id); + + unless ($category->set_name($name)) { + print '{error:"Name already used. Please choose another"}'; + exit; + } + + print "{name:\"$name\", product:\"product$product_id\", widget:\"category$id\"}"; + +} + +sub do_edit_element{ + my ($id) = (@_); + + # + # CGI params + # productCombo -> id of product (does not matter what this is, only used to find categories) + # categoryCombo -> id of category (if zero, leave the same) + # elementCombo -> id of parent element (id of 0 means no parent) + # name -> name of the element + # + + my $element = Bugzilla::Testopia::Environment::Element->new($id); + + my $cat_id = $cgi->param('categoryCombo'); + my $parent_id = $cgi->param('elementCombo'); + my $name = $cgi->param('name'); + my $parent; + + detaint_natural($cat_id); + detaint_natural($parent_id); + trick_taint($name); + + if ($cat_id){ + $element->update_element_category($cat_id); + $parent = 'category' . $cat_id; + } + else { + print '{error: "Category does not exist"}'; + exit; + } + if ($parent_id){ + $element->update_element_parent($parent_id); + $parent = 'element' . $parent_id; + } + elsif (!$cat_id) { + print '{error: "Parent element does not exist"}'; + exit; + } + unless ($element->update_element_name($name)){ + print '{error: "Name already taken. Please choose another."}'; + exit; + } + + print "{name:\"$name\", parent:\"$parent\", widget:\"element$id\"}"; +} + +sub do_edit_property{ + my ($id) = (@_); + my $name = $cgi->param('name'); + my $element_id = $cgi->param('element'); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + + trick_taint($name); + detaint_natural($element_id); + + eval{ + validate_selection($element_id, 'element_id', 'test_environment_element'); + }; + if ($@){ + print '{error:"Invalid element"}'; + exit; + } + $property->set_element($element_id); + + unless ($property->set_name($name)) { + print '{error:"Name already used. Please choose another"}'; + exit; + } + + print "{name:\"$name\", element:\"element$element_id\", widget:\"property$id\"}"; +} + +sub do_edit_validexp{ + my ($id) = (@_); + + my $property = Bugzilla::Testopia::Environment::Property->new($id); + my @expressions = $cgi->param('valid_exp'); + + my $exp; + foreach my $e (@expressions){ + trick_taint($e); + $exp .= $e.'|'; + } + + $property->update_property_validexp($exp); + display; +} + + + +################################### +### Create Child Helper Methods ### +################################### +sub add_category{ + my ($id) = (@_); + + my $category = Bugzilla::Testopia::Environment::Category->new({}); + $category->{'product_id'} = $id; + $category->{'name'} = 'New category ' . $category->new_category_count; + + my $new_cid = $category->store(); + + my $json = '{title:"' . $category->{'name'} . '",'; + $json .= 'objectId:"'. $new_cid . '",'; + $json .= 'widgetId:"category' . $new_cid . '",'; + $json .= 'childIconSrc:"testopia/img/square.gif",'; + $json .= 'actionsDisabled:["addCategory","addValue","addProperty"]}'; + + print $json; +} + +sub add_element{ + my ($id, $ischild) = (@_); + + my $element = Bugzilla::Testopia::Environment::Element->new({}); + # If we are adding this element as a child, $id is the parent element's id + if ($ischild) { + my $parent = Bugzilla::Testopia::Environment::Element->new($id); + $element->{'env_category_id'} = $parent->env_category_id; + } + # Otherwise $id is the catagory id + else { + $element->{'env_category_id'} = $id; + } + $element->{'name'} = "New element " . $element->new_element_count; + $element->{'parent_id'} = $ischild ? $id : 0; + $element->{'isprivate'} = 0; + + my $new_eid = $element->store(); + + my $json = '{title:"' . $element->{'name'} .'",'; + $json .= 'objectId:"'. $new_eid .'",'; + $json .= 'widgetId:"element'. $new_eid .'",'; + $json .= 'childIconSrc:"testopia/img/circle.gif",'; + $json .= 'actionsDisabled:["addCategory","addValue"]}'; + + print $json; +} + +sub add_property{ + my ($id) = (@_); + #add new property to element with id=$id + my $property = Bugzilla::Testopia::Environment::Property->new({}); + $property->{'element_id'} = $id; + $property->{'name'} = "New property " . $property->new_property_count; + $property->{'validexp'} = ""; + + my $new_pid = $property->store(); + + my $json = '{title:"' . $property->{'name'} . '",'; + $json .= 'objectId:"' . $new_pid . '",'; + $json .= 'widgetId:"property' . $new_pid . '",'; + $json .= 'childIconSrc:"testopia/img/triangle.gif",'; + $json .= 'actionsDisabled:["addCategory","addElement","addProperty"]}'; + + print $json; +} + +sub add_validexp{ + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + + my $exp = $property->validexp; + $exp ? $property->update_property_validexp($exp . "|New value") : $property->update_property_validexp("New value"); + + my $json .= '{title: "New value",'; + $json .= 'objectId:"' . $id . '~New Value",'; + $json .= 'widgetId:"validexp' . $id . '~New Value",'; + $json .= 'actionsDisabled:["addCategory","addElement","addProperty","addValue"]}'; + + print $json; +} + +############################# +### Delete Helper Methods ### +############################# +sub delete_category{ + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new({}); + my $success = $category->obliterate; + print $success == 1 ? "true" : "false"; +} + +sub delete_element{ + my ($id) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + my $success = $element->obliterate; + print $success == 1 ? "true" : "false"; +} + +sub delete_property{ + my ($id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + my $success = $property->obliterate; + print $success == 1 ? "true" : "false"; +} + +sub delete_validexp{ + # $id, $type + my ($id) = (@_); + $id =~ /^(\d+)~/; + my $property = Bugzilla::Testopia::Environment::Property->new($1); + my %values; + foreach my $v (split /\|/, $property->validexp){ + $values{$v} = 1; + } + $id =~ /~(.+)$/; + my $value = $1; + my $deleted = delete $values{$value}; + my $exp = join("|", keys %values); + $property->update_property_validexp($exp); + print $deleted ? "true" : "false"; +} diff --git a/mozilla/webtools/testopia/tr_case_dependencies.cgi b/mozilla/webtools/testopia/tr_case_dependencies.cgi index 8254759a8fa..e69de29bb2d 100755 --- a/mozilla/webtools/testopia/tr_case_dependencies.cgi +++ b/mozilla/webtools/testopia/tr_case_dependencies.cgi @@ -1,43 +0,0 @@ -#!/usr/bin/perl -wT -# -*- 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 - -use strict; -use lib "."; - -use Bugzilla; -use Bugzilla::Util; -use Bugzilla::Error; -use Bugzilla::Config; -use Bugzilla::Testopia::TestCase; -use Bugzilla::Testopia::Util; - -Bugzilla->login(); - -my $cgi = Bugzilla->cgi; - -print $cgi->header; -use vars qw($vars $template); -my $template = Bugzilla->template; - -push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; - -my $case = Bugzilla::Testopia::TestCase->new(1); -print join(",", @{$case->get_dep_tree}); diff --git a/mozilla/webtools/testopia/tr_defparams.pl b/mozilla/webtools/testopia/tr_defparams.pl deleted file mode 100644 index 3f21295d94e..00000000000 --- a/mozilla/webtools/testopia/tr_defparams.pl +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/perl -wT -# -*- 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 -# Ed Fuentetaja - -# Test Runner params: - -use strict; -use vars qw(@param_list); - -push @param_list, -( - { - name => 'private-cases-log', - desc => "If this option is on, the tester cannot view other testers' cases", - type => 'b', - default => 0, - }, - - { - name => 'allow-test-deletion', - desc => "If this option is on, users can delete objects including plans and cases", - type => 'b', - default => 0, - }, - - { - name => 'print-tag-in-case-log', - desc => '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.', - type => 'b', - default => 0, - }, - - { - name => 'new-case-action-template', - desc => "This is the text to be put in a newly created test case \"action\" field", - type => 'l', - default => qq{
    -
  1. -
}, - }, - - { - name => 'new-case-effect-template', - desc => "This is the text to be put in a newly created test case \"effect\" field", - type => 'l', - default => qq{
    -
  1. -
}, - }, - - { - name => 'bug-to-test-case-summary', - desc => 'This is the default summary 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", - type => 'l', - default => 'Check bug %id%', - }, - - { - name => 'bug-to-test-case-action', - desc => '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 summary", - type => 'l', - default => 'Verify if a bug %id% is fixed' - }, - - { - name => 'bug-to-test-case-effect', - desc => 'This is the default effect 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", - type => 'l', - default => 'Bug %id% is fixed', - }, - - { - name => 'default-test-case-status', - desc => 'Default status for newly created test cases.', - type => 's', - choices => ['PROPOSED', 'CONFIRMED', 'DISABLED'], - default => 'PROPOSED' - }, - - { - name => 'new-testrun-email-notif', - desc => 'E-mail message sent to assigned testers when a new test run is started. '. - 'There are some special symbols replaced at run time:
'. - '%to%: list of assigned testers email addresses
'. - '%summary%: test run summary
'. - '%plan%: plan\'s name
'. - '%plan_id%: plan\'s id
'. - '%product%: product\'s name
'. - '%product_id%: product\'s id', - 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', - desc => 'E-mail message sent when a test case log is marked as \'failed\'. '. - 'There are some special symbols replaced at run time:
'. - '%id%: test case log id
'. - '%manager%: test run\'s manager
'. - '%test_run%: test run\'s summary
'. - '%tester%: tester
'. - '%component%: component\'s name', - 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', - desc => 'E-mail message sent when a tester has run every assigned test case.', - 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 => 'component-completed-email-notif', - desc => 'E-mail message sent when every test case of a component is run.', - type => 'l', - default => 'From: bugzilla-daemon'."\n". - 'To: %manager%'."\n". - 'Subject: Test run completed for component.'."\n". - "\n". - 'Test run \'%test_run%\' completed for component \'%component%\'.' - }, - - { - name => 'test-run-completed-email-notif', - desc => 'E-mail message sent when every test case in a test run is completed.', - type => 'l', - default => 'From: bugzilla-daemon'."\n". - 'To: %manager%'."\n". - 'Subject: Test run completed.'."\n". - "\n". - 'Test run \'%test_run%\' completed.' - } -); - -1; - - diff --git a/mozilla/webtools/testopia/tr_environment.cgi b/mozilla/webtools/testopia/tr_environment.cgi deleted file mode 100755 index 5e029bf217d..00000000000 --- a/mozilla/webtools/testopia/tr_environment.cgi +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/perl -wT -# -*- 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 - -use strict; -use lib "."; - -use Bugzilla; -use Bugzilla::Util; -use Bugzilla::Config; -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::Testopia::Util; -use Bugzilla::Testopia::TestRun; -use Bugzilla::Testopia::Environment; - -Bugzilla->login(LOGIN_REQUIRED); - -my $cgi = Bugzilla->cgi; - -use vars qw($vars $template); -my $template = Bugzilla->template; - -print $cgi->header; - -my $action = $cgi->param('action') || ''; -my $env_id = $cgi->param('env_id') || 0; - -if ($action eq 'add'){ - my $env = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - $vars->{'environment'} = $env; - $vars->{'action'} = "do_add"; - $template->process("testopia/environment/add.html.tmpl", $vars) - || print $template->error(); -} - -elsif ($action eq 'do_add'){ - my $name = $cgi->param('name'); - my $os = $cgi->param('op_sys'); - my $platform = $cgi->param('rep_platform'); - my $xml = $cgi->param('xml'); - detaint_natural($os); - detaint_natural($platform); - trick_taint($name); - trick_taint($xml); - validate_selection( $os, 'id', 'op_sys'); - validate_selection($platform, 'id', 'rep_platform'); - - my $env = Bugzilla::Testopia::Environment->new( - { 'name' => $name, - 'op_sys_id' => $os, - 'rep_platform_id' => $platform, - 'xml' => $xml - }); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - my $success = $env->store; - unless ($success){ - $vars->{'tr_error'} = "Environment Name $name already taken"; - $vars->{'environment'} = $env; - $template->process("testopia/environment/add.html.tmpl", $vars) - || print $template->error(); - exit; - } - $vars->{'tr_message'} = "Environment $name Added"; - display_list(); -} - -elsif ($action eq 'edit'){ - my $env = Bugzilla::Testopia::Environment->new($env_id); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - $vars->{'action'} = 'do_edit'; - $vars->{'environment'} = $env; - $template->process("testopia/environment/show.html.tmpl", $vars) - || print $template->error(); -} - -elsif ($action eq 'do_edit'){ - my $name = $cgi->param('name'); - my $os = $cgi->param('op_sys'); - my $platform = $cgi->param('rep_platform'); - my $xml = $cgi->param('xml'); - detaint_natural($os); - detaint_natural($platform); - trick_taint($name); - trick_taint($xml); - validate_selection( $os, 'id', 'op_sys'); - validate_selection($platform, 'id', 'rep_platform'); - my $env = Bugzilla::Testopia::Environment->new($env_id); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - my %newvalues = - ( 'name' => $name, - 'op_sys_id' => $os, - 'rep_platform_id' => $platform, - 'xml' => $xml - ); - my $success = $env->update(\%newvalues); - unless ($success){ - $vars->{'tr_error'} = "Environment Name $name already taken"; - $vars->{'environment'} = $env; - $template->process("testopia/environment/show.html.tmpl", $vars) - || print $template->error(); - exit; - } - $vars->{'tr_message'} = "Environment $name Updated"; - display_list(); - -} - -elsif ($action eq 'delete'){ - my $env = Bugzilla::Testopia::Environment->new($env_id); - ThrowUserError('testopia-no-delete', {'object' => 'Environment'}) unless Param("allow-test-deletion"); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - $vars->{'environment'} = $env; - $template->process("testopia/environment/delete.html.tmpl", $vars) - || print $template->error(); - -} - -elsif ($action eq 'do_delete'){ - my $env = Bugzilla::Testopia::Environment->new($env_id); - ThrowUserError('testopia-no-delete', {'object' => 'Environment'}) unless Param("allow-test-deletion"); - ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; - ThrowUserError("testopia-non-zero-run-count", {'object' => 'Environment'}) if $env->get_run_count; - $env->obliterate; - $vars->{'tr_message'} = "Environment Deleted"; - display_list(); -} - -else { - display_list(); -} - -sub display_list { - my $dbh = Bugzilla->dbh; - my $ref = $dbh->selectcol_arrayref("SELECT environment_id FROM test_environments"); - my @envs; - foreach my $id (@{$ref}){ - push @envs, Bugzilla::Testopia::Environment->new($id); - } - $vars->{'environments'} = \@envs; - $template->process("testopia/environment/list.html.tmpl", $vars) - || print $template->error(); -} diff --git a/mozilla/webtools/testopia/tr_export_environment.cgi b/mozilla/webtools/testopia/tr_export_environment.cgi new file mode 100755 index 00000000000..f3b665ac467 --- /dev/null +++ b/mozilla/webtools/testopia/tr_export_environment.cgi @@ -0,0 +1,68 @@ +#!/usr/bin/perl -wT +# -*- 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 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): Garrett Braden + +# This script expects an env_id in the query string pointing to the +# environment that you want to export to XML and displays the XML in +# a html textarea. + + +=head1 NAME + +tr_export_environment.cgi + +=head1 DESCRIPTION + +Exports an Environment by env_id from the database to XML and displays the XML in +a html textarea. + +=cut + +#************************************************** Uses ****************************************************# +use strict; +use CGI; +use lib "."; +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Xml; +use vars qw($vars); + +#************************************ Variable Declarations/Initialization **********************************# +Bugzilla->login(LOGIN_REQUIRED); +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; +my $env_id = $cgi->param('env_id'); + +#********************************************* UI Logic ************************************************# +print $cgi->header; + +my $xml = Bugzilla::Testopia::Environment::Xml->export($env_id); +if (!defined($xml)) { + $vars->{'tr_error'} .= "Exporting XML Environment Failed. Please try again.
"; +} +$vars->{'xml'} = $xml; +$template->process("testopia/environment/export.xml.tmpl", $vars) || print $template->error(); diff --git a/mozilla/webtools/testopia/tr_import_environment.cgi b/mozilla/webtools/testopia/tr_import_environment.cgi new file mode 100755 index 00000000000..9d90ae4b0f5 --- /dev/null +++ b/mozilla/webtools/testopia/tr_import_environment.cgi @@ -0,0 +1,347 @@ +#!/usr/bin/perl -wT +# -*- 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 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): Garrett Braden + +# This script reads in an environment in xml and persists it to the database. + + +=head1 NAME + +tr_import_environment.cgi - Imports an environment from a XML file into the database. + +=head1 DESCRIPTION + +Administrator's and testopia users can import an environment from XML into the database. +A tool is being designed to automatically scan a system and create such an XML file to +import. + +=cut + +#************************************************** Uses ****************************************************# +use strict; +use CGI; +use lib "."; +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Xml; +use Data::Dumper; +use vars qw($vars); + +#************************************ Variable Declarations/Initialization **********************************# +Bugzilla->login(LOGIN_REQUIRED); +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; +my $upload_dir = "testopia/temp"; +my $env_filename = $cgi->param('env_file'); +my $env_fh = $cgi->upload('env_file'); +my $action = $cgi->param('action') || ''; +my $xml = $cgi->param('xml'); +my $submit = $cgi->param('submit'); +my $environment = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); +my $user_can_edit = $environment->canedit; +#my $user_can_edit = 0; # Remove on production <- used to toggle user's rights. +my $message = ''; +$CGI::POST_MAX = 1024 * 500; # max file size 500K + +#***************************************** UI Logic ************************************************************# +print $cgi->header; +# Make sure the file isn't too big. +if (!$env_filename && $cgi->cgi_error()) { + $vars->{'tr_error'} .= "File size cannot exceed 500K.
"; +} +# Upload the file and read it into a string if it's been posted. +if ($action eq 'import' && $env_filename) { + trick_taint($env_filename); + # Continue only if import exits without errors, otherwise display the errors. + if (slurp_env_file() && $xml && import()) { + # Check for new data + if (check_new_items()) { + if (write_env_file()) { + # If there's new data and the user can edit have them approve the changes. + if ($user_can_edit) { + admin_approve(); + } + else { + $vars->{'tr_error'} .= "Please contact an admin who can import the above mentioned non-existing data.
"; + } + } + } + # If there's no new Categories, Elements, or Properties then store the environment. + else { + store_environment(); # Stores the new Environment values based on existing Categories, Elements, Properties, and Valid Expressions. + } + } +} +# If the admin form posted +elsif ($action eq 'admin' && $xml) { + if ($user_can_edit) { + my $file = "$upload_dir/$xml"; + trick_taint($file); + # If the admin clicked the 'Add Now' and not the 'Cancel' button + if ($submit eq 'Add Now') { + if (read_env_file($file)) { + import("admin"); # Creates Bugzilla::Testopia::Environment::Xml object and store's it to the database. + } + } + else { + $vars->{'tr_error'} .= "Import XML Environment Cancelled.
"; + delete_env_file($file); + } + } + else { + $vars->{'tr_error'} .= "Error reading from file. Please try again.
If the problem persists please contact the site administrator.
"; + } +} +display(); +#*********************************************** EXISTS HERE *************************************************# + + + + +#************************************* Sub Routine Deffinitions Below ****************************************# + + +=head2 Create XML document + +=head2 DESCRIPTION + +Creates the Environment XML Document and node lists. + +=cut + +sub import { + # If the user is an admin and has approved to add the values pass 1 to parse(). + my $admin = @_; + $environment = Bugzilla::Testopia::Environment::Xml->new($xml, $admin); + if ($environment->{'error'}) { + $vars->{'tr_error'} .= $environment->{'error'}; + return 0; + } + return 1; +} + + +=head2 Checking Exists + +=head2 DESCRIPTION + +Checking if the Environment, Elements, Categories, and Properties already exist or not. + +=cut + +sub check_new_items { + return $environment->check_new_items(); +} + + +=head2 Admin Approve + +=head2 Description + +Prepares the $vars being passed to the template to display the +Admin Approval Form first before importing an environment with +new data. + +=cut + +sub admin_approve { + $vars->{'action'} = "admin"; + $vars->{'xml'} = $xml; +} + + +=head2 Store the Environment + +=head2 Description + +Stores the Environment based on existing Categories, Elements, Properties, and selectable values. + +=cut + +sub store_environment { + $environment->store(); +} + + +=head2 Upload and read the XML file + +=head2 Description + +Uploads and reads the XML Environment file and prepares it into an array for parsing. + +=cut + +sub slurp_env_file { + my $untainted_filename; + my $post_max = $CGI::POST_MAX; + if (!$env_filename) { + $vars->{'tr_error'} .= "Please upload an Environment XML file.
"; + return 0; + } + # untaint $env_file + if ($env_filename =~ /^([-\@:\/\\\w.]+)$/) { + $untainted_filename = $1; + } + else { + $vars->{'tr_error'} .= "The filename must contain numbers and letters only. Please try again.
"; + return 0; + } + if ($untainted_filename =~ m/\.\./) { + $vars->{'tr_error'} .= "The filename cannot conain the sequence '..' Please try again.
"; + return 0; + } + my $num_bytes = $CGI::POST_MAX; + my ($totalbytes, $byteswritten, $buffer); + while ($byteswritten = read($env_fh, $buffer, $num_bytes)) { + $xml .= $buffer; + $totalbytes += $num_bytes; + } + if (!($byteswritten && $totalbytes)) { + $vars->{'error'} .= "Problem uploading file " . $env_filename . ".
" + } + if (!$xml) { + $vars->{'tr_error'} .= "File is empty. Please upload a valid Environment XML file.
"; + return 0; + } + if (length($xml) > $post_max) { + $vars->{'tr_error'} .= "File uploaded was too large. Please make sure the file size is no bigger than 500 KB.
"; + return 0; + } + return 1; +} + + +=head2 Write the Environment XML File to the Server + +=head Description + +Writes the environment XML File to the $upload_dir directory where it will be read in later by the admin to be approved. Once approved it's deleted. + +=cut + +sub write_env_file { + # If a writable $upload_dir exists, log error details there. + if (-w "$upload_dir") { + my $timestamp = Bugzilla::Testopia::Util::get_time_stamp(); + my $filename = $env_filename; + $filename =~ s/(.xml)//; + $filename .= "_" . $timestamp; + $filename =~ s/\:/./g; + $filename =~ s/[\: -]/_/g; + $filename .= ".xml"; + my $untainted_filename; + if (!$filename) { + $vars->{'tr_error'} .= "Please upload an Environment XML file.
"; + return 0; + } + # untaint $env_file + if ($filename =~ /^([-\@:\/\\\w.]+)$/) { + $untainted_filename = $1; + } + else { + $vars->{'tr_error'} .= "The filename must contain numbers and letters only. Please try again.
"; + return 0; + } + if ($untainted_filename =~ m/\.\./) { + $vars->{'tr_error'} .= "The filename cannot conain the sequence '..' Please try again.
"; + return 0; + } + open (my $fh, ">$upload_dir/$filename") || die "PROBLEM WRITING FILE.
"; + print $fh $xml; + close $fh; + $xml = $filename; + } + else { + $vars->{'tr_error'} .= "Unable to write temporary environment file. Please try again.
If the problem persists please contact the site administrator.
"; + return 0; + } + return 1; +} + + +=head2 Read the uploaded Environment XML file + +=head2 Description + +Uploads and reads the XML Environment file and prepares it into an array for parsing. + +=cut + +sub read_env_file { + my ($file) = @_; + $xml = ''; + open (my $fh, "<$file") || do { + $vars->{'tr_error'} .= "Unable to read temporary environment file. Please to try again.
If the problem persists please contact the site administrator.
"; + return 0; + }; + binmode($fh); + while (<$fh>) { + $xml .= $_; + } + close $fh; + if (!$xml) { + $vars->{'tr_error'} .= "File is empty. Please upload a valid Environment XML file.
"; + return 0; + } + if (length($xml) > $CGI::POST_MAX) { + $vars->{'tr_error'} .= "File uploaded was too large. Please make sure the file size is no bigger than 500 KB.
"; + return 0; + } + return delete_env_file($file); +} + + +=head2 delete_env_file + +=head2 Description + +Deletes the temporary environment file. + +=cut + +sub delete_env_file { + my ($file) = @_; + if (!unlink($file)) { + $vars->{'tr_error'} .= "Unable to delete temporary environment file.
If the problem persists please contact the site administrator.
"; + return 0; + } + return 1; +} + + +=head2 Display + +=head2 Description + +Displays the Import Environment template + +=cut + +sub display { + $vars->{'tr_message'} .= $message . $environment->{'message'}; + $template->process("testopia/environment/import.xml.tmpl", $vars) || print $template->error(); +} \ No newline at end of file diff --git a/mozilla/webtools/testopia/tr_install.pl b/mozilla/webtools/testopia/tr_install.pl index 33a1368ce7c..147201d61b7 100644 --- a/mozilla/webtools/testopia/tr_install.pl +++ b/mozilla/webtools/testopia/tr_install.pl @@ -20,13 +20,12 @@ # Contributor(s): Maciej Maczynski # Ed Fuentetaja -use DBI; use File::Copy; - -require Bugzilla; +use Bugzilla; use Bugzilla::Constants; +use Bugzilla::DB; -my $VERSION = "1.0-beta"; +my $VERSION = "1.1"; #check that we are on the Bugzilla's directory: @@ -41,10 +40,11 @@ if (! -e "localconfig") { "Can't continue.\n"); } +############################################################################## print "Now installing Testopia ...\n\n"; - do 'localconfig'; - +############################################################################## +# Patching. my $rollbackPatchOnFail = 0; my $patchSuccessFile = "testopia/tr_patch_successful"; my @files = (); @@ -57,128 +57,83 @@ if ($patchFile && $patchFile eq "-nopatch") { } else { doPatch($patchFile); } - +############################################################################## print "\nSetting file permissions ...\n"; SetupPermissions(); print "Done.\n\n"; - +############################################################################## print "Checking Testopia database ...\n"; -my ($dbh, $my_db_base, $my_db_host, $my_db_port, $my_db_name, $my_db_user, $my_db_sock) = DBConnect(); -print " Make : $my_db_base\n"; -print " Host : $my_db_host\n"; -print " Port : $my_db_port\n"; -print " Name : $my_db_name\n"; -print " User : $my_db_user\n"; -print " Socket: $my_db_sock\n"; -print "\n"; -UpdateDB($dbh); +my $dbh = Bugzilla::DB::connect_main(); +my $rebuild_bz_schema = 0; +# If bz_schema doesn't know about Testopia, it's either because +# Testopia has never been installed, or because Testopia was installed +# without using the methods in Bugzilla::DB. Either way, we need to +# bring bz_schema up-to-date. +if (!$dbh->bz_column_info('test_cases', 'case_id')) { + $rebuild_bz_schema = 1; + # Since we're going to rebuild bz_schema, we need to disconnect to + # forget what we know about the schema. + $dbh->disconnect; + $dbh = Bugzilla::DB::connect_main(); +} +UpdateDB($dbh, $rebuild_bz_schema); print "Done.\n\n"; - -print "Adding Testopia groups ...\n"; -# Identify admin group. +############################################################################## +print "Checking Testopia groups ...\n"; +# Create groups if needed and grant permissions to the admin group over them my $adminid = GetAdminGroup($dbh); -#Create groups if needed and grant permissions to the admin group over them my $groupid; -$groupid = tr_AddGroup($dbh, 'managetestplans', 'Can create, destroy, run and edit test plans.', $adminid); + +$groupid = tr_AddGroup($dbh, 'managetestplans', + 'Can create, destroy, run and edit test plans.', $adminid); tr_AssignAdminGrants($dbh, $groupid, $adminid); -$groupid = tr_AddGroup($dbh, 'edittestcases', 'Can add, delete and edit test cases.', $adminid); + +$groupid = tr_AddGroup($dbh, 'edittestcases', + 'Can add, delete and edit test cases.', $adminid); tr_AssignAdminGrants($dbh, $groupid, $adminid); -$groupid = tr_AddGroup($dbh, 'runtests', 'Can add, delete and edit test runs.', $adminid); + +$groupid = tr_AddGroup($dbh, 'runtests', + 'Can add, delete and edit test runs.', $adminid); tr_AssignAdminGrants($dbh, $groupid, $adminid); print "Done.\n\n"; - -DBDisconnect($dbh); - -#print "Copying Testopia templates ...\n"; -#CopyTemplates('testopia'); -#CopyTemplates('hook'); -#print "Done.\n\n"; - +############################################################################## print "Cleaning up Testopia cache ...\n"; unlink "data/plancache"; print "Done.\n\n"; -print "Running checksetup.pl ...\n"; -`perl checksetup.pl`; -print "Done.\n\n"; - -#now recover permissions over this script: -chmod(0750, "tr_install.pl"); - print "Testopia installed successfully!\n"; - -print "\n"; -print "Please follow now the 'Users' link at the bottom of your Bugzilla's home page to assign users to groups 'Edittestcases' and 'Managetestplans'.\n"; -print "\n"; - +############################################################################## 1; - - -############################################################################### - -sub CopyTemplates { - - my ($d) = @_; - - my $src = "testopia/template/en/default/$d"; - my $dst = "template/en/default/$d"; - - _copyRec($src, $dst); -} - -sub _copyRec { - - my ($src, $dst) = (@_); - - opendir DIRH, $src; - my @files = grep( $_ ne "." && $_ ne "..", readdir(DIRH)); - closedir DIRH; - - mkdir $dst; - foreach my $f (@files) { - if (-d "$src/$f") { - # A directory - _copyRec("$src/$f", "$dst/$f"); - } else { - copy("$src/$f", "$dst/$f"); - } - } -} +############################################################################## sub doPatch { - my ($fPatch) = @_; - + if (-e $patchSuccessFile) { - - #TODO: check the version run, in case we are upgrading - #since version 0.6.2 is the first version using this system - #this won't be needed until next version (0.6.3 ?) comes along - print "Patch already run successfully. Skipping.\n"; return 1; } - + print "Patching Bugzilla's files ...\n"; - + if ($fPatch) { # check that the patch file exists: if (!-e $fPatch) { DieWithStyle("Cannot find patch file: $fPatch\n". "Please, double-check the patch's file name and try again."); } - + print "\nUsing patch file: $fPatch\n\n"; - + } else { - # Guess the patch file to use: + # Guess the patch file to use: print "\nNo patch file especified, trying to determine the right one to use ...\n\n"; - + print "Bugzilla version ".$Bugzilla::Config::VERSION." detected.\n"; - + # This piece of code needs to be modified when a new patch file is added to the distribution: - + if ($Bugzilla::Config::VERSION =~ /^2\.18.*/) { # version 2.18.* detected $fPatch = "patch-2.18"; @@ -199,22 +154,21 @@ sub doPatch { DieWithStyle("No suitable patch detected for your Bugzilla. Patch cannot continue.\n". "Still, there is a chance that a manual patch might work on your installation. Please, check out Testopia's installation manual for details."); } - + print "Patch file chosen: $fPatch\n\n"; - + if (isWindows()) { $fPatch = "testopia\\".$fPatch; } else { $fPatch = "testopia/".$fPatch; } } - - + # Read the files involved in the patch and put them in @files readPatchFiles($fPatch, \@files); - + # Now, let's go ahead and run the patch command: - + print "Now patching ...\n\n"; my $patchPath = "patch"; @@ -223,22 +177,18 @@ sub doPatch { } my $output; - + # Check out if patch is installed: $output = `$patchPath -v` || DieWithStyle("Patch failed. Is 'patch' installed and in your path?"); - # Patch doesn't really run yet: --dry-run -# print "$patchPath -s --dry-run -bp 1 -i $fPatch 2>&1 \n"; -# exit; - $output = `$patchPath -s -l --dry-run -bp 2 -i $fPatch 2>&1`; # If the output is empty, everything was perfect (the -s argument) chomp $output; if ($output) { - # Nop... the patch didn't apply correctly: - + # Nope... the patch didn't apply correctly: + # Print out the output: print $output; @@ -260,52 +210,50 @@ sub doPatch { "> Please, check the documentation. Verify that this version of Bugzilla is supported.\n". "\n". "If you still have problems, please, check out Testopia's installation manual again and, if nothing helps, report the problem to Testopia sourceforge forum at http://sourceforge.net/projects/testopia."); - + } $rollbackPatchOnFail = 1; - + #Now let's really run the patch: $output = `$patchPath -s -l -z .orig -bp 2 -i $fPatch`; - + # If the output is empty, everything was perfect (the -s argument) chomp $output; if ($output) { - - # Nop... the patch didn't apply correctly: - + + # Nope... the patch didn't apply correctly: + # Print out the output: print $output; - + DieWithStyle ("\n*** Unexpected condition. Patch failed, but I was pretty sure it should have worked.\n". "Try to repeat the installation.\n"); } - + $rollbackPatchOnFail = 0; - + print "\nDone.\n"; - + restorePermissions(); - + open(MYOUTPUTFILE, ">$patchSuccessFile") or DieWithStyle("Couldn't write file: $patchSuccessFile"); print MYOUTPUTFILE $VERSION; close(MYOUTPUTFILE); - + print "\n"; print "Congratulations, patch worked flawless!\n"; print "\n"; print "A backup copy of the modified files has been saved with the .orig sufix.\n"; - + 1; } sub isWindows { - return ($^O eq "MSWin32" || $^O eq "cygwin"); } sub SetupPermissions { - if (isWindows()) { SetupPermissions_windows(); } else { @@ -314,7 +262,6 @@ sub SetupPermissions { } sub SetupPermissions_windows { - print "Running on Windows or Cygwin... skipping permissions...\n"; } @@ -323,56 +270,246 @@ sub SetupPermissions_unix { `chmod 640 tr_*.pl`; `chmod 750 tr_install.pl`; `chmod 750 testopia`; - `chmod 750 testopia/tr_*`; `chmod -R 750 testopia/doc`; `chmod -R 750 testopia/img`; `chmod -R 750 testopia/css`; `chmod -R 750 testopia/js`; - `chmod -R 750 testopia/dojo-ajax`; + `chmod -R 750 testopia/dojo`; `chmod -R 750 testopia/scripts`; `chmod 770 testopia/temp`; if (defined $::webservergroup && ($::webservergroup ne '')) { - print " Webserver group: $::webservergroup\n"; `chown :$::webservergroup tr_*`; `chown :$::webservergroup testopia`; - `chown :$::webservergroup testopia/tr_*`; `chown -R :$::webservergroup testopia/doc`; `chown -R :$::webservergroup testopia/img`; `chown -R :$::webservergroup testopia/css`; `chown -R :$::webservergroup testopia/js`; - `chown -R :$::webservergroup testopia/dojo-ajax`; + `chown -R :$::webservergroup testopia/dojo`; `chown -R :$::webservergroup testopia/scripts`; `chown :$::webservergroup testopia/temp`; } } sub UpdateDB { + my ($dbh, $rebuild_bz_schema) = (@_); - my ($dbh) = (@_); - - my $testRunnerDBPresent = 0; - - my $qh; - - $qh = $dbh->prepare("SHOW TABLES LIKE 'test_cases'"); - $qh->execute; - if ($qh->rows) { - #assume that the BTR 0.4.5 db was created successfuly - $testRunnerDBPresent = 1; - } - $qh->finish; - - if ($testRunnerDBPresent) { - print " Testopia database already present.\n"; - - - } else { - print " No Testopia database was detected. Need to create it ...\n"; - - #Run the create db script: - RunSQLScript('testopia/testopia.create.sql'); - print " Done.\n"; - } + if ($rebuild_bz_schema) { + $dbh->do("TRUNCATE bz_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_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_run_tags', 'userid', {TYPE => 'INT3', NOTNULL => 1}, 0); + $dbh->bz_add_column('test_runs', 'default_tester_id', {TYPE => 'INT3'}); + + $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', 'case_id', {TYPE => 'INT4', UNSIGNED => 1}); + $dbh->bz_alter_column('test_attachments', 'creation_ts', {TYPE => 'DATETIME', NOTNULL => 1}); + $dbh->bz_alter_column('test_attachments', 'plan_id', {TYPE => 'INT4', UNSIGNED => 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_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_run_status', 'case_run_status_id', {TYPE => 'TINYSERIAL', PRIMARYKEY => 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_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_status', 'case_status_id', {TYPE => 'TINYSERIAL', 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_cases', 'case_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $dbh->bz_alter_column('test_environment_map', 'environment_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $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_plan_group_map', 'plan_id', {TYPE => 'INT4', UNSIGNED => 1, NOTNULL => 1}); + $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_plans', 'plan_id', {TYPE => 'INTSERIAL', PRIMARYKEY => 1, NOTNULL => 1}); + $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_run_tags', '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_drop_index('test_case_run_status', 'AI_case_run_status_id'); + $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_cases', 'test_case_requirment_idx'); + $dbh->bz_drop_index('test_environments', 'environment_id'); + $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_add_index('test_case_tags', 'case_tags_user_idx', [qw(tag_id userid)]); + $dbh->bz_add_index('test_cases', 'test_case_requirement_idx', ['requirement']); + $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_runs_summary_idx', {FIELDS => ['summary'], TYPE => 'FULLTEXT'}); + + if ($dbh->bz_index_info('test_case_tags', 'case_tags_case_id_idx')->{TYPE} eq '') { + $dbh->bz_drop_index('test_case_tags', 'case_tags_case_id_idx'); + $dbh->bz_add_index('test_case_tags', 'case_tags_case_id_idx', {FIELDS => [qw(tag_id case_id)], TYPE => 'UNIQUE'}); + } + if ($dbh->bz_index_info('test_environments', 'environment_name_idx')->{TYPE} eq 'UNIQUE') { + $dbh->bz_drop_index('test_environments', 'environment_name_idx'); + $dbh->bz_add_index('test_environments', 'environment_name_idx', ['name']); + } + + populateMiscTables($dbh); + populateEnvTables($dbh); + migrateEnvData($dbh); +} + +sub populateMiscTables { + my ($dbh) = (@_); + + # Insert initial values in static tables. Going out on a limb and + # assuming that if one table is empty, they all are. + return unless $dbh->selectrow_array("SELECT COUNT(*) FROM test_case_run_status") == 0; + + 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"; + open FH, "< testopia/testopia.insert.sql" or die; + $dbh->do($_) while (); + close FH; +} + +sub populateEnvTables { + my ($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_lock_tables( + 'test_environment_category WRITE', + 'test_environment_element WRITE', + 'op_sys READ', + 'rep_platform READ'); + + print "Populating test_environment_category table ...\n"; + $dbh->do("INSERT INTO test_environment_category " . + "(env_category_id, product_id, name) " . + "VALUES (1, 0, 'Operating System'), (2, 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_unlock_tables(); +} + +sub migrateEnvData { + my ($dbh) = (@_); + 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_lock_tables( + 'test_environment_map WRITE', + 'test_environments READ'); + 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_unlock_tables(); + + 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'); } # Copied from checksetup.pl @@ -383,9 +520,8 @@ sub UpdateDB { # sub tr_AddGroup { - my ($dbh, $name, $desc) = @_; - + my ($id) = GroupDoesExist($dbh, $name); return $id if $id; @@ -403,18 +539,16 @@ sub tr_AddGroup { } sub GetAdminGroup { - my ($dbh) = @_; - + my $sth = $dbh->prepare("SELECT id FROM groups WHERE name = 'admin'"); $sth->execute(); my ($adminid) = $sth->fetchrow_array(); - + return $adminid; } sub tr_AssignAdminGrants { - my ($dbh, $id, $adminid) = @_; #clean up first: @@ -422,7 +556,7 @@ sub tr_AssignAdminGrants { "WHERE member_id=$adminid AND grantor_id=$id"); #Assign privileges to the admin group over this group: - + my $blessColumn; my $group_membership; my $group_bless; @@ -436,15 +570,11 @@ sub tr_AssignAdminGrants { $group_membership = GROUP_MEMBERSHIP; $group_bless = GROUP_BLESS; } - + # Admins can bless. $dbh->do("INSERT INTO group_group_map ". "(member_id, grantor_id, $blessColumn) ". "VALUES ($adminid, $id," . $group_bless . ")"); - # Admins can see. - #$dbh->do("INSERT INTO group_group_map - # (member_id, grantor_id, grant_type) - # VALUES ($adminid, $last," . GROUP_VISIBLE . ")"); # Admins are initially members. $dbh->do("INSERT INTO group_group_map ". "(member_id, grantor_id, $blessColumn) ". @@ -452,7 +582,6 @@ sub tr_AssignAdminGrants { } sub GroupDoesExist { - my ($dbh, $name) = @_; my $sth = $dbh->prepare("SELECT id FROM groups WHERE name='$name'"); $sth->execute; @@ -463,93 +592,21 @@ sub GroupDoesExist { return $id; } -sub GetDBConnectionInfo { - - my $my_db_base = 'mysql'; - # code taken from checksetup.pl: - my $my_db_host = ${*{$main::{'db_host'}}{SCALAR}}; - my $my_db_port = ${*{$main::{'db_port'}}{SCALAR}}; - my $my_db_name = ${*{$main::{'db_name'}}{SCALAR}}; - my $my_db_user = ${*{$main::{'db_user'}}{SCALAR}}; - my $my_db_pass = ${*{$main::{'db_pass'}}{SCALAR}}; - my $my_db_sock = ''; - if (defined $main::{'db_sock'}) { - $my_db_sock = ${*{$main::{'db_sock'}}{SCALAR}}; - } - - ($my_db_base, $my_db_host, $my_db_port, $my_db_name, - $my_db_user, $my_db_pass, $my_db_sock); -} - -sub DBConnect { - - my ($my_db_base, $my_db_host, $my_db_port, $my_db_name, - $my_db_user, $my_db_pass, $my_db_sock) = GetDBConnectionInfo(); - - my $dsn = "DBI:$my_db_base:$my_db_name:host=$my_db_host:port=$my_db_port"; - if ($my_db_sock ne '') { - $dsn .= ";mysql_socket=$my_db_sock"; - } - my $dbh = DBI->connect($dsn, $my_db_user, $my_db_pass) - || DieWithStyle("Can't connect to the $my_db_base database. Is the database " . - "installed and up and running? Do you have the correct username " . - "and password selected in\nlocalconfig?"); - ($dbh, $my_db_base, $my_db_host, $my_db_port, $my_db_name, - $my_db_user, $my_db_sock); -} - -sub DBDisconnect { - - my ($dbh) = (@_); - $dbh->disconnect if $dbh; -} - -sub RunSQLScript { - - my ($script) = (@_); - - my ($my_db_base, $my_db_host, $my_db_port, $my_db_name, - $my_db_user, $my_db_pass, $my_db_sock) = GetDBConnectionInfo(); - - my $cmdline = "mysql -h$my_db_host -P$my_db_port -u $my_db_user $my_db_name -p$my_db_pass"; - if ($my_db_sock ne '') { - $cmdline.= " -S$my_db_sock"; - } - $cmdline.= ' < '.$script; - - my $output; - - #Check out that mysql is installed and can be reached: - $output = `mysql -V` || DieWithStyle("MySql command failed. Is 'mysql' installed and in your path?"); - - $output = `$cmdline 2>&1`; - print $output; - # If the output is empty, everything was perfect - chomp $output; - if ($output) { - DieWithStyle("Could not create Testopia's tables. See previous errors."); - } else { - print " Testopia's tables created successfully.\n"; - } -} - sub DieWithStyle { - my($message) = @_; - + print "\n$message\n"; if ($rollbackPatchOnFail) { rollbackPatch(); } - + die "\nInstall failed. See details above.\n"; } sub readPatchFiles { - my($path, $f) = (@_); - + open(MYINPUTFILE, "<$path") or DieWithStyle("Couldn't find file: $path"); while () { my $line = $_; @@ -561,9 +618,8 @@ sub readPatchFiles { } sub rollbackPatch { + # Recovered files from the .orig copies made by patch: - # Recoved files from the .orig copies made by patch: - print "\n"; print "Restoring original files ...\n"; foreach my $file (@files) { @@ -577,9 +633,8 @@ sub rollbackPatch { } sub restorePermissions { - # Restore permissions from the .orig files: - + print "\n"; print "Recovering original permissions ...\n"; foreach my $file (@files) { @@ -593,13 +648,11 @@ sub restorePermissions { # http://www.linuxgazette.com/issue87/misc/tips/cpmod.pl.txt sub perm_cp { - my $perms = perm_get($_[0]); perm_set($_[1], $perms); } sub perm_get { - my($filename) = @_; my @stats = (stat $filename)[2,4,5] or @@ -609,14 +662,11 @@ sub perm_get { } sub perm_set { - my($filename, $perms) = @_; - chown($perms->[1], $perms->[2], $filename) or + chown($perms->[1], $perms->[2], $filename) or DieWithStyle("Cannot chown $filename ($!)"); chmod($perms->[0] & 07777, $filename) or DieWithStyle("Cannot chmod $filename ($!)"); } - -###################################### diff --git a/mozilla/webtools/testopia/tr_list_caseruns.cgi b/mozilla/webtools/testopia/tr_list_caseruns.cgi index 26d2a5b0afb..12200d4177c 100755 --- a/mozilla/webtools/testopia/tr_list_caseruns.cgi +++ b/mozilla/webtools/testopia/tr_list_caseruns.cgi @@ -70,8 +70,13 @@ if ($action eq 'Commit'){ my $status = $cgi->param('status') == -1 ? $caserun->status_id : $cgi->param('status'); my $build = $cgi->param('caserun_build') == -1 ? $caserun->build->id : $cgi->param('caserun_build'); my $assignee = $cgi->param('assignee') eq '--Do Not Change--' ? $caserun->assignee->id : DBNameToIdAndCheck(trim($cgi->param('assignee'))); - my $notes = $cgi->param('notes'); + my $notes = $cgi->param('notes'); + my $env = $cgi->param('caserun_env') eq '--Do Not Change--' ? $caserun->environment->id : $cgi->param('caserun_env'); + validate_test_id($build, 'build'); + validate_test_id($env, 'environment'); + + detaint_natural($env); detaint_natural($build); detaint_natural($status); trick_taint($notes); @@ -79,6 +84,7 @@ if ($action eq 'Commit'){ foreach my $bug (@buglist){ $caserun->attach_bug($bug); } + my $testedby; my $close_date; if ($caserun->is_closed_status($status)){ @@ -91,11 +97,12 @@ if ($action eq 'Commit'){ 'close_date' => $close_date, 'case_run_status_id' => $status, 'build_id' => $build, + 'environment_id' => $env, ); $caserun->update(\%newfields); - $caserun->set_note($notes, 1); + $caserun->append_note($notes); } - $vars->{'title'} = "Update Susccessful"; + $vars->{'title'} = "Update Successful"; $vars->{'tr_message'} = scalar @caseruns . ' Test Case-Runs Updated'; if ($cgi->param('run_id')){ @@ -110,6 +117,7 @@ if ($action eq 'Commit'){ $vars->{'run'} = $run; $vars->{'table'} = $table; $vars->{'action'} = 'Commit'; + $vars->{'form_action'} = "tr_show_run.cgi"; $template->process("testopia/run/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -124,7 +132,6 @@ if ($action eq 'Commit'){ exit; } # Take the search from the URL params and convert it to SQL -$cgi->delete('build'); $cgi->param('current_tab', 'case_run'); my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_list_caseruns.cgi', $cgi, undef, $search->query); @@ -148,6 +155,8 @@ if ($table->list_count > 0){ if ($cgi->param('run_id')){ $vars->{'run'} = Bugzilla::Testopia::TestRun->new($cgi->param('run_id')); } +my $case = Bugzilla::Testopia::TestCase->new({'case_id' => 0}); +$vars->{'component_list'} = $case->get_available_components(); $vars->{'dotweak'} = UserInGroup('edittestcases'); $vars->{'table'} = $table; $vars->{'action'} = 'tr_list_caserun.cgi'; diff --git a/mozilla/webtools/testopia/tr_list_cases.cgi b/mozilla/webtools/testopia/tr_list_cases.cgi index 617860f3e23..94a86a3d1a8 100755 --- a/mozilla/webtools/testopia/tr_list_cases.cgi +++ b/mozilla/webtools/testopia/tr_list_cases.cgi @@ -97,9 +97,10 @@ if ($action eq 'Commit'){ 'arguments' => $arguments, 'default_tester_id' => $tester || $case->default_tester->id, ); + $case->update(\%newvalues); if ($cgi->param('addtags')){ - foreach my $name (split(/,/, $cgi->param('addtags'))){ + foreach my $name (split(/[\s,]+/, $cgi->param('addtags'))){ trick_taint($name); my $tag = Bugzilla::Testopia::TestTag->new({'tag_name' => $name}); my $tag_id = $tag->store; @@ -107,17 +108,44 @@ if ($action eq 'Commit'){ } } my @runs; - foreach my $runid (split(",", $cgi->param('addruns'))){ + foreach my $runid (split(/[\s,]+/, $cgi->param('addruns'))){ validate_test_id($runid, 'run'); push @runs, Bugzilla::Testopia::TestRun->new($runid); } foreach my $run (@runs){ $run->add_case_run($case->id); } + + foreach my $planid (split(",", $cgi->param('linkplans'))){ + validate_test_id($planid, 'plan'); + $case->link_plan($planid); + } } + my @runlist = split(/[\s,]+/, $cgi->param('addruns')); + if (scalar @runlist == 1){ + my $run_id = $cgi->param('addruns'); + validate_test_id($run_id, 'run'); + $cgi->delete_all; + $cgi->param('run_id', $run_id); + $cgi->param('current_tab', 'case_run'); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_show_run.cgi', $cgi, undef, $search->query); + + my @case_list; + foreach my $caserun (@{$table->list}){ + push @case_list, $caserun->case_id; + } + $vars->{'run'} = Bugzilla::Testopia::TestRun->new($run_id); + $vars->{'table'} = $table; + $vars->{'case_list'} = join(",", @case_list); + $vars->{'action'} = 'Commit'; + $template->process("testopia/run/show.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } my $case = Bugzilla::Testopia::TestCase->new({ 'case_id' => 0 }); $vars->{'case'} = $case; - $vars->{'title'} = "Update Susccessful"; + $vars->{'title'} = "Update Successful"; $vars->{'tr_message'} = scalar @cases . " Test Cases Updated"; $vars->{'current_tab'} = 'case'; $template->process("testopia/search/advanced.html.tmpl", $vars) @@ -130,23 +158,56 @@ $cgi->param('current_tab', 'case'); my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('case', 'tr_list_cases.cgi', $cgi, undef, $search->query); +# Check that all of the test cases returned only belong to one product. +if ($table->list_count > 0){ + my %case_prods; + my $prod_id; + foreach my $case (@{$table->list}){ + $case_prods{$case->id} = $case->get_product_ids; + $prod_id = @{$case_prods{$case->id}}[0]; + if (scalar(@{$case_prods{$case->id}} > 1)){ + $vars->{'multiprod'} = 1 ; + last; + } + } + # Check that all of them are the same product + if (!$vars->{'multiprod'}){ + foreach my $c (keys %case_prods){ + if ($case_prods{$c}->[0] != $prod_id){ + $vars->{'multiprod'} = 1; + last; + } + } + } + if (!$vars->{'multiprod'}) { + my $category_list = $table->list->[0]->get_category_list; + unshift @{$category_list}, {'id' => -1, 'name' => "--Do Not Change--"}; + $vars->{'category_list'} = $category_list; + } +} # create an empty case to use for getting status and priority lists my $c = Bugzilla::Testopia::TestCase->new({'case_id' => 0 }); my $status_list = $c->get_status_list; my $priority_list = $c->get_priority_list; -my $category_list = $c->get_distinct_categories; # add the "do not change" option to each list # we use unshift so they show at the top of the list unshift @{$status_list}, {'id' => -1, 'name' => "--Do Not Change--"}; -unshift @{$category_list}, {'id' => -1, 'name' => "--Do Not Change--"}; unshift @{$priority_list}, {'id' => -1, 'name' => "--Do Not Change--"}; -$vars->{'fullwidth'} = 1; #novellonly -$vars->{'status_list'} = $status_list; -$vars->{'category_list'} = $category_list; -$vars->{'priority_list'} = $priority_list; +my $addrun = $cgi->param('addrun'); +if ($addrun){ + validate_test_id($addrun, 'run'); + my $run = Bugzilla::Testopia::TestRun->new($addrun); + $vars->{'addruns'} = $addrun; + $vars->{'plan'} = $run->plan; +} + $vars->{'addrun'} = $cgi->param('addrun'); +$vars->{'fullwidth'} = 1; #novellonly +$vars->{'case'} = $c; +$vars->{'status_list'} = $status_list; +$vars->{'priority_list'} = $priority_list; $vars->{'dotweak'} = UserInGroup('edittestcases'); $vars->{'table'} = $table; diff --git a/mozilla/webtools/testopia/tr_list_environments.cgi b/mozilla/webtools/testopia/tr_list_environments.cgi new file mode 100755 index 00000000000..2b21af70119 --- /dev/null +++ b/mozilla/webtools/testopia/tr_list_environments.cgi @@ -0,0 +1,56 @@ +#!/usr/bin/perl -wT +# -*- 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 + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; + +Bugzilla->login(); + +my $cgi = Bugzilla->cgi; + +use vars qw($vars); +my $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; + +$cgi->param('current_tab', 'environment'); +my $search = Bugzilla::Testopia::Search->new($cgi); +my $table = Bugzilla::Testopia::Table->new('environment', 'tr_list_environments.cgi', $cgi, undef, $search->query); + +$vars->{'table'} = $table; + +$template->process("testopia/environment/list.html.tmpl", $vars) + || print $template->error(); diff --git a/mozilla/webtools/testopia/tr_list_plans.cgi b/mozilla/webtools/testopia/tr_list_plans.cgi index 731eef1964f..56c666e5861 100755 --- a/mozilla/webtools/testopia/tr_list_plans.cgi +++ b/mozilla/webtools/testopia/tr_list_plans.cgi @@ -82,7 +82,7 @@ if ($action eq 'Commit'){ } my $plan = Bugzilla::Testopia::TestPlan->new({ 'plan_id' => 0 }); $vars->{'plan'} = $plan; - $vars->{'title'} = "Update Susccessful"; + $vars->{'title'} = "Update Successful"; $vars->{'tr_message'} = scalar @plans . " Test Plan(s) Updated"; $vars->{'current_tab'} = 'plan'; $template->process("testopia/search/advanced.html.tmpl", $vars) diff --git a/mozilla/webtools/testopia/tr_list_runs.cgi b/mozilla/webtools/testopia/tr_list_runs.cgi index e1028c0fc9d..daf050002e5 100755 --- a/mozilla/webtools/testopia/tr_list_runs.cgi +++ b/mozilla/webtools/testopia/tr_list_runs.cgi @@ -53,6 +53,7 @@ if ($action eq 'Commit'){ push @runs, Bugzilla::Testopia::TestRun->new($1) if $r =~ $reg; } ThrowUserError('testopia-none-selected', {'object' => 'run'}) if (scalar @runs < 1); + ThrowUserError('testopia-missing-required-field', {'field' => 'environment'}) if ($cgi->param('environment') eq ''); foreach my $run (@runs){ ThowUserError('insufficient-perms') unless $run->canedit; my $manager = DBNameToIdAndCheck(trim($cgi->param('manager'))) if $cgi->param('manager'); @@ -65,7 +66,8 @@ if ($action eq 'Commit'){ $status = get_time_stamp(); } } - my $enviro = $cgi->param('environment') == -1 ? $run->environment->id : $cgi->param('environment'); + + my $enviro = $cgi->param('environment') eq '--Do Not Change--' ? $run->environment->id : $cgi->param('environment'); my $build = $cgi->param('build') == -1 ? $run->build->id : $cgi->param('build'); detaint_natural($status); @@ -89,11 +91,10 @@ if ($action eq 'Commit'){ } my $run = Bugzilla::Testopia::TestRun->new({ 'run_id' => 0 }); $vars->{'run'} = $run; - $vars->{'title'} = "Update Susccessful"; + $vars->{'title'} = "Update Successful"; $vars->{'tr_message'} = scalar @runs . " Test Runs Updated"; $vars->{'current_tab'} = 'run'; - $vars->{'env_list'} = get_environments(); - $vars->{'build_list'} = get_all_builds(); + $vars->{'build_list'} = $run->get_distinct_builds(); $template->process("testopia/search/advanced.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -121,13 +122,10 @@ else { } my $r = Bugzilla::Testopia::TestRun->new({'run_id' => 0 }); - my $env_list = $r->environments; my $status_list = $r->get_status_list; - unshift @{$env_list}, {'id' => -1, 'name' => "--Do Not Change--"}; unshift @{$status_list}, {'id' => -1, 'name' => "--Do Not Change--"}; - $vars->{'env_list'} = $env_list; $vars->{'status_list'} = $status_list; $vars->{'fullwidth'} = 1; #novellonly diff --git a/mozilla/webtools/testopia/tr_new_case.cgi b/mozilla/webtools/testopia/tr_new_case.cgi index a6f9acc73e1..048ca456b67 100755 --- a/mozilla/webtools/testopia/tr_new_case.cgi +++ b/mozilla/webtools/testopia/tr_new_case.cgi @@ -29,6 +29,7 @@ use Bugzilla::Util; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::TestCase; +use JSON; require "globals.pl"; @@ -48,7 +49,7 @@ push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; my $action = $cgi->param('action') || ''; my @plan_id = $cgi->param('plan_id'); -unless (@plan_id){ +unless ($plan_id[0]){ $vars->{'form_action'} = 'tr_new_case.cgi'; $template->process("testopia/plan/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -83,8 +84,10 @@ if ($action eq 'Add'){ my $arguments = $cgi->param("arguments")|| ''; my $summary = $cgi->param("summary")|| ''; my $requirement = $cgi->param("requirement")|| ''; - my $tcaction = $cgi->param("tcaction"); - my $tceffect = $cgi->param("tceffect"); + my $tcaction = $cgi->param("tcaction") || ''; + my $tceffect = $cgi->param("tceffect") || ''; + my $tcsetup = $cgi->param("tcsetup") || ''; + my $tcbreakdown = $cgi->param("tcbreakdown") || ''; my $tcdependson = $cgi->param("tcdependson")|| ''; my $tcblocks = $cgi->param("tcblocks")|| ''; my $tester = $cgi->param("tester") || ''; @@ -94,8 +97,6 @@ if ($action eq 'Add'){ } ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}) if $summary eq ''; - ThrowUserError('testopia-missing-required-field', {'field' => 'Case Action'}) if $tcaction eq ''; - ThrowUserError('testopia-missing-required-field', {'field' => 'Case Expected Results'}) if $tceffect eq ''; detaint_natural($status); detaint_natural($category); @@ -111,6 +112,8 @@ if ($action eq 'Add'){ trick_taint($tcaction); trick_taint($tceffect); trick_taint($tcdependson); + trick_taint($tcsetup); + trick_taint($tcbreakdown); trick_taint($tcblocks); validate_selection($category, 'category_id', 'test_case_categories'); @@ -137,11 +140,27 @@ if ($action eq 'Add'){ 'author_id' => Bugzilla->user->id, 'action' => $tcaction, 'effect' => $tceffect, + 'setup' => $tcsetup, + 'breakdown' => $tcbreakdown, 'dependson' => $tcdependson, 'blocks' => $tcblocks, 'plans' => \@plans, }); + # Check for valid ids or aliases in dependecy fields + + foreach my $field ("dependson", "blocks") { + if ($case->{$field}) { + my @validvalues; + foreach my $id (split(/[\s,]+/, $case->{$field})) { + next unless $id; + Bugzilla::Testopia::Util::validate_test_id($id, 'case'); + push(@validvalues, $id); + } + $case->{$field} = join(",", @validvalues); + } + } + ThrowUserError('testiopia-alias-exists', {'alias' => $alias}) if $case->check_alias($alias); ThrowUserError('testiopia-invalid-data', @@ -149,7 +168,7 @@ if ($action eq 'Add'){ if ($isautomated !~ /^[01]$/); my $case_id = $case->store; - my $case = Bugzilla::Testopia::TestCase->new($case_id); + $case = Bugzilla::Testopia::TestCase->new($case_id); $case->add_component($_) foreach (@components); if ($cgi->param('addtags')){ @@ -161,7 +180,7 @@ if ($action eq 'Add'){ } } - $vars->{'action'} = "update"; + $vars->{'action'} = "Commit"; $vars->{'form_action'} = "tr_show_case.cgi"; $vars->{'case'} = $case; $vars->{'tr_message'} = "Case $case_id Created. @@ -171,17 +190,24 @@ if ($action eq 'Add'){ ThrowTemplateError($template->error()); } + #################### ### Display Form ### #################### else { - $vars->{'action'} = "Add"; - $vars->{'form_action'} = "tr_new_case.cgi"; - $vars->{'case'} = Bugzilla::Testopia::TestCase->new( + my $case = Bugzilla::Testopia::TestCase->new( {'case_id' => 0, 'plans' => \@plans, 'category' => {'name' => 'Default'}, }); + my @comps; + foreach my $comp (@{$case->get_selectable_components(1)}){ + push @comps, $comp->default_qa_contact->login; + } + $vars->{'case'} = $case; + $vars->{'components'} = objToJson(\@comps); + $vars->{'action'} = "Add"; + $vars->{'form_action'} = "tr_new_case.cgi"; $template->process("testopia/case/add.html.tmpl", $vars) || ThrowTemplateError($template->error()); } diff --git a/mozilla/webtools/testopia/tr_new_environment.cgi b/mozilla/webtools/testopia/tr_new_environment.cgi new file mode 100755 index 00000000000..9e3c4bf751c --- /dev/null +++ b/mozilla/webtools/testopia/tr_new_environment.cgi @@ -0,0 +1,92 @@ +#!/usr/bin/perl -wT +# -*- 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) 2001 +# Greg Hendricks. All Rights Reserved. +# +# Contributor(s): Greg Hendricks +# Michael Hight +# Garrett Braden + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; + +Bugzilla->login(LOGIN_REQUIRED); + +my $cgi = Bugzilla->cgi; + +use vars qw($vars); +my $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; + +if ($action eq 'Add'){ + my $name = $cgi->param('name'); + my $product = $cgi->param('product'); + + trick_taint($name); + detaint_natural($product); + + my $env = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); + + my $success = $env->store_environment_name($name, $product); + unless ($success){ + $vars->{'tr_error'} = "The environment name '$name' is already taken."; + $vars->{'environment'} = $env; + $template->process("testopia/environment/add.html.tmpl", $vars) + || print $template->error(); + exit; + } + $vars->{'tr_message'} = "The environment '$name' was successfully added."; + + $env = Bugzilla::Testopia::Environment->new($success); + my $category = Bugzilla::Testopia::Environment::Category->new({'id' => 0}); + if (Param('useclassification')){ + $vars->{'allhaschild'} = $category->get_all_child_count; + $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; + $vars->{'type'} = 'classification'; + } + else { + $vars->{'toplevel'} = $category->get_env_product_list; + $vars->{'type'} = 'product'; + } + $vars->{'user'} = Bugzilla->user; + $vars->{'action'} = 'do_edit'; + $vars->{'environment'} = $env; + $template->process("testopia/environment/show.html.tmpl", $vars) + || print $template->error(); + +} + +else { + $vars->{'environment'} = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); + $vars->{'products'} = Bugzilla->user->get_selectable_products; + + $template->process("testopia/environment/add.html.tmpl", $vars) + || print $template->error(); +} \ No newline at end of file diff --git a/mozilla/webtools/testopia/tr_new_plan.cgi b/mozilla/webtools/testopia/tr_new_plan.cgi index 6fd5521eaab..4be76bb4a52 100755 --- a/mozilla/webtools/testopia/tr_new_plan.cgi +++ b/mozilla/webtools/testopia/tr_new_plan.cgi @@ -76,7 +76,6 @@ if ($action eq 'Add'){ 'type_id' => $type, 'text' => $text, 'author_id' => Bugzilla->user->id, - 'editor_id' => Bugzilla->user->id, }); my $plan_id = $plan->store; $vars->{'case_table'} = undef; diff --git a/mozilla/webtools/testopia/tr_new_run.cgi b/mozilla/webtools/testopia/tr_new_run.cgi index b2b791b6c21..81f3eb7a806 100755 --- a/mozilla/webtools/testopia/tr_new_run.cgi +++ b/mozilla/webtools/testopia/tr_new_run.cgi @@ -72,7 +72,7 @@ if ($action eq 'Add'){ my $summary = $cgi->param('summary'); my $notes = $cgi->param('notes'); my $env = $cgi->param('environment'); - + ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}) if $summary eq ''; detaint_natural($status); @@ -85,6 +85,21 @@ if ($action eq 'Add'){ trick_taint($notes); trick_taint($prodver); + ThrowUserError('number_not_numeric', {'field' => 'environment', 'num' => $cgi->param('environment')}) if $env eq ''; + + if ($cgi->param('new_build')){ + my $new_build = $cgi->param('new_build'); + trick_taint($new_build); + my $b = Bugzilla::Testopia::Build->new({ + 'name' => $new_build, + 'milestone' => '---', + 'product_id' => $plan->product_id, + 'description' => '' + }); + my $bid = $b->check_name($new_build); + $bid ? $build = $bid : $build = $b->store; + } + my $reg = qr/c_([\d]+)/; my @c; foreach my $p ($cgi->param()){ @@ -118,6 +133,7 @@ if ($action eq 'Add'){ $vars->{'run'} = $run; $vars->{'table'} = $table; $vars->{'action'} = 'Commit'; + $vars->{'form_action'} = 'tr_show_run.cgi'; $template->process("testopia/run/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -131,6 +147,7 @@ else { $cgi->param('viewall', 1); my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('case', 'tr_new_run.cgi', $cgi, undef, $search->query); + $vars->{'case'} = Bugzilla::Testopia::TestCase->new({'case_id' => 0}); $vars->{'table'} = $table; $vars->{'dotweak'} = 1; $vars->{'fullwidth'} = 1; #novellonly diff --git a/mozilla/webtools/testopia/tr_query.cgi b/mozilla/webtools/testopia/tr_query.cgi index 76201d6477a..74a03053462 100755 --- a/mozilla/webtools/testopia/tr_query.cgi +++ b/mozilla/webtools/testopia/tr_query.cgi @@ -30,6 +30,9 @@ use Bugzilla::Error; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::TestPlan; use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Property; use vars qw($vars $template); require "globals.pl"; @@ -40,9 +43,10 @@ my $template = Bugzilla->template; push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; Bugzilla->login(); +#Bugzilla->batch(1); print $cgi->header; -my $action = $cgi->param('action'); +my $action = $cgi->param('action') || ''; if ($action eq 'getversions'){ my @prod_ids = split(",", $cgi->param('prod_ids')); @@ -81,6 +85,102 @@ if ($action eq 'getversions'){ print $ret; } + +elsif ($action eq 'get_products'){ + my @prod; + if (Param('useclassification')){ + my @classes = $cgi->param('class_ids'); + foreach my $id (@classes){ + my $class = Bugzilla::Classification->new($id); + push @prod, @{$class->user_visible_products}; + } + } + else { + @prod = Bugzilla->user->get_selectable_products; + } + + my $ret; + foreach my $e (@prod){ + $ret .= $e->{'id'}.'||'.$e->{'name'}.'|||'; + } + chop($ret); + print $ret; +} + +elsif ($action eq 'get_categories'){ + my @prod_ids = $cgi->param('prod_id'); + my $ret; + + foreach my $prod_id (@prod_ids){ + detaint_natural($prod_id); + my $cat = Bugzilla::Testopia::Environment::Category->new({}); + my $cats_ref = $cat->get_element_categories_by_product($prod_id); + + foreach my $e (@{$cats_ref}){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + } + chop($ret); + print $ret; +} +elsif ($action eq 'get_elements'){ + my @cat_ids = $cgi->param('cat_id'); + my $ret; + my @elmnts; + + foreach my $cat_id (@cat_ids){ + detaint_natural($cat_id); + + my $cat = Bugzilla::Testopia::Environment::Category->new($cat_id); + my $elmnts_ref = $cat->get_elements_by_category($cat->{'name'}); + + foreach my $e (@{$elmnts_ref}){ + push @elmnts, Bugzilla::Testopia::Environment::Element->new($e->{'element_id'}); + } + } + + foreach my $e (@elmnts){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + chop($ret); + print $ret; +} + +elsif ($action eq 'get_properties'){ + my @elmnt_ids = $cgi->param('elmnt_id'); + my $ret; + + foreach my $elmnt_id (@elmnt_ids){ + detaint_natural($elmnt_id); + + my $elmnt = Bugzilla::Testopia::Environment::Element->new($elmnt_id); + my $props = $elmnt->get_properties(); + + foreach my $e (@{$props}){ + $ret .= $e->id.'||'.$e->name.'|||'; + } + } + chop($ret); + print $ret; +} +elsif ($action eq 'get_valid_exp'){ + my @prop_ids = $cgi->param('prop_id'); + my $ret; + + foreach my $prop_id (@prop_ids){ + detaint_natural($prop_id); + + my $prop = Bugzilla::Testopia::Environment::Property->new($prop_id); + my $exp = $prop->validexp; + + my @exps = split /\|/, $exp; + foreach my $exp (@exps){ + $ret .=$exp.'|||'; + } + } + chop($ret); + print $ret; +} else{ my $plan = Bugzilla::Testopia::TestPlan->new({ 'plan_id' => 0 }); my @allversions; @@ -107,9 +207,14 @@ else{ elsif ($tab eq 'run'){ my $run = Bugzilla::Testopia::TestRun->new({ 'run_id' => 0 }); $vars->{'run'} = $run; - # TODO Narrow build list by product $vars->{'title'} = "Search For Test Runs"; } + elsif ($tab eq 'environment'){ + $vars->{'classifications'} = Bugzilla->user->get_selectable_classifications; + $vars->{'products'} = Bugzilla->user->get_selectable_products; + $vars->{'env'} = Bugzilla::Testopia::Environment->new({'environment_id' => 0 }); + $vars->{'title'} = "Search For Test Run Environments"; + } else { # show the case form $tab = 'case'; my $case = Bugzilla::Testopia::TestCase->new({ 'case_id' => 0 }); diff --git a/mozilla/webtools/testopia/tr_show_case.cgi b/mozilla/webtools/testopia/tr_show_case.cgi index 51182f1ef93..ff396a3a7f3 100755 --- a/mozilla/webtools/testopia/tr_show_case.cgi +++ b/mozilla/webtools/testopia/tr_show_case.cgi @@ -23,12 +23,14 @@ use strict; use lib "."; use Bugzilla; -use Bugzilla::Constants; -use Bugzilla::Error; +use Bugzilla::Bug; use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::TestCase; use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::TestTag; use Bugzilla::Testopia::Attachment; use Bugzilla::Testopia::Search; use Bugzilla::Testopia::Table; @@ -167,6 +169,7 @@ elsif ($action eq 'Attach'){ do_update($case); display($case); } + elsif ($action eq 'Commit'){ Bugzilla->login(LOGIN_REQUIRED); my $case = Bugzilla::Testopia::TestCase->new($case_id); @@ -188,6 +191,35 @@ elsif ($action eq 'History'){ } +elsif ($action eq 'unlink'){ + Bugzilla->login(LOGIN_REQUIRED); + my $plan_id = $cgi->param('plan_id'); + validate_test_id($plan_id, 'plan'); + my $case = Bugzilla::Testopia::TestCase->new($case_id); + if ($case->unlink_plan($plan_id)){ + $vars->{'tr_message'} = "Test plan successfully unlinked"; + } + else { + $vars->{'tr_error'} = "Test plan could not be unlinked. It is used in test runs."; + } + display($case); +} + +elsif ($action eq 'detach_bug'){ + Bugzilla->login(LOGIN_REQUIRED); + my $case = Bugzilla::Testopia::TestCase->new($case_id); + ThrowUserError("testopia-read-only", {'object' => 'case'}) unless $case->canedit; + my @buglist; + foreach my $bug (split(/[\s,]+/, $cgi->param('bug_id'))){ + ValidateBugID($bug); + push @buglist, $bug; + } + foreach my $bug (@buglist){ + $case->detach_bug($bug); + } + display(Bugzilla::Testopia::TestCase->new($case_id)); +} + #################### ### Ajax Actions ### #################### @@ -227,14 +259,14 @@ sub get_components_xml { foreach my $c (@{$case->components}){ $ret .= ""; $ret .= "". $c->{'id'} .""; - $ret .= "". $c->{'name'} .""; + $ret .= "". xml_quote($c->{'name'}) .""; $ret .= ""; } foreach my $c (@{$case->get_selectable_components}){ $ret .= ""; $ret .= "". $c->{'id'} .""; - $ret .= "". $c->{'name'} .""; + $ret .= "". xml_quote($c->{'name'}) .""; $ret .= ""; } @@ -246,6 +278,8 @@ sub do_update{ my ($case) = @_; my $newtcaction = $cgi->param("tcaction"); my $newtceffect = $cgi->param("tceffect"); + my $newtcsetup = $cgi->param("tcsetup") || ''; + my $newtcbreakdown = $cgi->param("tcbreakdown") || ''; my $alias = $cgi->param('alias')|| ''; my $category = $cgi->param('category'); my $status = $cgi->param('status'); @@ -263,8 +297,6 @@ sub do_update{ } ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}) if $summary eq ''; - ThrowUserError('testopia-missing-required-field', {'field' => 'Case Action'}) if $newtcaction eq ''; - ThrowUserError('testopia-missing-required-field', {'field' => 'Case Expected Results'}) if $newtceffect eq ''; detaint_natural($status); detaint_natural($category); @@ -279,20 +311,28 @@ sub do_update{ trick_taint($requirement); trick_taint($newtcaction); trick_taint($newtceffect); + trick_taint($newtcbreakdown); + trick_taint($newtcsetup); trick_taint($tcdependson); trick_taint($tcblocks); validate_selection($category, 'category_id', 'test_case_categories'); validate_selection($status, 'case_status_id', 'test_case_status'); + my @buglist; + foreach my $bug (split(/[\s,]+/, $cgi->param('bugs'))){ + ValidateBugID($bug); + push @buglist, $bug; + } + ThrowUserError('testiopia-alias-exists', {'alias' => $alias}) if $case->check_alias($alias); ThrowUserError('testiopia-invalid-data', {'field' => 'isautomated', 'value' => $isautomated }) if ($isautomated !~ /^[01]$/); - if($case->diff_case_doc($newtcaction, $newtceffect) ne ''){ - $case->store_text($case->id, Bugzilla->user->id, $newtcaction, $newtceffect); + if($case->diff_case_doc($newtcaction, $newtceffect, $newtcsetup, $newtcbreakdown) ne ''){ + $case->store_text($case->id, Bugzilla->user->id, $newtcaction, $newtceffect, $newtcsetup, $newtcbreakdown); } my %newvalues = ( @@ -309,6 +349,17 @@ sub do_update{ ); $case->update(\%newvalues); $case->update_deps($cgi->param('tcdependson'), $cgi->param('tcblocks')); + # Add new tags + foreach my $tag_name (split(/[\s,]+/, $cgi->param('newtag'))){ + trick_taint($tag_name); + my $tag = Bugzilla::Testopia::TestTag->new({tag_name => $tag_name}); + my $tag_id = $tag->store; + $case->add_tag($tag_id); + } + # Attach bugs + foreach my $bug (@buglist){ + $case->attach_bug($bug); + } $cgi->delete_all; $cgi->param('case_id', $case->id); } diff --git a/mozilla/webtools/testopia/tr_show_caserun.cgi b/mozilla/webtools/testopia/tr_show_caserun.cgi index c296430b87e..b60554bbb97 100755 --- a/mozilla/webtools/testopia/tr_show_caserun.cgi +++ b/mozilla/webtools/testopia/tr_show_caserun.cgi @@ -28,6 +28,7 @@ use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Util; use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::Search; use Bugzilla::Testopia::Table; use Bugzilla::Testopia::TestRun; use Bugzilla::Testopia::TestCase; @@ -53,6 +54,8 @@ push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; my $action = $cgi->param('action') || ''; +# For use on the classic form + if ($action eq 'Commit'){ Bugzilla->login(LOGIN_REQUIRED); my $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id); @@ -61,31 +64,34 @@ if ($action eq 'Commit'){ my $status = $cgi->param('status'); my $notes = $cgi->param('notes'); my $build = $cgi->param('caserun_build'); + my $env = $cgi->param('caserun_env'); my $confirm = $cgi->param('confirm'); my $assignee = DBNameToIdAndCheck(trim($cgi->param('assignee'))); validate_test_id($build, 'build'); + validate_test_id($env, 'environment'); my @buglist; foreach my $bug (split(/[\s,]+/, $cgi->param('bugs'))){ ValidateBugID($bug); push @buglist, $bug; } + detaint_natural($env); detaint_natural($build); detaint_natural($status); trick_taint($notes); - + my %newfields = ( 'assignee' => $assignee, 'testedby' => Bugzilla->user->id, 'case_run_status_id' => $status, 'build_id' => $build, - 'notes' => $notes, + 'environment_id' => $env, ); - my $is = $caserun->check_exists($caserun->run_id, $caserun->case_id, $build); + my $is = $caserun->check_exists($caserun->run_id, $caserun->case_id, $build, $env); my $existing = Bugzilla::Testopia::TestCaseRun->new($is) if $is; - if ($build != $caserun->build->id + if (($build != $caserun->build->id || $env != $caserun->environment->id) && $is && $existing->status ne 'IDLE' && !$confirm){ @@ -113,6 +119,18 @@ if ($action eq 'Commit'){ exit; } + if ($notes){ + $caserun->append_note($notes); + } + + my $oldstatus_id = $caserun->status_id; + if ($status != $oldstatus_id){ + my $newstatus = $caserun->lookup_status($status); + my $oldstatus = $caserun->status(); + my $note = "Status changed from $oldstatus to $newstatus by ". Bugzilla->user->login; + $caserun->append_note($note); + } + foreach my $bug (@buglist){ $caserun->attach_bug($bug); } @@ -120,6 +138,30 @@ if ($action eq 'Commit'){ $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun->update(\%newfields)); display($caserun); } +elsif ($action eq 'delete'){ + Bugzilla->login(LOGIN_REQUIRED); + my $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id); + ThrowUserError("testopia-read-only", {'object' => 'case run'}) if !$caserun->candelete; + $caserun->do_delete; + $cgi->delete_all; + $cgi->param('current_tab', 'case_run'); + $cgi->param('run_id', $caserun->run->id); + my $search = Bugzilla::Testopia::Search->new($cgi); + my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_show_run.cgi', $cgi, undef, $search->query); + + my @case_list; + foreach my $cr (@{$table->list}){ + push @case_list, $cr->case_id; + } + my $case = Bugzilla::Testopia::TestCase->new({'case_id' => 0}); + $vars->{'run'} = $caserun->run; + $vars->{'table'} = $table; + $vars->{'case_list'} = join(",", @case_list); + $vars->{'action'} = 'Commit'; + $template->process("testopia/run/show.html.tmpl", $vars) || + ThrowTemplateError($template->error()); + +} #################### ### Ajax Actions ### #################### @@ -133,7 +175,7 @@ elsif ($action eq 'update_build'){ my $build_id = $cgi->param('build_id'); detaint_natural($build_id); validate_test_id($build_id, 'build'); - my $is = $caserun->check_exists($caserun->run_id, $caserun->case_id, $build_id); + my $is = $caserun->check_exists($caserun->run_id, $caserun->case_id, $build_id, $caserun->environment->id); if ($is){ $caserun = Bugzilla::Testopia::TestCaseRun->new($is); } @@ -154,6 +196,37 @@ elsif ($action eq 'update_build'){ ThrowTemplateError($template->error()); print $head_data . "|~+" . $body_data; } +elsif ($action eq 'update_environment'){ + Bugzilla->login(LOGIN_REQUIRED); + my $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id); + if (!$caserun->canedit) { + print "Error - You don't have permission"; + exit; + } + my $environment_id = $cgi->param('caserun_env'); + detaint_natural($environment_id); + validate_test_id($environment_id, 'environment'); + my $is = $caserun->check_exists($caserun->run_id, $caserun->case_id, $caserun->build->id, $environment_id); + if ($is){ + $caserun = Bugzilla::Testopia::TestCaseRun->new($is); + } + elsif ($caserun->status ne 'IDLE'){ + my $cid = $caserun->clone({'environment_id' => $environment_id }); + $caserun = Bugzilla::Testopia::TestCaseRun->new($cid); + } + else { + $caserun->set_environment($environment_id ); + } + my $body_data; + my $head_data; + $vars->{'caserun'} = $caserun; + $vars->{'index'} = $cgi->param('index'); + $template->process("testopia/caserun/short-form-header.html.tmpl", $vars, \$head_data) || + ThrowTemplateError($template->error()); + $template->process("testopia/caserun/short-form.html.tmpl", $vars, \$body_data) || + ThrowTemplateError($template->error()); + print $head_data . "|~+" . $body_data; +} elsif ($action eq 'update_status'){ Bugzilla->login(LOGIN_REQUIRED); my $caserun = Bugzilla::Testopia::TestCaseRun->new($caserun_id); @@ -163,7 +236,13 @@ elsif ($action eq 'update_status'){ } my $status_id = $cgi->param('status_id'); detaint_natural($status_id); - $caserun->set_status($status_id); + if ($status_id != $caserun->status_id){ + my $oldstatus = $caserun->status(); + $caserun->set_status($status_id); + my $newstatus = $caserun->status(); + my $note = "Status changed from $oldstatus to $newstatus by ". Bugzilla->user->login; + $caserun->append_note($note); + } print $caserun->status ."|". $caserun->close_date ."|". $caserun->testedby->login; if ($caserun->updated_deps) { print "|". join(',', @{$caserun->updated_deps}); @@ -178,7 +257,8 @@ elsif ($action eq 'update_note'){ } my $note = $cgi->param('note'); trick_taint($note); - $caserun->set_note($note); + $caserun->append_note($note); + print '
' .  $caserun->notes . '
'; } elsif ($action eq 'update_assignee'){ Bugzilla->login(LOGIN_REQUIRED); @@ -187,7 +267,11 @@ elsif ($action eq 'update_assignee'){ print "Error - You don't have permission"; exit; } - my $assignee_id = DBNameToIdAndCheck(trim($cgi->param('assignee'))); + my $assignee_id = login_to_id(trim($cgi->param('assignee'))); + if ($assignee_id == 0){ + print "Error - Invalid assignee"; + exit; + } $caserun->set_assignee($assignee_id); } elsif ($action eq 'attach_bug'){ @@ -205,7 +289,7 @@ elsif ($action eq 'attach_bug'){ foreach my $bug (@buglist){ $caserun->attach_bug($bug); } - foreach my $bug (@{$caserun->bugs}){ + foreach my $bug (@{$caserun->case->bugs}){ print &::GetBugLink($bug->bug_id, $bug->bug_id) ." "; } } diff --git a/mozilla/webtools/testopia/tr_show_environment.cgi b/mozilla/webtools/testopia/tr_show_environment.cgi new file mode 100755 index 00000000000..4a4eeca7879 --- /dev/null +++ b/mozilla/webtools/testopia/tr_show_environment.cgi @@ -0,0 +1,300 @@ +#!/usr/bin/perl -wT +# -*- 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 + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Config; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Testopia::Search; +use Bugzilla::Testopia::Table; +use Bugzilla::Testopia::Util; +use Bugzilla::Testopia::TestRun; +use Bugzilla::Testopia::Product; +use Bugzilla::Testopia::Classification; +use Bugzilla::Testopia::Environment; +use Bugzilla::Testopia::Environment::Element; +use Bugzilla::Testopia::Environment::Category; +use Bugzilla::Testopia::Environment::Property; +use Data::Dumper; +use JSON; + +Bugzilla->login(LOGIN_REQUIRED); +Bugzilla->batch(1); + +my $cgi = Bugzilla->cgi; + +use vars qw($vars $template); +my $template = Bugzilla->template; + +print $cgi->header; + +my $action = $cgi->param('action') || ''; +my $env_id = trim(Bugzilla->cgi->param('env_id')) || ''; + +unless ($env_id || $action){ + $template->process("testopia/environment/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} + +if ($action eq 'delete'){ + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError('testopia-no-delete', {'object' => 'Environment'}) unless Param("allow-test-deletion"); + ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; + $vars->{'environment'} = $env; + $template->process("testopia/environment/delete.html.tmpl", $vars) + || print $template->error(); + +} + +elsif ($action eq 'do_delete'){ + my $env = Bugzilla::Testopia::Environment->new($env_id); + ThrowUserError('testopia-no-delete', {'object' => 'Environment'}) unless Param("allow-test-deletion"); + ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; + ThrowUserError("testopia-non-zero-run-count", {'object' => 'Environment'}) if $env->get_run_count; + $env->obliterate; + $vars->{'tr_message'} = "Environment Deleted"; + display_list(); +} + + + +#################### +### Ajax Actions ### +#################### + +elsif ($action eq 'edit'){ + my $name = $cgi->param('name'); + my $product_id = $cgi->param('product_id'); + + trick_taint($name); + detaint_natural($product_id); + eval{ + validate_selection( $product_id, 'id', 'products'); + }; + if ($@){ + print "{error: 'Invalid product'}"; + exit; + } + + my $env = Bugzilla::Testopia::Environment->new($env_id); + unless ($env->update({'product_id' => $product_id})){ + print "{error: 'Error updating product'}"; + exit; + } + unless ($env->update({'name' => $name})){ + print "{error: 'Name already in use in this product, please choose another'}"; + exit; + } + unless ($env->canedit){ + print "{error: 'You don't have rights to edit this environment'}"; + exit; + } + print "{message: 'Environment updated'}"; + +} + +elsif ($action eq 'getChildren'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + + my $node = $data->{'node'}; + my $tree = $data->{'tree'}; + + my $id = $node->{'objectId'}; + my $type = $node->{'widgetId'}; + my $tree_id = $tree->{'objectId'}; + + detaint_natural($id); + trick_taint($type); + + for ($type){ + /classification/ && do { get_products($id); }; + /product/ && do { get_categories($id); }; + /category/ && do { get_category_element_json($id) }; + /element/ && do { get_element_children($id) }; + /property/ && do { get_validexp_json($id,$tree_id) }; + /environment/ && do { get_env_elements($id) }; + } +} + +elsif($action eq 'removeNode'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + + my $tree = $data->{'tree'}; + my $env_id = $tree->{'objectId'}; + + my $node = $data->{'node'}; + my $id = $node->{'objectId'}; + my $type = $node->{'widgetId'}; + + detaint_natural($env_id) unless $type =~ /validexp/; + detaint_natural($id); + trick_taint($type); + + my $env = Bugzilla::Testopia::Environment->new($env_id); + $env->delete_element($id); + + print "true"; + +} + +elsif($action eq 'set_selected'){ + my $type = $cgi->param('type'); + + if ($type =~ /exp/) + { + my $env_id = $cgi->param('env_id'); + my $value = $cgi->param('value'); + $value =~ s/|<\/span>//g; + $type =~ /^validexp(\d+)/; + my $prop_id = $1; + + detaint_natural($env_id); + detaint_natural($prop_id); + trick_taint($value); + + my $env = Bugzilla::Testopia::Environment->new($env_id); + + my $property = Bugzilla::Testopia::Environment::Property->new($prop_id); + my $elmnt_id = $property->element_id(); + my $old = $env->get_value_selected($env->id,$elmnt_id,$property->id); + $old = undef if $old eq $value; + if ($env->store_property_value($prop_id,$elmnt_id,$value) == 0){ + $env->update_property_value($prop_id,$elmnt_id,$value); + } + my $json ='{message:"Updated selection for '. $property->name .'."'; + $json .= ',old: "validexp'. $prop_id. '~'. $old if $old; + $json .= '"}'; + print $json; + } +} + +elsif($action eq 'move'){ + my $json = new JSON; + my $data = $json->jsonToObj($cgi->param('data')); + + my $element = $data->{'child'}; + my $env_tree = $data->{'newParentTree'}; + + my $element_id = $element->{'objectId'}; + my $environment_id = $env_tree->{'objectId'}; + trick_taint($element_id); + trick_taint($environment_id); + + my $env = Bugzilla::Testopia::Environment->new($environment_id); + $element = Bugzilla::Testopia::Environment::Element->new($element_id); + my $properties = $element->get_properties; + if (scalar @$properties == 0){ + my $success = $env->store_property_value(0, $element_id, ""); + } + foreach my $property (@$properties){ + my $success = $env->store_property_value($property->{'property_id'}, $element_id, ""); + if ($success == 0){print "{error:\"error\"";exit;} + } + + print "true";exit; + + print "{error:\""; + print "element_id: ".$element_id."
"; + print "environment_id: ".$environment_id."\"}"; +} + +else { + display(); +} + + +sub display { + detaint_natural($env_id); + my $env = Bugzilla::Testopia::Environment->new($env_id); + if(!defined($env)){ + my $env = Bugzilla::Testopia::Environment->new({'environment_id' => 0}); + ThrowUserError("testopia-read-only", {'object' => 'Environment'}) unless $env->canedit; + $vars->{'environment'} = $env; + $vars->{'action'} = 'do_add'; + $template->process("testopia/environment/add.html.tmpl", $vars) + || print $template->error(); + exit; + } + + my $category = Bugzilla::Testopia::Environment::Category->new({'id' => 0}); + if (Param('useclassification')){ + $vars->{'allhaschild'} = $category->get_all_child_count; + $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; + $vars->{'type'} = 'classification'; + } + else { + $vars->{'toplevel'} = $category->get_env_product_list; + $vars->{'type'} = 'product'; + } + $vars->{'user'} = Bugzilla->user; + $vars->{'action'} = 'do_edit'; + $vars->{'environment'} = $env; + $template->process("testopia/environment/show.html.tmpl", $vars) + || print $template->error(); + +} + +########################### +### Tree Helper Methods ### +########################### +sub get_products{ + my ($class_id) = (@_); + my $class = Bugzilla::Testopia::Classification->new($class_id); + print $class->products_to_json; +} + +sub get_categories{ + my ($product_id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new({}); + print $category->product_categories_to_json($product_id,1); +} + +sub get_category_element_json { + my ($id) = (@_); + my $category = Bugzilla::Testopia::Environment::Category->new($id); + my $fish = $category->elements_to_json("TRUE"); + print $fish; +} + +sub get_element_children { + my ($id) = (@_); + my $element = Bugzilla::Testopia::Environment::Element->new($id); + print $element->children_to_json(1); +} + +sub get_env_elements { + my ($id) = (@_); + my $env = Bugzilla::Testopia::Environment->new($id); + print $env->elements_to_json(1); +} + +sub get_validexp_json { + my ($id,$env_id) = (@_); + my $property = Bugzilla::Testopia::Environment::Property->new($id); + print $property->valid_exp_to_json(1,$env_id); +} diff --git a/mozilla/webtools/testopia/tr_show_plan.cgi b/mozilla/webtools/testopia/tr_show_plan.cgi index da03b5b32a8..585eb564043 100755 --- a/mozilla/webtools/testopia/tr_show_plan.cgi +++ b/mozilla/webtools/testopia/tr_show_plan.cgi @@ -31,11 +31,12 @@ use Bugzilla::Util; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::Table; use Bugzilla::Testopia::TestPlan; +use Bugzilla::Testopia::TestTag; use Bugzilla::Testopia::Category; use Bugzilla::Testopia::Attachment; use Bugzilla::Testopia::Search; use Bugzilla::Testopia::Table; - +use JSON; require "globals.pl"; @@ -267,7 +268,6 @@ sub do_update { ThrowUserError('testopia-missing-required-field', {'field' => 'product version'}) if ($prodver eq ''); - trick_taint($plan_name); trick_taint($prodver); @@ -296,6 +296,15 @@ sub do_update { ); $plan->update(\%newvalues); + + # Add new tags + foreach my $tag_name (split(/[\s,]+/, $cgi->param('newtag'))){ + trick_taint($tag_name); + my $tag = Bugzilla::Testopia::TestTag->new({tag_name => $tag_name}); + my $tag_id = $tag->store; + $plan->add_tag($tag_id); + } + $cgi->delete_all; $cgi->param('plan_id', $plan->id); } @@ -345,6 +354,12 @@ sub display { $vars->{'run_table'} = $table; } + my @dojo_search; + foreach my $run (@{$plan->test_runs}){ + push @dojo_search, "tip_" . $run->id; + } + push @dojo_search, "plandoc"; + $vars->{'dojo_search'} = objToJson(\@dojo_search); $vars->{'plan'} = $plan; $template->process("testopia/plan/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/mozilla/webtools/testopia/tr_show_run.cgi b/mozilla/webtools/testopia/tr_show_run.cgi index 082b8dac2e2..25296f1782f 100755 --- a/mozilla/webtools/testopia/tr_show_run.cgi +++ b/mozilla/webtools/testopia/tr_show_run.cgi @@ -29,6 +29,7 @@ use Bugzilla::Util; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::TestRun; use Bugzilla::Testopia::TestCaseRun; +use Bugzilla::Testopia::TestTag; use Bugzilla::Testopia::Environment; use Bugzilla::Testopia::Search; use Bugzilla::Testopia::Table; @@ -44,7 +45,7 @@ print Bugzilla->cgi->header(); my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; -my $run_id = trim(Bugzilla->cgi->param('run_id') || ''); +my $run_id = trim($cgi->param('run_id') || ''); unless ($run_id){ $template->process("testopia/run/choose.html.tmpl", $vars) @@ -79,12 +80,14 @@ elsif ($action eq 'History'){ ############# ### Clone ### ############# -elsif ($action eq 'Clone'){ +elsif ($action =~ /^Clone/){ Bugzilla->login(LOGIN_REQUIRED); my $run = Bugzilla::Testopia::TestRun->new($run_id); ThrowUserError("testopia-read-only", {'object' => 'run'}) unless $run->canedit; + my $case_list = $cgi->param('case_list'); do_update($run); $vars->{'run'} = $run; + $vars->{'case_list'} = $case_list if ($action =~/These Cases/); $vars->{'caserun'} = Bugzilla::Testopia::TestCaseRun->new({'case_run_id' => 0}); $template->process("testopia/run/clone.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -110,7 +113,20 @@ elsif ($action eq 'do_clone'){ $newrun->add_tag($newtagid); } } - if($cgi->param('copy_test_cases')){ + if ($cgi->param('case_list')){ + my @case_ids; + foreach my $id (split(",", $cgi->param('case_list'))){ + detaint_natural($id); + my $case = Bugzilla::Testopia::TestCase->new($id); + ThrowUserError('testopia-permission-denied', {'object' => 'Test Case'}) + unless $case->canview; + push @case_ids, $id + } + foreach my $id (@case_ids){ + $newrun->add_case_run($id); + } + } + if ($cgi->param('copy_test_cases')){ if ($cgi->param('status')){ my @status = $cgi->param('status'); foreach my $s (@status){ @@ -133,6 +149,7 @@ elsif ($action eq 'do_clone'){ } } } + $cgi->delete_all; $cgi->param('run_id', $newrun->id); display($newrun); } @@ -192,8 +209,9 @@ sub get_cc_xml { sub do_update { my ($run) = @_; - # possible race conditions. Should use locks + ThrowUserError('testopia-missing-required-field', {'field' => 'summary'}) if ($cgi->param('summary') eq ''); + ThrowUserError('testopia-missing-required-field', {'field' => 'environment'}) if ($cgi->param('environment') eq ''); my $timestamp; $timestamp = $run->stop_date; $timestamp = undef if $cgi->param('status') && $run->stop_date; @@ -236,6 +254,15 @@ sub do_update { 'notes' => $notes ); $run->update(\%newvalues); + + # Add new tags + foreach my $tag_name (split(/[\s,]+/, $cgi->param('newtag'))){ + trick_taint($tag_name); + my $tag = Bugzilla::Testopia::TestTag->new({tag_name => $tag_name}); + my $tag_id = $tag->store; + $run->add_tag($tag_id); + } + $cgi->delete_all; $cgi->param('run_id', $run->id); @@ -247,9 +274,15 @@ sub display { $cgi->param('current_tab', 'case_run'); my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('case_run', 'tr_show_run.cgi', $cgi, undef, $search->query); - + + my @case_list; + foreach my $caserun (@{$table->list}){ + push @case_list, $caserun->case_id; + } + my $case = Bugzilla::Testopia::TestCase->new({'case_id' => 0}); $vars->{'run'} = $run; $vars->{'table'} = $table; + $vars->{'case_list'} = join(",", @case_list); $vars->{'action'} = 'Commit'; $template->process("testopia/run/show.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/mozilla/webtools/testopia/tr_xmlrpc.cgi b/mozilla/webtools/testopia/tr_xmlrpc.cgi new file mode 100755 index 00000000000..604f1a2cd79 --- /dev/null +++ b/mozilla/webtools/testopia/tr_xmlrpc.cgi @@ -0,0 +1,48 @@ +#!/usr/bin/perl -wT +####!/usr/bin/perl -d:ptkdb -wT +# -*- 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 Bug Tracking System. +# +# Contributor(s): Marc Schumann +# Dallas Harken + +sub BEGIN +{ + # For use with ptkdb. + $ENV{DISPLAY}=":0.0"; +} + +use strict; +use lib qw(.); + +require "globals.pl"; + +use XMLRPC::Transport::HTTP; +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::WebService; + +# To be used in version 2.23/3.0 of Bugzilla +# Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_WEBSERVICE); + +die 'Content-Type must be "text/xml" when using API' unless + $ENV{'CONTENT_TYPE'} eq 'text/xml'; + +my $response = Bugzilla::WebService::XMLRPC::Transport::HTTP::CGI + ->dispatch_with({'TestPlan' => 'Bugzilla::WebService::Testopia::TestPlan', + 'TestCase' => 'Bugzilla::WebService::Testopia::TestCase', + 'TestRun' => 'Bugzilla::WebService::Testopia::TestRun', + 'TestCaseRun' => 'Bugzilla::WebService::Testopia::TestCaseRun', + }) + ->handle;