diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm index c1cb065053d..3853c3ee8fa 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Search.pm @@ -88,7 +88,9 @@ sub init { my $page = $cgi->param('page') || 0; detaint_natural($page) if $page; $page = undef if ($cgi->param('viewall')); - my $pagesize = 25; + my $pagesize = $cgi->param('pagesize') if $cgi->param('pagesize'); + detaint_natural($pagesize); + $pagesize ||= 25; my @specialchart; my @supptables; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm index 219068a6460..be428f3c74d 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Table.pm @@ -142,6 +142,7 @@ sub init { push (@list, $o); } $self->{'list'} = \@list; + $self->{'view_count'} = scalar @list; $self->{'id_list'} = join(",", @$count_res); } if ($cgi){ @@ -349,6 +350,7 @@ sub get_next{ sub list { return $_[0]->{'list'}; } sub id_list { return $_[0]->{'id_list'}; } sub list_count { return $_[0]->{'list_count'}; } +sub view_count { return $_[0]->{'view_count'}; } sub page { return $_[0]->{'page'}; } sub url_loc { return $_[0]->{'url_loc'}; } diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm index fade4ca4f0c..f6cf90cf503 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestPlan.pm @@ -434,6 +434,27 @@ sub get_product_components { return $ref; } +=head2 get_case_ids_by_category + +Returns a list of case_ids in this plan from the selected categories. + +=cut + +sub get_case_ids_by_category { + my $self = shift; + my ($categories) = @_; + my $dbh = Bugzilla->dbh; + + my $ref = $dbh->selectcol_arrayref( + "SELECT DISTINCT test_cases.case_id from test_cases + JOIN test_case_plans AS tcp ON tcp.case_id = test_cases.case_id + WHERE tcp.plan_id = ? + AND category_id in (". join(',',@$categories) . ")", + undef, $self->id); + + return $ref; +} + =head2 get_plan_types Returns a list of types from the test_plan_types table diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Util.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Util.pm index 8a984e3ff72..3043edd846e 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Util.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Util.pm @@ -40,7 +40,7 @@ use base qw(Exporter); can_view_product GetProducts get_field_id get_time_stamp validate_test_id validate_selection - validate_version); + validate_version support_server_push); use Bugzilla; use Bugzilla::Config; @@ -238,6 +238,19 @@ sub validate_version { return $res; } +sub support_server_push { + my ($cgi) = @_; + my $serverpush = + exists $ENV{'HTTP_USER_AGENT'} + && $ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/ + && $ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/ + && $ENV{'HTTP_USER_AGENT'} !~ /WebKit/ + && !defined($cgi->param('serverpush')) + || $cgi->param('serverpush'); + + return $serverpush; +} + =head1 AUTHOR Greg Hendricks 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 666f0ca4d3a..eb03a802dee 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 @@ -23,7 +23,7 @@ You are not authorized to view this [% object FILTER html %] [% ELSIF error == "testopia-read-only" %] [% title = "Insufficient Permissions" %] - You are not authorized to edit this [% object FILTER html %] + You are not authorized to edit [% object FILTER html %] [% id FILTER html %] [% ELSIF error == "testopia-no-delete" %] [% title = "Insufficient Permissions" %] You are not authorized to delete this [% object FILTER html %] @@ -109,5 +109,13 @@ [% ELSIF error == "testopia-none-selected" %] [% title = "Nothing Selected" %] You did not select any [% object FILTER none %]s to change. + [% ELSIF error == "testopia-query-too-large" %] + This query returns more than [% limit FILTER none %] rows.
+ Please do one of the following: +
    +
  1. Try to narrow your search.
  2. +
  3. Try using the pagesize and page URL paramaters to limit your results.
  4. +
  5. Use the XML-RPC Interface.
  6. +
[% END %] 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 1b3eb8759c1..1d67fda598c 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/blocks.html.tmpl @@ -39,6 +39,7 @@ [% END %] [% BLOCK navigation %] +[% SET MAX_LIMIT = 10000 %] [% IF ajax %] [% url = "javascript:page(" %] [% pc = ")" %] @@ -101,15 +102,21 @@       - [% IF ajax %] +  Out of [% table.page_count %] +     + [% IF ajax %] All  - [% ELSE %] + [% ELSE %] + [% IF table.list_count < MAX_LIMIT %] All  + [% ELSE %] + All [% END %] + [% END %] [% IF table.page < table.page_count - 1 %] Next > - Last >> + Last >> [% ELSE %] Next > Last >> diff --git a/mozilla/webtools/testopia/template/en/default/testopia/case/table.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/case/table.html.tmpl index 0de10cf3aea..4342cbf2536 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/case/table.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/case/table.html.tmpl @@ -24,6 +24,13 @@ [% PROCESS testopia/blocks.html.tmpl %] [% IF table.list_count > 0 %] +[%# More than this will cause your browser to get really slow + # So we won't show the action and effects %] +[% SET MAX_LIMIT = 1000 %] + +[% IF table.view_count >= MAX_LIMIT %] +

This list is too big to display quickly. Action and Effect will not be included.

+[% END %] Collapse All Expand All [% IF dotweak %] @@ -58,14 +65,14 @@ Select: Priority[% table.arrow IF table.last_sort == 'priority' %] Status[% table.arrow IF table.last_sort == 'status' %] - [% FOREACH case = table.list %] +[% FOREACH case = table.list %] [% index = loop.count %] - + - +
[% IF dotweak %][% END %][% IF loop.size < MAX_LIMIT %][% END %]
@@ -81,7 +88,7 @@ Select: [% case.priority FILTER html %] [% case.status %] - + [% IF loop.size < MAX_LIMIT %] @@ -122,7 +129,8 @@ Select: - [% END %] + [% END %] +[% END %] [% IF NOT table.viewall %] [% PROCESS navigation %] diff --git a/mozilla/webtools/testopia/template/en/default/testopia/plan/show.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/plan/show.html.tmpl index a396deff79c..823b376e280 100644 --- a/mozilla/webtools/testopia/template/en/default/testopia/plan/show.html.tmpl +++ b/mozilla/webtools/testopia/template/en/default/testopia/plan/show.html.tmpl @@ -227,6 +227,7 @@ [% PROCESS testopia/case/table.html.tmpl table = case_table %] Create a New Test Case + List Cases [%##### Test Runs #####%] @@ -239,6 +240,7 @@ [% PROCESS testopia/run/table.html.tmpl table = run_table %] Create a New Test Run + List Runs [%##### Attachments #####%]

diff --git a/mozilla/webtools/testopia/template/en/default/testopia/progress.html.tmpl b/mozilla/webtools/testopia/template/en/default/testopia/progress.html.tmpl new file mode 100644 index 00000000000..91b9938e06f --- /dev/null +++ b/mozilla/webtools/testopia/template/en/default/testopia/progress.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): Ed Fuentetaja + # Greg Hendricks + #%] + +[%# INTERFACE: + # complete: The number processed up till now + # total: The total number to process + #%] + +[% SET percent = ((complete / total) * 100) %] + + + + Processing + + +

Processing...

+ +
+ + + + + +
+ + \ No newline at end of file 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 a2ca605ca49..fa4b8926020 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 @@ -41,8 +41,15 @@ [% PROCESS testopia/blocks.html.tmpl %] [% PROCESS testopia/messages.html.tmpl %] +[%# More than this will cause your browser to get really slow + # So we won't show the action and effects %] +[% SET MAX_LIMIT = 500 %] + +[% IF table.view_count < MAX_LIMIT %] +[% END %] +
[% PROCESS testopia/run/table.html.tmpl %] diff --git a/mozilla/webtools/testopia/tr_list_caseruns.cgi b/mozilla/webtools/testopia/tr_list_caseruns.cgi index 9613d9d42b2..3c84d7d09b3 100755 --- a/mozilla/webtools/testopia/tr_list_caseruns.cgi +++ b/mozilla/webtools/testopia/tr_list_caseruns.cgi @@ -38,6 +38,7 @@ require 'globals.pl'; my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; +my $query_limit = 15000; push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; @@ -45,7 +46,20 @@ $cgi->send_cookie(-name => "TEST_LAST_ORDER", -value => $cgi->param('order'), -expires => "Fri, 01-Jan-2038 00:00:00 GMT"); Bugzilla->login(); -print $cgi->header; +my $serverpush = support_server_push($cgi); +if ($serverpush) { + print $cgi->multipart_init; + print $cgi->multipart_start; + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else { + print $cgi->header; +} +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; my $action = $cgi->param('action') || ''; @@ -54,20 +68,35 @@ if ($action eq 'Commit'){ # Get the list of checked items. This way we don't have to cycle through # every test case, only the ones that are checked. my $reg = qr/r_([\d]+)/; - my @caseruns; - foreach my $p ($cgi->param()){ - push @caseruns, Bugzilla::Testopia::TestCaseRun->new($1) if $p =~ $reg; - } - ThrowUserError('testopia-none-selected', {'object' => 'case-run'}) if (scalar @caseruns < 1); + my $params = join(" ", $cgi->param()); + my @params = $cgi->param(); my @buglist; + + ThrowUserError('testopia-none-selected', {'object' => 'case-run'}) unless $params =~ $reg; foreach my $bug (split(/[\s,]+/, $cgi->param('bugs'))){ ValidateBugID($bug); push @buglist, $bug; } - foreach my $caserun (@caseruns){ - - ThrowUserError("testopia-read-only", {'object' => 'Case Run'}) unless $caserun->canedit; + my $progress_interval = 250; + my $i = 0; + my $total = scalar @params; + + foreach my $p ($cgi->param()){ + my $caserun = Bugzilla::Testopia::TestCaseRun->new($1) if $p =~ $reg; + next unless $caserun; + + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + ThrowUserError("testopia-read-only", {'object' => 'Case Run', 'id' => $caserun->id}) unless $caserun->canedit; 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'))); @@ -104,8 +133,11 @@ if ($action eq 'Commit'){ $caserun->append_note($notes); } $vars->{'title'} = "Update Successful"; - $vars->{'tr_message'} = scalar @caseruns . ' Test Case-Runs Updated'; - + $vars->{'tr_message'} = "$i Test Case-Runs Updated"; + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } if ($cgi->param('run_id')){ my $run_id = $cgi->param('run_id'); my $run = Bugzilla::Testopia::TestRun->new($run_id); @@ -130,6 +162,7 @@ if ($action eq 'Commit'){ $template->process("testopia/search/advanced.html.tmpl", $vars) || ThrowTemplateError($template->error()); } + print $cgi->multipart_final if $serverpush; exit; } elsif ($action eq 'Delete Selected'){ @@ -141,6 +174,10 @@ elsif ($action eq 'Delete Selected'){ ThrowUserError("testopia-read-only", {'object' => 'case run'}) if ($caserun && !$caserun->candelete); push @caseruns, $caserun if $caserun; } + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } ThrowUserError('testopia-none-selected', {'object' => 'case-run'}) if (scalar @caseruns < 1); $vars->{'caseruns'} = \@caseruns; $vars->{'caseruncount'} = scalar @caseruns; @@ -149,6 +186,7 @@ elsif ($action eq 'Delete Selected'){ $vars->{'run_id'} = $cgi->param('run_id'); $template->process("testopia/caserun/delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); + print $cgi->multipart_final if $serverpush; exit; } elsif ($action eq 'do_delete'){ @@ -159,13 +197,30 @@ elsif ($action eq 'do_delete'){ push @caseruns, $caserun; ThrowUserError("testopia-read-only", {'object' => 'case run'}) if !$caserun->candelete; } + my $progress_interval = 250; + my $i = 0; + my $total = scalar @caseruns; foreach my $c (@caseruns){ + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } $c->obliterate; } + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } $vars->{'deleted'} = 1; $vars->{'run_id'} = $cgi->param('run_id'); $template->process("testopia/caserun/delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); + print $cgi->multipart_final if $serverpush; exit; } @@ -173,6 +228,7 @@ elsif ($action eq 'do_delete'){ $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); +ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; if ($table->list_count > 0){ my $prod_id = $table->list->[0]->run->plan->product_id; @@ -198,6 +254,10 @@ $vars->{'component_list'} = $case->get_available_components(); $vars->{'dotweak'} = UserInGroup('edittestcases'); $vars->{'table'} = $table; $vars->{'action'} = 'tr_list_caserun.cgi'; +if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; +} $template->process("testopia/caserun/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); - +print $cgi->multipart_final if $serverpush; diff --git a/mozilla/webtools/testopia/tr_list_cases.cgi b/mozilla/webtools/testopia/tr_list_cases.cgi index 94a86a3d1a8..a9795b112c5 100755 --- a/mozilla/webtools/testopia/tr_list_cases.cgi +++ b/mozilla/webtools/testopia/tr_list_cases.cgi @@ -40,6 +40,7 @@ require "globals.pl"; my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; +my $query_limit = 10000; push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; @@ -47,21 +48,56 @@ $cgi->send_cookie(-name => "TEST_LAST_ORDER", -value => $cgi->param('order'), -expires => "Fri, 01-Jan-2038 00:00:00 GMT"); Bugzilla->login(); -print $cgi->header; -my $action = $cgi->param('action') || ''; +my $action = $cgi->param('action') || ''; +my $serverpush = support_server_push($cgi); +if ($serverpush) { + print $cgi->multipart_init; + print $cgi->multipart_start; + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else { + print $cgi->header; +} +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; + +############### +### Actions ### +############### if ($action eq 'Commit'){ Bugzilla->login(LOGIN_REQUIRED); - # Get the list of checked items. This way we don't have to cycle through - # every test case, only the ones that are checked. + + # Match the list of checked items. my $reg = qr/c_([\d]+)/; - my @cases; + my $params = join(" ", $cgi->param()); + my @params = $cgi->param(); + + ThrowUserError('testopia-none-selected', {'object' => 'case'}) unless $params =~ $reg; + + my $progress_interval = 250; + my $i = 0; + my $total = scalar @params; + foreach my $p ($cgi->param()){ - push @cases, Bugzilla::Testopia::TestCase->new($1) if $p =~ $reg; - } - ThrowUserError('testopia-none-selected', {'object' => 'case'}) if (scalar @cases < 1); - foreach my $case (@cases){ - ThowUserError('insufficient-perms') unless $case->canedit; + my $case = Bugzilla::Testopia::TestCase->new($1) if $p =~ $reg; + next unless $case; + + ThrowUserError("testopia-read-only", {'object' => 'case', 'id' => $case->id}) unless $case->canedit; + + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + my $requirement = $cgi->param('requirement') eq '--Do Not Change--' ? $case->requirement : $cgi->param('requirement'); my $arguments = $cgi->param('arguments') eq '--Do Not Change--' ? $case->arguments : $cgi->param('requirement'); my $script = $cgi->param('script') eq '--Do Not Change--' ? $case->script : $cgi->param('requirement'); @@ -121,6 +157,10 @@ if ($action eq 'Commit'){ $case->link_plan($planid); } } + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } my @runlist = split(/[\s,]+/, $cgi->param('addruns')); if (scalar @runlist == 1){ my $run_id = $cgi->param('addruns'); @@ -146,17 +186,20 @@ if ($action eq 'Commit'){ my $case = Bugzilla::Testopia::TestCase->new({ 'case_id' => 0 }); $vars->{'case'} = $case; $vars->{'title'} = "Update Successful"; - $vars->{'tr_message'} = scalar @cases . " Test Cases Updated"; + $vars->{'tr_message'} = "$i Test Cases Updated"; $vars->{'current_tab'} = 'case'; + $template->process("testopia/search/advanced.html.tmpl", $vars) || ThrowTemplateError($template->error()); + print $cgi->multipart_final if $serverpush; exit; -} +} # Take the search from the URL params and convert it to SQL $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); +ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; # Check that all of the test cases returned only belong to one product. if ($table->list_count > 0){ @@ -210,7 +253,11 @@ $vars->{'status_list'} = $status_list; $vars->{'priority_list'} = $priority_list; $vars->{'dotweak'} = UserInGroup('edittestcases'); $vars->{'table'} = $table; - +if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; +} $template->process("testopia/case/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); - + +print $cgi->multipart_final if $serverpush; diff --git a/mozilla/webtools/testopia/tr_list_plans.cgi b/mozilla/webtools/testopia/tr_list_plans.cgi index 56c666e5861..9cb446692b7 100755 --- a/mozilla/webtools/testopia/tr_list_plans.cgi +++ b/mozilla/webtools/testopia/tr_list_plans.cgi @@ -37,9 +37,23 @@ require "globals.pl"; my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; +my $query_limit = 5000; Bugzilla->login(); -print $cgi->header; +my $serverpush = support_server_push($cgi); +if ($serverpush) { + print $cgi->multipart_init; + print $cgi->multipart_start; + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else { + print $cgi->header; +} +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; my $action = $cgi->param('action') || ''; if ($action eq 'Commit'){ @@ -47,13 +61,30 @@ if ($action eq 'Commit'){ # Get the list of checked items. This way we don't have to cycle through # every test case, only the ones that are checked. my $reg = qr/p_([\d]+)/; - my @plans; + my $params = join(" ", $cgi->param()); + my @params = $cgi->param(); + + ThrowUserError('testopia-none-selected', {'object' => 'plan'}) unless $params =~ $reg; + + my $progress_interval = 250; + my $i = 0; + my $total = scalar @params; + foreach my $p ($cgi->param()){ - push @plans, Bugzilla::Testopia::TestPlan->new($1) if $p =~ $reg; - } - ThrowUserError('testopia-none-selected', {'object' => 'plan'}) if (scalar @plans < 1); - foreach my $plan (@plans){ - ThowUserError('insufficient-perms') unless $plan->canedit; + my $plan = Bugzilla::Testopia::TestPlan->new($1) if $p =~ $reg; + next unless $plan; + + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + ThrowUserError("testopia-read-only", {'object' => 'plan', 'id' => $plan->id}) unless $plan->canedit; my $plan_type = $cgi->param('plan_type') == -1 ? $plan->type_id : $cgi->param('plan_type'); my $product = $cgi->param('product_id') == -1 ? $plan->product_id : $cgi->param('product_id'); my $prodver = $cgi->param('prod_version') == -1 ? $plan->product_version : $cgi->param('prod_version'); @@ -80,20 +111,26 @@ if ($action eq 'Commit'){ } } } + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } my $plan = Bugzilla::Testopia::TestPlan->new({ 'plan_id' => 0 }); $vars->{'plan'} = $plan; $vars->{'title'} = "Update Successful"; - $vars->{'tr_message'} = scalar @plans . " Test Plan(s) Updated"; + $vars->{'tr_message'} = "$i Test Plan(s) Updated"; $vars->{'current_tab'} = 'plan'; $template->process("testopia/search/advanced.html.tmpl", $vars) || ThrowTemplateError($template->error()); + print $cgi->multipart_final if $serverpush; exit; - + } else { $cgi->param('current_tab', 'plan'); my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('plan', 'tr_list_plans.cgi', $cgi, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; my $p = Bugzilla::Testopia::TestPlan->new({'plan_id' => 0 }); my $product_list = $p->get_available_products; @@ -110,7 +147,11 @@ else { $vars->{'fullwidth'} = 1; #novellonly $vars->{'dotweak'} = UserInGroup('managetestplans'); $vars->{'table'} = $table; + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } $template->process("testopia/plan/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); - + print $cgi->multipart_final if $serverpush; } diff --git a/mozilla/webtools/testopia/tr_list_runs.cgi b/mozilla/webtools/testopia/tr_list_runs.cgi index daf050002e5..a41317e2524 100755 --- a/mozilla/webtools/testopia/tr_list_runs.cgi +++ b/mozilla/webtools/testopia/tr_list_runs.cgi @@ -38,9 +38,23 @@ require "globals.pl"; my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; +my $query_limit = 5000; Bugzilla->login(); -print $cgi->header; +my $serverpush = support_server_push($cgi); +if ($serverpush) { + print $cgi->multipart_init; + print $cgi->multipart_start; + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); +} +else { + print $cgi->header; +} +# prevent DOS attacks from multiple refreshes of large data +$::SIG{TERM} = 'DEFAULT'; +$::SIG{PIPE} = 'DEFAULT'; my $action = $cgi->param('action') || ''; if ($action eq 'Commit'){ @@ -48,14 +62,31 @@ if ($action eq 'Commit'){ # Get the list of checked items. This way we don't have to cycle through # every test case, only the ones that are checked. my $reg = qr/r_([\d]+)/; - my @runs; - foreach my $r ($cgi->param()){ - push @runs, Bugzilla::Testopia::TestRun->new($1) if $r =~ $reg; - } - ThrowUserError('testopia-none-selected', {'object' => 'run'}) if (scalar @runs < 1); + my $params = join(" ", $cgi->param()); + my @params = $cgi->param(); + + ThrowUserError('testopia-none-selected', {'object' => 'run'}) unless $params =~ $reg; ThrowUserError('testopia-missing-required-field', {'field' => 'environment'}) if ($cgi->param('environment') eq ''); - foreach my $run (@runs){ - ThowUserError('insufficient-perms') unless $run->canedit; + + my $progress_interval = 250; + my $i = 0; + my $total = scalar @params; + + foreach my $p ($cgi->param()){ + my $run = Bugzilla::Testopia::TestRun->new($1) if $p =~ $reg; + next unless $run; + + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + ThrowUserError("testopia-read-only", {'object' => 'run', 'id' => $run->id}) unless $run->canedit; my $manager = DBNameToIdAndCheck(trim($cgi->param('manager'))) if $cgi->param('manager'); my $status; if ($cgi->param('run_status')){ @@ -89,21 +120,28 @@ if ($action eq 'Commit'){ } } } + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } my $run = Bugzilla::Testopia::TestRun->new({ 'run_id' => 0 }); $vars->{'run'} = $run; $vars->{'title'} = "Update Successful"; - $vars->{'tr_message'} = scalar @runs . " Test Runs Updated"; + $vars->{'tr_message'} = "$i Test Runs Updated"; $vars->{'current_tab'} = 'run'; $vars->{'build_list'} = $run->get_distinct_builds(); $template->process("testopia/search/advanced.html.tmpl", $vars) || ThrowTemplateError($template->error()); + print $cgi->multipart_final if $serverpush; exit; - + } else { $cgi->param('current_tab', 'run'); my $search = Bugzilla::Testopia::Search->new($cgi); - my $table = Bugzilla::Testopia::Table->new('run', 'tr_list_runs.cgi', $cgi, undef, $search->query); + my $table = Bugzilla::Testopia::Table->new('run', 'tr_list_runs.cgi', $cgi, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; + if ($table->list_count > 0){ my $plan_id = $table->list->[0]->plan->product_id; foreach my $run (@{$table->list}){ @@ -131,7 +169,11 @@ else { $vars->{'fullwidth'} = 1; #novellonly $vars->{'dotweak'} = UserInGroup('runtests'); $vars->{'table'} = $table; + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } $template->process("testopia/run/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); - + print $cgi->multipart_final if $serverpush; } diff --git a/mozilla/webtools/testopia/tr_new_run.cgi b/mozilla/webtools/testopia/tr_new_run.cgi index 3b639ea89eb..7a110148b34 100755 --- a/mozilla/webtools/testopia/tr_new_run.cgi +++ b/mozilla/webtools/testopia/tr_new_run.cgi @@ -36,21 +36,24 @@ require "globals.pl"; use vars qw($template $vars); my $template = Bugzilla->template; +my $query_limit = 10000; Bugzilla->login(LOGIN_REQUIRED); -ThrowUserError("testopia-create-denied", {'object' => 'Test Run'}) unless UserInGroup('edittestcases'); - -print Bugzilla->cgi->header(); - my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; +unless (UserInGroup('edittestcases')){ + print $cgi->header; + ThrowUserError("testopia-create-denied", {'object' => 'Test Run'}); +} + push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; my $action = $cgi->param('action') || ''; my $plan_id = $cgi->param('plan_id'); unless ($plan_id){ $vars->{'form_action'} = 'tr_new_run.cgi'; + print $cgi->header; $template->process("testopia/plan/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -59,11 +62,22 @@ unless ($plan_id){ detaint_natural($plan_id); validate_test_id($plan_id, 'plan'); -ThrowUserError("testopia-missing-parameter", {'param' => 'plan_id'}) unless ($plan_id); my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); -ThrowUserError('testopia-create-build', {'plan' => $plan}) unless (scalar @{$plan->builds} >0); +unless (scalar @{$plan->builds} >0){ + print $cgi->header; + ThrowUserError('testopia-create-build', {'plan' => $plan}); +} if ($action eq 'Add'){ + my $serverpush = support_server_push($cgi); + if ($serverpush) { + print $cgi->multipart_init; + print $cgi->multipart_start; + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + my $manager = DBNameToIdAndCheck($cgi->param('manager')); my $status = $cgi->param('status'); my $prodver = $cgi->param('product_version'); @@ -121,23 +135,43 @@ if ($action eq 'Add'){ }); my $run_id = $run->store; $run = Bugzilla::Testopia::TestRun->new($run_id); - foreach my $case_id (@c){ + + my $progress_interval = 250; + my $i = 0; + my $total = scalar @c; + foreach my $case_id (@c){ + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } $run->add_case_run($case_id); } # clear the params so we don't confuse search. $cgi->delete_all; - + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } else { + print $cgi->header; + } $cgi->param('current_tab', 'case_run'); $cgi->param('run_id', $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); - + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; + $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()); + print $cgi->multipart_final if $serverpush; } @@ -160,6 +194,7 @@ else { 'plan' => $plan, 'plan_text_version' => $plan->version } ); ThrowUserError('testopia-create-environment') unless (scalar @{$run->environments} > 0); + print $cgi->header; $vars->{'run'} = $run; $template->process("testopia/run/add.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/mozilla/webtools/testopia/tr_show_case.cgi b/mozilla/webtools/testopia/tr_show_case.cgi index ecbab24cee2..e652909f94e 100755 --- a/mozilla/webtools/testopia/tr_show_case.cgi +++ b/mozilla/webtools/testopia/tr_show_case.cgi @@ -37,7 +37,7 @@ use Bugzilla::Testopia::Table; use vars qw($template $vars); my $template = Bugzilla->template; - +my $query_limit = 15000; require "globals.pl"; Bugzilla->login(); @@ -392,6 +392,7 @@ 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_case.cgi', $cgi, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; $vars->{'case'} = $case; $vars->{'table'} = $table; $template->process("testopia/case/show.html.tmpl", $vars) || diff --git a/mozilla/webtools/testopia/tr_show_caserun.cgi b/mozilla/webtools/testopia/tr_show_caserun.cgi index 7dbbc6182a6..23b5aca4c98 100755 --- a/mozilla/webtools/testopia/tr_show_caserun.cgi +++ b/mozilla/webtools/testopia/tr_show_caserun.cgi @@ -36,7 +36,7 @@ use Bugzilla::Testopia::TestCaseRun; use vars qw($template $vars); my $template = Bugzilla->template; - +my $query_limit = 15000; # These are going away after 2.22 require "globals.pl"; @@ -159,6 +159,7 @@ elsif ($action eq 'do_delete'){ $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); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; my @case_list; foreach my $cr (@{$table->list}){ @@ -329,6 +330,7 @@ sub display { my $caserun = shift; ThrowUserError('insufficient-view-perms') if !$caserun->canview; my $table = Bugzilla::Testopia::Table->new('case_run'); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; $vars->{'table'} = $table; $vars->{'caserun'} = $caserun; $vars->{'action'} = 'tr_show_caserun.cgi'; diff --git a/mozilla/webtools/testopia/tr_show_plan.cgi b/mozilla/webtools/testopia/tr_show_plan.cgi index c3e93b578bc..33eed361646 100755 --- a/mozilla/webtools/testopia/tr_show_plan.cgi +++ b/mozilla/webtools/testopia/tr_show_plan.cgi @@ -42,10 +42,11 @@ require "globals.pl"; use vars qw($template $vars); my $template = Bugzilla->template; +my $run_query_limit = 5000; +my $case_query_limit = 10000; Bugzilla->login(); -print Bugzilla->cgi->header(); - + my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; @@ -53,6 +54,7 @@ my $plan_id = trim(Bugzilla->cgi->param('plan_id') || ''); unless ($plan_id){ $vars->{'form_action'} = 'tr_show_plan.cgi'; + print $cgi->header; $template->process("testopia/plan/choose.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; @@ -60,6 +62,8 @@ unless ($plan_id){ validate_test_id($plan_id, 'plan'); push @{$::vars->{'style_urls'}}, 'testopia/css/default.css'; +my $serverpush = support_server_push($cgi); + my $action = $cgi->param('action') || ''; $vars->{'action'} = "Commit"; $vars->{'form_action'} = "tr_show_plan.cgi"; @@ -78,6 +82,7 @@ if ($action eq 'Archive' || $action eq 'Unarchive'){ $plan->toggle_archive(Bugzilla->user->id); $vars->{'tr_message'} = $plan->isactive == 0 ? "Plan archived":"Plan Unarchived"; + print $cgi->header; display($plan); } @@ -90,6 +95,7 @@ elsif ($action eq 'Clone'){ ThrowUserError("testopia-read-only", {'object' => 'plan'}) unless $plan->canedit; do_update($plan); $vars->{'plan'} = $plan; + print $cgi->header; $template->process("testopia/plan/clone.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -99,6 +105,14 @@ elsif ($action eq 'do_clone'){ my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); ThrowUserError("testopia-read-only", {'object' => 'plan'}) unless $plan->canedit; my $plan_name = $cgi->param('plan_name'); + if ($serverpush) { + print $cgi->multipart_init(); + print $cgi->multipart_start(); + + $template->process("list/server-push.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + + } # All DB actions use place holders so we are OK doing this trick_taint($plan_name); @@ -114,28 +128,49 @@ elsif ($action eq 'do_clone'){ } } if ($cgi->param('copy_cases')){ - my @caseids; + my @catids; #TODO: Copy case to the new category foreach my $id ($cgi->param('clone_categories')){ - my $cat = Bugzilla::Testopia::Category->new($id); - push @caseids, @{$cat->case_ids}; + detaint_natural($id); + validate_selection($id,'category_id','test_case_categories'); + push @catids, $id; } - foreach my $case (@{$plan->test_cases}){ - if (lsearch(\@caseids, $case->category->id)){ - if ($cgi->param('copy_cases') == 2 ){ - my $caseid = $case->copy($newplan->id, 1); - $case->link_plan($newplan->id, $caseid); - } - else { - $case->link_plan($newplan->id); - } + my $caseids_ref = $plan->get_case_ids_by_category(\@catids); + my $progress_interval = 250; + my $i = 0; + my $total = scalar @$caseids_ref; + foreach my $id (@$caseids_ref){ + $i++; + if ($i % $progress_interval == 0 && $serverpush){ + print $cgi->multipart_end; + print $cgi->multipart_start; + $vars->{'complete'} = $i; + $vars->{'total'} = $total; + $template->process("testopia/progress.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + + my $case = Bugzilla::Testopia::TestCase->new($id); + if ($cgi->param('copy_cases') == 2 ){ + my $caseid = $case->copy($newplan->id, 1); + $case->link_plan($newplan->id, $caseid); + } + else { + $case->link_plan($newplan->id); } } } - + if ($serverpush) { + print $cgi->multipart_end; + print $cgi->multipart_start; + } else { + print $cgi->header; + } $vars->{'tr_message'} = "Plan ". $plan->name ." cloned as " . $newplan->name ."."; $cgi->param('plan_id', $newplan->id); - display($newplan); + + display($newplan); + print $cgi->multipart_final if $serverpush; } ### Changes to Plan Attributes or Doc ### @@ -145,13 +180,15 @@ elsif ($action eq 'Commit'){ ThrowUserError("testopia-read-only", {'object' => 'plan'}) unless $plan->canedit; do_update($plan); $vars->{'tr_message'} = "Test plan updated"; + print $cgi->header; display($plan); } elsif ($action eq 'Print'){ my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); ThrowUserError("testopia-permission-denied", {'object' => 'plan'}) unless $plan->canview; - $vars->{'plan'} = $plan; + $vars->{'plan'} = $plan; + print $cgi->header; $template->process("testopia/plan/show-document.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -163,6 +200,7 @@ elsif ($action eq 'History'){ $vars->{'diff'} = $plan->diff_plan_doc($cgi->param('new'),$cgi->param('old')); $vars->{'new'} = $cgi->param('new'); $vars->{'old'} = $cgi->param('old'); + print $cgi->header; $template->process("testopia/plan/history.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -203,6 +241,7 @@ elsif ($action eq 'Attach'){ $vars->{'tr_message'} = "Attachment added successfully"; do_update($plan); + print $cgi->header; display(Bugzilla::Testopia::TestPlan->new($plan_id)); } #TODO: Import plans @@ -214,6 +253,7 @@ elsif ($action eq 'Delete'){ my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); ThrowUserError("testopia-read-only", {'object' => 'plan'}) unless $plan->candelete; $vars->{'plan'} = $plan; + print $cgi->header; $template->process("testopia/plan/delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -224,6 +264,7 @@ elsif ($action eq 'do_delete'){ ThrowUserError("testopia-read-only", {'object' => 'plan'}) unless $plan->candelete; $plan->obliterate; $vars->{'deleted'} = 1; + print $cgi->header; $template->process("testopia/plan/delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); } @@ -255,6 +296,7 @@ elsif ($action eq 'caselist'){ my $table = Bugzilla::Testopia::Table->new('case', 'tr_list_cases.cgi', $cgi, $plan->test_cases); $table->{'ajax'} = 1; $vars->{'table'} = $table; + print $cgi->header; $template->process("testopia/case/table.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -265,6 +307,7 @@ elsif ($action eq 'caselist'){ else{ my $plan = Bugzilla::Testopia::TestPlan->new($plan_id); ThrowUserError("testopia-permission-denied", {'object' => 'plan'}) unless $plan->canview; + print $cgi->header; display($plan); } ################### @@ -335,28 +378,32 @@ sub display { if (($cgi->param('order') || $cgi->param('page') || $cgi->param('viewall')) && $cgi->param('current_tab') eq 'case'){ my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('case', 'tr_show_plan.cgi', $cgi, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $case_query_limit}) if $table->list_count > $case_query_limit; + $vars->{'case_table'} = $table; - $runquery->delete('order'); $runquery->delete('page'); $runquery->delete('viewall'); $runquery->param('current_tab', 'run'); $search = Bugzilla::Testopia::Search->new($runquery); $table = Bugzilla::Testopia::Table->new('run', 'tr_show_plan.cgi', $runquery, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $run_query_limit}) if $table->list_count > $run_query_limit; $vars->{'run_table'} = $table; } elsif (($cgi->param('order') || $cgi->param('page') || $cgi->param('viewall')) && $cgi->param('current_tab') eq 'run'){ my $search = Bugzilla::Testopia::Search->new($cgi); my $table = Bugzilla::Testopia::Table->new('run', 'tr_show_plan.cgi', $cgi, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $run_query_limit}) if $table->list_count > $run_query_limit; + $vars->{'run_table'} = $table; - $casequery->delete('order'); $casequery->delete('page'); $casequery->delete('viewall'); $casequery->param('current_tab', 'case'); $search = Bugzilla::Testopia::Search->new($casequery); $table = Bugzilla::Testopia::Table->new('case', 'tr_show_plan.cgi', $casequery, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $case_query_limit}) if $table->list_count > $case_query_limit; $vars->{'case_table'} = $table; } else { @@ -364,11 +411,13 @@ sub display { $casequery->param('current_tab', 'case'); my $search = Bugzilla::Testopia::Search->new($casequery); my $table = Bugzilla::Testopia::Table->new('case', 'tr_show_plan.cgi', $casequery, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $case_query_limit}) if $table->list_count > $case_query_limit; $vars->{'case_table'} = $table; $runquery->param('current_tab', 'run'); $search = Bugzilla::Testopia::Search->new($runquery); $table = Bugzilla::Testopia::Table->new('run', 'tr_show_plan.cgi', $runquery, undef, $search->query); + ThrowUserError('testopia-query-too-large', {'limit' => $run_query_limit}) if $table->list_count > $run_query_limit; $vars->{'run_table'} = $table; } @@ -379,6 +428,7 @@ sub display { 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 338187aa886..4f6f2e3b232 100755 --- a/mozilla/webtools/testopia/tr_show_run.cgi +++ b/mozilla/webtools/testopia/tr_show_run.cgi @@ -36,6 +36,7 @@ use Bugzilla::Testopia::Table; use vars qw($template $vars); my $template = Bugzilla->template; +my $query_limit = 15000; require "globals.pl"; @@ -294,6 +295,7 @@ 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); + ThrowUserError('testopia-query-too-large', {'limit' => $query_limit}) if $table->list_count > $query_limit; my @case_list; foreach my $caserun (@{$table->list}){