diff options
author | David Lawrence <dkl@mozilla.com> | 2016-02-26 18:57:55 +0100 |
---|---|---|
committer | David Lawrence <dkl@mozilla.com> | 2016-02-26 18:57:55 +0100 |
commit | 9b6ec1f545da1cc4088ddf9cc117747954e58e65 (patch) | |
tree | 6cc3eb342a740b795052e587756f6c33438b772a /xt/lib/Bugzilla/Test/Search | |
parent | 6f70920f2d2bb038a371e3cb3debff44f7001fa8 (diff) | |
download | bugzilla-9b6ec1f545da1cc4088ddf9cc117747954e58e65.tar.gz bugzilla-9b6ec1f545da1cc4088ddf9cc117747954e58e65.tar.xz |
Bug 1069799 - move the QA repository into the main repository
r=LpSolit
Diffstat (limited to 'xt/lib/Bugzilla/Test/Search')
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/AndTest.pm | 52 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/Constants.pm | 1203 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/CustomTest.pm | 101 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/FieldTest.pm | 617 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/FieldTestNormal.pm | 104 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/InjectionTest.pm | 77 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/NotTest.pm | 61 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/OperatorTest.pm | 103 | ||||
-rw-r--r-- | xt/lib/Bugzilla/Test/Search/OrTest.pm | 141 |
9 files changed, 0 insertions, 2459 deletions
diff --git a/xt/lib/Bugzilla/Test/Search/AndTest.pm b/xt/lib/Bugzilla/Test/Search/AndTest.pm deleted file mode 100644 index f34ba1f3a..000000000 --- a/xt/lib/Bugzilla/Test/Search/AndTest.pm +++ /dev/null @@ -1,52 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This test combines two field/operator combinations using AND in -# a single boolean chart. -package Bugzilla::Test::Search::AndTest; -use parent qw(Bugzilla::Test::Search::OrTest); - -use Bugzilla::Test::Search::Constants; -use List::MoreUtils qw(all); - -use constant type => 'AND'; - -############# -# Accessors # -############# - -# In an AND test, bugs ARE supposed to be contained only if they are contained -# by ALL tests. -sub bug_is_contained { - my ($self, $number) = @_; - return all { $_->bug_is_contained($number) } $self->field_tests; -} - -sub _bug_will_actually_be_contained { - my ($self, $number) = @_; - return all { $_->will_actually_contain_bug($number) } $self->field_tests; -} - -############################## -# Bugzilla::Search arguments # -############################## - -sub search_params { - my ($self) = @_; - my @all_params = map { $_->search_params } $self->field_tests; - my %params; - my $chart = 0; - foreach my $item (@all_params) { - $params{"field0-$chart-0"} = $item->{'field0-0-0'}; - $params{"type0-$chart-0"} = $item->{'type0-0-0'}; - $params{"value0-$chart-0"} = $item->{'value0-0-0'}; - $chart++; - } - return \%params; -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/Constants.pm b/xt/lib/Bugzilla/Test/Search/Constants.pm deleted file mode 100644 index 5d84ec6ff..000000000 --- a/xt/lib/Bugzilla/Test/Search/Constants.pm +++ /dev/null @@ -1,1203 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - - -# These are constants used by Bugzilla::Test::Search. -# See the comment at the top of that package for a general overview -# of how the search test works, and how the constants are used. -# More detailed information on each constant is available in the comments -# in this file. -package Bugzilla::Test::Search::Constants; -use parent qw(Exporter); -use Bugzilla::Constants; -use Bugzilla::Util qw(generate_random_password); - -our @EXPORT = qw( - ATTACHMENT_FIELDS - BROKEN_NOT - COLUMN_TRANSLATION - COMMENT_FIELDS - CUSTOM_FIELDS - CUSTOM_SEARCH_TESTS - FIELD_SIZE - FIELD_SUBSTR_SIZE - FLAG_FIELDS - INJECTION_BROKEN_FIELD - INJECTION_BROKEN_OPERATOR - INJECTION_TESTS - KNOWN_BROKEN - NUM_BUGS - NUM_SEARCH_TESTS - SKIP_FIELDS - SPECIAL_PARAM_TESTS - SUBSTR_NO_FIELD_ADD - SUBSTR_SIZE - TESTS - TESTS_PER_RUN - USER_FIELDS -); - -# Bug 1 is designed to be found by all the "equals" tests. It has -# multiple values for several fields where other fields only have -# one value. -# -# Bug 2 and 3 have a dependency relationship with Bug 1, -# but show up in "not equals" tests. We do use bug 2 in multiple-value -# tests. -# -# Bug 4 should never show up in any equals test, and has no relationship -# with any other bug. However, it does have all its fields set. -# -# Bug 5 only has values set for mandatory fields, to expose problems -# that happen with "not equals" tests failing to catch bugs that don't -# have a value set at all. -# -# Bug 6 is a clone of Bug 1, but is in a group that the searcher isn't -# in. -use constant NUM_BUGS => 6; - -# How many tests there are for each operator/field combination other -# than the "contains" tests. -use constant NUM_SEARCH_TESTS => 3; -# This is how many tests get run for each field/operator. -use constant TESTS_PER_RUN => NUM_SEARCH_TESTS + NUM_BUGS; - -# This is how many random characters we generate for most fields' names. -# (Some fields can't be this long, though, so they have custom lengths -# in Bugzilla::Test::Search). -use constant FIELD_SIZE => 30; - -# These are the custom fields that are created if the BZ_MODIFY_DATABASE_TESTS -# environment variable is set. -use constant CUSTOM_FIELDS => { - FIELD_TYPE_FREETEXT, 'cf_freetext', - FIELD_TYPE_SINGLE_SELECT, 'cf_single_select', - FIELD_TYPE_MULTI_SELECT, 'cf_multi_select', - FIELD_TYPE_TEXTAREA, 'cf_textarea', - FIELD_TYPE_DATETIME, 'cf_datetime', - FIELD_TYPE_BUG_ID, 'cf_bugid', -}; - -# This translates fielddefs names into Search column names. -use constant COLUMN_TRANSLATION => { - creation_ts => 'opendate', - delta_ts => 'changeddate', - work_time => 'actual_time', -}; - -# Make comment field names to their Bugzilla::Comment accessor. -use constant COMMENT_FIELDS => { - longdesc => 'body', - commenter => 'author', - 'longdescs.isprivate' => 'is_private', -}; - -# Same as above, for Bugzilla::Attachment. -use constant ATTACHMENT_FIELDS => { - mimetype => 'contenttype', - submitter => 'attacher', - thedata => 'data', -}; - -# Same, for Bugzilla::Flag. -use constant FLAG_FIELDS => { - 'flagtypes.name' => 'name', - 'setters.login_name' => 'setter', - 'requestees.login_name' => 'requestee', -}; - -# These are fields that we don't test. Test::More will mark these -# "TODO & SKIP", and not run tests for them at all. -# -# We don't support days_elapsed or owner_idle_time yet. -use constant SKIP_FIELDS => qw( - owner_idle_time - days_elapsed -); - -# All the fields that represent users. -use constant USER_FIELDS => qw( - assigned_to - cc - reporter - qa_contact - commenter - attachments.submitter - setters.login_name - requestees.login_name -); - -# For the "substr"-type searches, how short of a substring should -# we use? The goal is to be shorter than the full string, but -# long enough to still be globally unique. -use constant SUBSTR_SIZE => 20; -# However, for some fields, we use a different size. -use constant FIELD_SUBSTR_SIZE => { - alias => 11, - # Just the month and day. - deadline => -5, - creation_ts => -8, - delta_ts => -8, - percentage_complete => 1, - work_time => 3, - remaining_time => 3, - target_milestone => 15, - longdesc => 25, - # Just the hour and minute. - FIELD_TYPE_DATETIME, -5, -}; - -# For most fields, we add the length of the name of the field plus -# the SUBSTR_SIZE specified above to determine how large of a substring -# we're going to use. However, for some fields, it doesn't make sense to -# add in their field name this way. -use constant SUBSTR_NO_FIELD_ADD => FIELD_TYPE_DATETIME, qw( - target_milestone remaining_time percentage_complete work_time - attachments.mimetype attachments.submitter attachments.filename - attachments.description flagtypes.name -); - -################ -# Known Broken # -################ - -# See the KNOWN_BROKEN constant for a general description of these -# "_BROKEN" constants. - -# Shared between greaterthan and greaterthaneq. -# -# As with other fields, longdescs greaterthan matches if any comment -# matches (which might be OK). -# -# Same for keywords, and cc. Logically, all of these might -# be OK, but it makes the operation not the logical reverse of -# lessthaneq. What we're really saying here by marking these broken -# is that there ought to be some way of searching "all ccs" vs "any cc" -# (and same for the other fields). -use constant GREATERTHAN_BROKEN => ( - cc => { contains => [1] }, -); - -# allwords and allwordssubstr have these broken tests in common. -use constant ALLWORDS_BROKEN => ( - # allwordssubstr on cc fields matches against a single cc, - # instead of matching against all ccs on a bug. - cc => { contains => [1] }, - # bug 828344 changed how these searches operate to revert back to the 4.0 - # behavour, so these tests need to be updated (bug 849117). - 'flagtypes.name' => { contains => [1] }, - longdesc => { contains => [1] }, -); - -# Fields that don't generally work at all with changed* searches, but -# probably should. -use constant CHANGED_BROKEN => ( - classification => { contains => [1] }, - commenter => { contains => [1] }, - percentage_complete => { contains => [1] }, - 'requestees.login_name' => { contains => [1] }, - 'setters.login_name' => { contains => [1] }, - delta_ts => { contains => [1] }, -); - -# These are additional broken tests that changedfrom and changedto -# have in common. -use constant CHANGED_VALUE_BROKEN => ( - bug_group => { contains => [1] }, - cc => { contains => [1] }, - estimated_time => { contains => [1] }, - 'flagtypes.name' => { contains => [1] }, - keywords => { contains => [1] }, - 'longdescs.count' => { search => 1 }, - FIELD_TYPE_MULTI_SELECT, { contains => [1] }, -); - - -# Any test listed in KNOWN_BROKEN gets marked TODO by Test::More -# (using some complex code in Bugzilla::Test::Seach::FieldTest). -# This means that if you run the test under "prove -v", these tests will -# still show up as "not ok", but the test suite results won't show them -# as a failure. -# -# This constant contains operators as keys, which point to hashes. The hashes -# have field names as keys. Each field name points to a hash describing -# how that field/operator combination is broken. The "contains" -# array specifies that that particular "contains" test is expected -# to fail. If "search" is set to 1, then we expect the creation of the -# Bugzilla::Search object to fail. -# -# To allow handling custom fields, you can also use the field type as a key -# instead of the field name. Specifying explicit field names always overrides -# specifying a field type. -# -# Sometimes the operators have multiple tests, and one of them works -# while the other fails. In this case, we have a special override for -# "operator-value", which uniquely identifies tests. -use constant KNOWN_BROKEN => { - greaterthan => { GREATERTHAN_BROKEN }, - greaterthaneq => { GREATERTHAN_BROKEN }, - - 'allwordssubstr-<1>' => { ALLWORDS_BROKEN }, - 'allwords-<1>' => { - ALLWORDS_BROKEN, - }, - 'anywords-<1>' => { - 'flagtypes.name' => { contains => [1,2,3,4,5] }, - }, - 'anywords-<1> <2>' => { - 'flagtypes.name' => { contains => [3,4,5] }, - }, - 'anywordssubstr-<1> <2>' => { - 'flagtypes.name' => { contains => [3,4,5] }, - }, - - # setters.login_name and requestees.login name aren't tracked individually - # in bugs_activity, so can't be searched using this method. - # - # percentage_complete isn't tracked in bugs_activity (and it would be - # really hard to track). However, it adds a 0=0 term instead of using - # the changed* charts or simply denying them. - # - # delta_ts changedbefore/after should probably search for bugs based - # on their delta_ts. - # - # creation_ts changedbefore/after should search for bug creation dates. - # - # The commenter field changedbefore/after should search for comment - # creation dates. - # - # classification isn't being tracked properly in bugs_activity, I think. - # - # attach_data.thedata should search when attachments were created and - # who they were created by. - 'changedbefore' => { - CHANGED_BROKEN, - 'attach_data.thedata' => { contains => [1] }, - }, - 'changedafter' => { - 'attach_data.thedata' => { contains => [2,3,4] }, - classification => { contains => [2,3,4] }, - commenter => { contains => [2,3,4] }, - delta_ts => { contains => [2,3,4] }, - percentage_complete => { contains => [2,3,4] }, - 'requestees.login_name' => { contains => [2,3,4] }, - 'setters.login_name' => { contains => [2,3,4] }, - }, - changedfrom => { - CHANGED_BROKEN, - CHANGED_VALUE_BROKEN, - # All fields should have a way to search for "changing - # from a blank value" probably. - blocked => { contains => [3,4,5], no_criteria => 1 }, - dependson => { contains => [2,4,5], no_criteria => 1 }, - work_time => { contains => [1] }, - FIELD_TYPE_BUG_ID, { contains => [5], no_criteria => 1 }, - }, - # changeto doesn't find remaining_time changes (possibly due to us not - # tracking that data properly). - # - # multi-valued fields are stored as comma-separated strings, so you - # can't do changedfrom/to on them. - # - # Perhaps commenter can either tell you who the last commenter was, - # or if somebody commented at a given time (combined with other - # charts). - # - # longdesc changedto/from doesn't do anything; maybe it should. - # Same for attach_data.thedata. - changedto => { - CHANGED_BROKEN, - CHANGED_VALUE_BROKEN, - 'attach_data.thedata' => { contains => [1] }, - longdesc => { contains => [1] }, - remaining_time => { contains => [1] }, - }, - changedby => { - CHANGED_BROKEN, - # This should probably search the attacher or anybody who changed - # anything about an attachment at all. - 'attach_data.thedata' => { contains => [1] }, - # This should probably search the reporter. - creation_ts => { contains => [1] }, - }, - notequals => { - 'flagtypes.name' => { contains => [1, 5] }, - longdesc => { contains => [1] }, - }, - notregexp => { - 'flagtypes.name' => { contains => [1, 5] }, - longdesc => { contains => [1] }, - }, - notsubstring => { - 'flagtypes.name' => { contains => [5] }, - longdesc => { contains => [1] }, - }, - nowords => { - 'flagtypes.name' => { contains => [1, 5] }, - }, - nowordssubstr => { - 'flagtypes.name' => { contains => [5] }, - }, -}; - -################### -# Broken NotTests # -################### - -# Common BROKEN_NOT values for the changed* fields. -use constant CHANGED_BROKEN_NOT => ( - "attach_data.thedata" => { contains => [1] }, - "classification" => { contains => [1] }, - "commenter" => { contains => [1] }, - "delta_ts" => { contains => [1] }, - percentage_complete => { contains => [1] }, - "requestees.login_name" => { contains => [1] }, - "setters.login_name" => { contains => [1] }, -); - -# For changedfrom and changedto. -use constant CHANGED_FROM_TO_BROKEN_NOT => ( - 'longdescs.count' => { search => 1 }, - "bug_group" => { contains => [1] }, - "cc" => { contains => [1] }, - "estimated_time" => { contains => [1] }, - "flagtypes.name" => { contains => [1] }, - "keywords" => { contains => [1] }, - FIELD_TYPE_MULTI_SELECT, { contains => [1] }, -); - -# These are field/operator combinations that are broken when run under NOT(). -use constant BROKEN_NOT => { - allwords => { - cc => { contains => [1] }, - 'flagtypes.name' => { contains => [1, 5] }, - longdesc => { contains => [1] }, - }, - 'allwords-<1> <2>' => { - cc => { }, - }, - allwordssubstr => { - cc => { contains => [1] }, - 'flagtypes.name' => { contains => [5, 6] }, - longdesc => { contains => [1] }, - }, - 'allwordssubstr-<1>,<2>' => { - cc => { }, - longdesc => { contains => [1] }, - }, - anyexact => { - 'flagtypes.name' => { contains => [1, 2, 5] }, - }, - 'anywords-<1>' => { - 'flagtypes.name' => { contains => [1, 2, 3, 4, 5] }, - }, - 'anywords-<1> <2>' => { - 'flagtypes.name' => { contains => [3, 4, 5] }, - }, - anywordssubstr => { - 'flagtypes.name' => { contains => [5] }, - }, - 'anywordssubstr-<1> <2>' => { - 'flagtypes.name' => { contains => [3,4,5] }, - }, - casesubstring => { - 'flagtypes.name' => { contains => [5] }, - }, - changedafter => { - "attach_data.thedata" => { contains => [2, 3, 4] }, - "classification" => { contains => [2, 3, 4] }, - "commenter" => { contains => [2, 3, 4] }, - percentage_complete => { contains => [2, 3, 4] }, - "delta_ts" => { contains => [2, 3, 4] }, - "requestees.login_name" => { contains => [2, 3, 4] }, - "setters.login_name" => { contains => [2, 3, 4] }, - }, - changedbefore => { - CHANGED_BROKEN_NOT, - }, - changedby => { - CHANGED_BROKEN_NOT, - creation_ts => { contains => [1] }, - work_time => { contains => [1] }, - }, - changedfrom => { - CHANGED_BROKEN_NOT, - CHANGED_FROM_TO_BROKEN_NOT, - 'attach_data.thedata' => { }, - blocked => { contains => [1, 2] }, - dependson => { contains => [1, 3] }, - work_time => { contains => [1] }, - FIELD_TYPE_BUG_ID, { contains => [1 .. 4] }, - }, - changedto => { - CHANGED_BROKEN_NOT, - CHANGED_FROM_TO_BROKEN_NOT, - longdesc => { contains => [1] }, - "remaining_time" => { contains => [1] }, - }, - greaterthan => { - cc => { contains => [1] }, - 'flagtypes.name' => { contains => [5] }, - }, - greaterthaneq => { - cc => { contains => [1] }, - 'flagtypes.name' => { contains => [2, 5] }, - }, - equals => { - 'flagtypes.name' => { contains => [1, 5] }, - }, - notequals => { - longdesc => { contains => [1] }, - }, - notregexp => { - longdesc => { contains => [1] }, - }, - notsubstring => { - longdesc => { contains => [1] }, - }, - 'nowords-<1>' => { - 'flagtypes.name' => { contains => [5] }, - }, - 'nowordssubstr-<1>' => { - 'flagtypes.name' => { contains => [5] }, - }, - lessthan => { - 'flagtypes.name' => { contains => [5] }, - }, - lessthaneq => { - 'flagtypes.name' => { contains => [1, 5] }, - }, - regexp => { - 'flagtypes.name' => { contains => [1, 5] }, - longdesc => { contains => [1] }, - }, - substring => { - 'flagtypes.name' => { contains => [5] }, - longdesc => { contains => [1] }, - }, -}; - -############# -# Overrides # -############# - -# These overrides are used in the TESTS constant, below. - -# Regex tests need unique test values for certain fields. -use constant REGEX_OVERRIDE => { - 'attachments.mimetype' => { value => '^text/x-1-' }, - bug_file_loc => { value => '^http://1-' }, - see_also => { value => '^http://1-' }, - blocked => { value => '^<1>$' }, - dependson => { value => '^<1>$' }, - bug_id => { value => '^<1>$' }, - 'attachments.isobsolete' => { value => '^1'}, - 'attachments.ispatch' => { value => '^1'}, - 'attachments.isprivate' => { value => '^1' }, - cclist_accessible => { value => '^1' }, - reporter_accessible => { value => '^1' }, - everconfirmed => { value => '^1' }, - 'longdescs.count' => { value => '^3' }, - 'longdescs.isprivate' => { value => '^1' }, - creation_ts => { value => '^2037-01-01' }, - delta_ts => { value => '^2037-01-01' }, - deadline => { value => '^2037-02-01' }, - estimated_time => { value => '^1.0' }, - remaining_time => { value => '^9.0' }, - work_time => { value => '^1.0' }, - longdesc => { value => '^1-' }, - percentage_complete => { value => '^10' }, - FIELD_TYPE_BUG_ID, { value => '^<1>$' }, - FIELD_TYPE_DATETIME, { value => '^2037-03-01' } -}; - -# Common overrides between lessthan and lessthaneq. -use constant LESSTHAN_OVERRIDE => ( - alias => { contains => [1,5] }, - estimated_time => { contains => [1,5] }, - qa_contact => { contains => [1,5] }, - resolution => { contains => [1,5] }, - status_whiteboard => { contains => [1,5] }, - FIELD_TYPE_TEXTAREA, { contains => [1,5] }, - FIELD_TYPE_FREETEXT, { contains => [1,5] }, -); - -# The mandatorily-set fields have values higher than <1>, -# so bug 5 shows up. -use constant GREATERTHAN_OVERRIDE => ( - classification => { contains => [2,3,4,5] }, - assigned_to => { contains => [2,3,4,5] }, - bug_id => { contains => [2,3,4,5] }, - bug_group => { contains => [1,2,3,4] }, - bug_severity => { contains => [2,3,4,5] }, - bug_status => { contains => [2,3,4,5] }, - component => { contains => [2,3,4,5] }, - commenter => { contains => [2,3,4,5] }, - # keywords matches if *any* keyword matches - keywords => { contains => [1,2,3,4] }, - longdesc => { contains => [1,2,3,4] }, - op_sys => { contains => [2,3,4,5] }, - priority => { contains => [2,3,4,5] }, - product => { contains => [2,3,4,5] }, - reporter => { contains => [2,3,4,5] }, - rep_platform => { contains => [2,3,4,5] }, - short_desc => { contains => [2,3,4,5] }, - version => { contains => [2,3,4,5] }, - tag => { contains => [1,2,3,4] }, - target_milestone => { contains => [2,3,4,5] }, - # Bug 2 is the only bug besides 1 that has a Requestee set. - 'requestees.login_name' => { contains => [2] }, - FIELD_TYPE_SINGLE_SELECT, { contains => [2,3,4,5] }, - # Override SINGLE_SELECT for resolution. - resolution => { contains => [2,3,4] }, - # MULTI_SELECTs match if *any* value matches - FIELD_TYPE_MULTI_SELECT, { contains => [1,2,3,4] }, -); - -# For all positive multi-value types. -use constant MULTI_BOOLEAN_OVERRIDE => ( - 'attachments.ispatch' => { value => '1,1', contains => [1] }, - 'attachments.isobsolete' => { value => '1,1', contains => [1] }, - 'attachments.isprivate' => { value => '1,1', contains => [1] }, - cclist_accessible => { value => '1,1', contains => [1] }, - reporter_accessible => { value => '1,1', contains => [1] }, - 'longdescs.isprivate' => { value => '1,1', contains => [1] }, - everconfirmed => { value => '1,1', contains => [1] }, -); - -# Same as above, for negative multi-value types. -use constant NEGATIVE_MULTI_BOOLEAN_OVERRIDE => ( - 'attachments.ispatch' => { value => '1,1', contains => [2,3,4,5] }, - 'attachments.isobsolete' => { value => '1,1', contains => [2,3,4,5] }, - 'attachments.isprivate' => { value => '1,1', contains => [2,3,4,5] }, - cclist_accessible => { value => '1,1', contains => [2,3,4,5] }, - reporter_accessible => { value => '1,1', contains => [2,3,4,5] }, - 'longdescs.isprivate' => { value => '1,1', contains => [2,3,4,5] }, - everconfirmed => { value => '1,1', contains => [2,3,4,5] }, -); - -# For anyexact and anywordssubstr -use constant ANY_OVERRIDE => ( - 'longdescs.count' => { contains => [1,2,3,4] }, - 'work_time' => { value => '1.0,2.0' }, - dependson => { value => '<1>,<3>', contains => [1,3] }, - MULTI_BOOLEAN_OVERRIDE, -); - -# For all the changed* searches. The ones that have empty contains -# are fields that never change in value, or will never be rationally -# tracked in bugs_activity. -use constant CHANGED_OVERRIDE => ( - 'attachments.submitter' => { contains => [] }, - bug_id => { contains => [] }, - reporter => { contains => [] }, - tag => { contains => [] }, -); - -######### -# Tests # -######### - -# The basic format of this is a hashref, where the keys are operators, -# and each operator has an arrayref of tests that it runs. The tests -# are hashrefs, with the following possible keys: -# -# contains: This is a list of bug numbers that the search is expected -# to contain. (This is bug numbers, like 1,2,3, not the bug -# ids. For a description of each bug number, see NUM_BUGS.) -# Any bug not listed in "contains" must *not* show up in the -# search result. -# value: The value that you're searching for. There are certain special -# codes that will be replaced with bug values when the tests are -# run. In these examples below, "#" indicates a bug number: -# -# <#> - The field value for this bug. -# -# For any operator that has the string "word" in it, this is -# *all* the values for the current field from the numbered bug, -# joined by a space. -# -# If the operator has the string "substr" in it, then we -# take a substring of the value (for single-value searches) -# or we take a substring of each value and join them (for -# multi-value "word" searches). The length of the substring -# is determined by the SUBSTR_SIZE constants above.) -# -# For other operators, this just becomes the first value from -# the field for the numbered bug. -# -# So, if we were running the "equals" test and checking the -# cc field, <1> would become the login name of the first cc on -# Bug 1. If we did an "anywords" search test, it would become -# a space-separated string of the login names of all the ccs -# on Bug 1. If we did an "anywordssubstr" search test, it would -# become a space-separated string of the first few characters -# of each CC's login name on Bug 1. -# -# <#-id> - The bug id of the numbered bug. -# <#-reporter> - The login name of the numbered bug's reporter. -# <#-delta> - The delta_ts of the numbered bug. -# -# escape: If true, we will call quotemeta() on the value immediately -# before passing it to Search.pm. -# -# transform: A function to call on any field value before inserting -# it for a <#> replacement. The transformation function -# gets all of the bug's values for the field as its arguments. -# if_equal: This allows you to override "contains" for the case where -# the transformed value (from calling the "transform" function) -# is equal to the original value. -# -# override: This allows you to override "contains" and "values" for -# certain fields. -use constant TESTS => { - equals => [ - { contains => [1], value => '<1>' }, - ], - notequals => [ - { contains => [2,3,4,5], value => '<1>' }, - ], - substring => [ - { contains => [1], value => '<1>', - override => { - percentage_complete => { contains => [1,2,3] }, - } - }, - ], - casesubstring => [ - { contains => [1], value => '<1>', - override => { - percentage_complete => { contains => [1,2,3] }, - } - }, - { contains => [], value => '<1>', transform => sub { lc($_[0]) }, - extra_name => 'lc', if_equal => { contains => [1] }, - override => { - percentage_complete => { contains => [1,2,3] }, - } - }, - ], - notsubstring => [ - { contains => [2,3,4,5], value => '<1>', - override => { - percentage_complete => { contains => [4,5] }, - }, - } - ], - regexp => [ - { contains => [1], value => '<1>', escape => 1, - override => { - percentage_complete => { value => '^10' }, - } - }, - { contains => [1], value => '^1-', override => REGEX_OVERRIDE }, - ], - notregexp => [ - { contains => [2,3,4,5], value => '<1>', escape => 1, - override => { - percentage_complete => { value => '^10' }, - } - }, - { contains => [2,3,4,5], value => '^1-', override => REGEX_OVERRIDE }, - ], - lessthan => [ - { contains => [1], value => 2, - override => { - # A lot of these contain bug 5 because an empty value is validly - # less than the specified value. - bug_file_loc => { value => 'http://2-', contains => [1,5] }, - see_also => { value => 'http://2-' }, - 'attachments.mimetype' => { value => 'text/x-2-' }, - blocked => { value => '<4-id>', contains => [1,2] }, - dependson => { value => '<3-id>', contains => [1,3] }, - bug_id => { value => '<2-id>' }, - 'attachments.isprivate' => { value => 1, contains => [2,3,4] }, - 'attachments.isobsolete' => { value => 1, contains => [2,3,4] }, - 'attachments.ispatch' => { value => 1, contains => [2,3,4] }, - cclist_accessible => { value => 1, contains => [2,3,4,5] }, - reporter_accessible => { value => 1, contains => [2,3,4,5] }, - 'longdescs.count' => { value => 3, contains => [2,3,4,5] }, - 'longdescs.isprivate' => { value => 1, contains => [1,2,3,4,5] }, - everconfirmed => { value => 1, contains => [2,3,4,5] }, - creation_ts => { value => '2037-01-02', contains => [1,5] }, - delta_ts => { value => '2037-01-02', contains => [1,5] }, - deadline => { value => '2037-02-02', contains => [1,5] }, - remaining_time => { value => 10, contains => [1,5] }, - percentage_complete => { value => 11, contains => [1,5] }, - longdesc => { value => '2-', contains => [1,5] }, - work_time => { value => 1, contains => [5] }, - FIELD_TYPE_BUG_ID, { value => '<2>', contains => [1,5] }, - FIELD_TYPE_DATETIME, { value => '2037-03-02', contains => [1,5] }, - LESSTHAN_OVERRIDE, - } - }, - ], - lessthaneq => [ - { contains => [1], value => '<1>', - override => { - 'attachments.isobsolete' => { value => 0, contains => [2,3,4] }, - 'attachments.ispatch' => { value => 0, contains => [2,3,4] }, - 'attachments.isprivate' => { value => 0, contains => [2,3,4] }, - cclist_accessible => { value => 0, contains => [2,3,4,5] }, - reporter_accessible => { value => 0, contains => [2,3,4,5] }, - 'longdescs.count' => { value => 2, contains => [2,3,4,5] }, - 'longdescs.isprivate' => { value => -1, contains => [] }, - everconfirmed => { value => 0, contains => [2,3,4,5] }, - bug_file_loc => { contains => [1,5] }, - blocked => { contains => [1,2] }, - deadline => { contains => [1,5] }, - dependson => { contains => [1,3] }, - creation_ts => { contains => [1,5] }, - delta_ts => { contains => [1,5] }, - remaining_time => { contains => [1,5] }, - longdesc => { contains => [1,5] }, - percentage_complete => { contains => [1,5] }, - work_time => { value => 1, contains => [1,5] }, - FIELD_TYPE_BUG_ID, { contains => [1,5] }, - FIELD_TYPE_DATETIME, { contains => [1,5] }, - LESSTHAN_OVERRIDE, - }, - }, - ], - greaterthan => [ - { contains => [2,3,4], value => '<1>', - override => { - dependson => { contains => [3] }, - blocked => { contains => [2] }, - 'attachments.ispatch' => { value => 0, contains => [1] }, - 'attachments.isobsolete' => { value => 0, contains => [1] }, - 'attachments.isprivate' => { value => 0, contains => [1] }, - cclist_accessible => { value => 0, contains => [1] }, - reporter_accessible => { value => 0, contains => [1] }, - 'longdescs.count' => { value => 2, contains => [1] }, - 'longdescs.isprivate' => { value => 0, contains => [1] }, - everconfirmed => { value => 0, contains => [1] }, - 'flagtypes.name' => { value => 2, contains => [2,3,4] }, - GREATERTHAN_OVERRIDE, - }, - }, - ], - greaterthaneq => [ - { contains => [2,3,4], value => '<2>', - override => { - 'attachments.ispatch' => { value => 1, contains => [1] }, - 'attachments.isobsolete' => { value => 1, contains => [1] }, - 'attachments.isprivate' => { value => 1, contains => [1] }, - cclist_accessible => { value => 1, contains => [1] }, - reporter_accessible => { value => 1, contains => [1] }, - 'longdescs.count' => { value => 3, contains => [1] }, - 'longdescs.isprivate' => { value => 1, contains => [1] }, - everconfirmed => { value => 1, contains => [1] }, - dependson => { value => '<3>', contains => [1,3] }, - blocked => { contains => [1,2] }, - GREATERTHAN_OVERRIDE, - } - }, - ], - matches => [ - { contains => [1], value => '<1>' }, - ], - notmatches => [ - { contains => [2,3,4,5], value => '<1>' }, - ], - anyexact => [ - { contains => [1,2], value => '<1>, <2>', - override => { ANY_OVERRIDE } }, - ], - anywordssubstr => [ - { contains => [1,2], value => '<1> <2>', - override => { - ANY_OVERRIDE, - percentage_complete => { contains => [1,2,3] }, - } - }, - ], - allwordssubstr => [ - { contains => [1], value => '<1>', - override => { - MULTI_BOOLEAN_OVERRIDE, - # We search just the number "1" for percentage_complete, - # which matches a lot of bugs. - percentage_complete => { contains => [1,2,3] }, - }, - }, - { contains => [], value => '<1>,<2>', - override => { - dependson => { value => '<1-id> <3-id>', contains => [] }, - # bug 3 has the value "21" here, so matches "2,1" - percentage_complete => { value => '<2>,<3>', contains => [3] }, - # 1 0 matches bug 1, which has both public and private comments. - 'longdescs.isprivate' => { contains => [1] }, - } - }, - ], - nowordssubstr => [ - { contains => [2,3,4,5], value => '<1>', - override => { - # longdescs.isprivate translates to "1 0", so no bugs should - # show up. - 'longdescs.isprivate' => { contains => [] }, - percentage_complete => { contains => [4,5] }, - work_time => { contains => [2,3,4,5] }, - } - }, - ], - anywords => [ - { contains => [1], value => '<1>', - override => { - MULTI_BOOLEAN_OVERRIDE, - } - }, - { contains => [1,2], value => '<1> <2>', - override => { - MULTI_BOOLEAN_OVERRIDE, - dependson => { value => '<1> <3>', contains => [1,3] }, - 'longdescs.count' => { contains => [1,2,3,4] }, - }, - }, - ], - allwords => [ - { contains => [1], value => '<1>', - override => { MULTI_BOOLEAN_OVERRIDE } }, - { contains => [], value => '<1> <2>', - override => { - dependson => { contains => [], value => '<2-id> <3-id>' }, - # 1 0 matches bug 1, which has both public and private comments. - 'longdescs.isprivate' => { contains => [1] }, - } - }, - ], - nowords => [ - { contains => [2,3,4,5], value => '<1>', - override => { - # longdescs.isprivate translates to "1 0", so no bugs should - # show up. - 'longdescs.isprivate' => { contains => [] }, - work_time => { contains => [2,3,4,5] }, - } - }, - ], - - changedbefore => [ - { contains => [1], value => '<1-delta>', - override => { - CHANGED_OVERRIDE, - creation_ts => { contains => [1,5] }, - blocked => { contains => [1,2] }, - dependson => { contains => [1,3] }, - longdesc => { contains => [1,5] }, - 'longdescs.count' => { contains => [1,5] }, - } - }, - ], - changedafter => [ - { contains => [2,3,4], value => '<2-delta>', - override => { - CHANGED_OVERRIDE, - creation_ts => { contains => [3,4] }, - # We only change this for one bug, and it doesn't match. - 'longdescs.isprivate' => { contains => [] }, - # Same for everconfirmed. - 'everconfirmed' => { contains => [] }, - # For blocked and dependson, they have the delta_ts of bug1 - # in the bugs_activity table, so they won't ever match. - blocked => { contains => [] }, - dependson => { contains => [] }, - } - }, - ], - changedfrom => [ - { contains => [1], value => '<1>', - override => { - CHANGED_OVERRIDE, - # The test never changes an already-set dependency field, but - # we *can* attempt to test searching against an empty value, - # which should get us some bugs. - blocked => { value => '', contains => [1,2] }, - dependson => { value => '', contains => [1,3] }, - FIELD_TYPE_BUG_ID, { value => '', contains => [1,2,3,4] }, - # longdesc changedfrom doesn't make any sense. - longdesc => { contains => [] }, - # Nor does creation_ts changedfrom. - creation_ts => { contains => [] }, - 'attach_data.thedata' => { contains => [] }, - bug_id => { value => '<1-id>', contains => [] }, - }, - }, - ], - changedto => [ - { contains => [1], value => '<1>', - override => { - CHANGED_OVERRIDE, - # I can't imagine any use for creation_ts changedto. - creation_ts => { contains => [] }, - } - }, - ], - changedby => [ - { contains => [1], value => '<1-reporter>', - override => { - CHANGED_OVERRIDE, - blocked => { contains => [1,2] }, - dependson => { contains => [1,3] }, - }, - }, - ], - # XXX these need tests developed - isempty => [], - isnotempty => [], -}; - -# Fields that do not behave as we expect, for InjectionTest. -# search => 1 means the Bugzilla::Search creation fails. -# sql_error is a regex that specifies a SQL error that's OK for us to throw. -# operator_ok overrides the "brokenness" of certain operators, so that they -# are always OK for that field/operator combination. -use constant INJECTION_BROKEN_FIELD => { - # Pg can't run injection tests against integer or date fields. See bug 577557. - 'attachments.isobsolete' => { db_skip => ['Pg'] }, - 'attachments.ispatch' => { db_skip => ['Pg'] }, - 'attachments.isprivate' => { db_skip => ['Pg'] }, - blocked => { db_skip => ['Pg'] }, - bug_id => { db_skip => ['Pg'] }, - cclist_accessible => { db_skip => ['Pg'] }, - creation_ts => { db_skip => ['Pg'] }, - days_elapsed => { db_skip => ['Pg'] }, - dependson => { db_skip => ['Pg'] }, - deadline => { db_skip => ['Pg'] }, - delta_ts => { db_skip => ['Pg'] }, - estimated_time => { db_skip => ['Pg'] }, - everconfirmed => { db_skip => ['Pg'] }, - 'longdescs.isprivate' => { db_skip => ['Pg'] }, - percentage_complete => { db_skip => ['Pg'] }, - remaining_time => { db_skip => ['Pg'] }, - reporter_accessible => { db_skip => ['Pg'] }, - work_time => { db_skip => ['Pg'] }, - FIELD_TYPE_BUG_ID, { db_skip => ['Pg'] }, - FIELD_TYPE_DATETIME, { db_skip => ['Pg'] }, - owner_idle_time => { search => 1 }, - 'longdescs.count' => { - search => 1, - db_skip => ['Pg'], - operator_ok => [qw(allwords allwordssubstr anywordssubstr casesubstring - changedbefore changedafter greaterthan greaterthaneq - lessthan lessthaneq notregexp notsubstring - nowordssubstr regexp substring anywords - notequals nowords equals anyexact)], - }, -}; - -# Operators that do not behave as we expect, for InjectionTest. -# search => 1 means the Bugzilla::Search creation fails, but -# field_ok contains fields that it does actually succeed for. -use constant INJECTION_BROKEN_OPERATOR => { - changedafter => { search => 1, field_ok => ['creation_ts'] }, - changedbefore => { search => 1, field_ok => ['creation_ts'] }, - changedby => { search => 1 }, - isempty => { search => 1 }, - isnotempty => { search => 1 }, -}; - -# Tests run by Bugzilla::Test::Search::InjectionTest. -# We have to make sure the values are all one word or they'll be split -# up by the multi-word tests. -use constant INJECTION_TESTS => ( - { value => ';SEMICOLON_TEST' }, - { value => '--COMMENT_TEST' }, - { value => "'QUOTE_TEST" }, - { value => "';QUOTE_SEMICOLON_TEST" }, - { value => '/*STAR_COMMENT_TEST' } -); - -################# -# Special Tests # -################# - -use constant SPECIAL_PARAM_TESTS => ( - { field => 'bug_status', operator => 'anyexact', value => '__open__', - contains => [5] }, - { field => 'bug_status', operator => 'anyexact', value => '__closed__', - contains => [1,2,3,4] }, - { field => 'bug_status', operator => 'anyexact', value => '__all__', - contains => [1,2,3,4,5] }, - - { field => 'resolution', operator => 'anyexact', value => '---', - contains => [5] }, - - # email* query parameters. - { field => 'assigned_to', operator => 'anyexact', - value => '<1>, <2-reporter>', contains => [1,2], - extra_params => { emailreporter1 => 1 } }, - { field => 'assigned_to', operator => 'equals', - value => '<1>', extra_name => 'email2', contains => [], - extra_params => { - email2 => generate_random_password(100), emaillongdesc2 => 1, - }, - }, - - # standard pronouns - { field => 'assigned_to', operator => 'equals', value => '%assignee%', - contains => [1,2,3,4,5] }, - { field => 'reporter', operator => 'equals', value => '%reporter%', - contains => [1,2,3,4,5] }, - { field => 'qa_contact', operator => 'equals', value => '%qacontact%', - contains => [1,2,3,4,5] }, - { field => 'cc', operator => 'equals', value => '%user%', - contains => [1] }, - # group pronouns - { field => 'reporter', operator => 'equals', - value => '%group.<1-bug_group>%', contains => [1,2,3,4,5] }, - { field => 'assigned_to', operator => 'equals', - value => '%group.<1-bug_group>%', contains => [1,2,3,4,5] }, - { field => 'qa_contact', operator => 'equals', - value => '%group.<1-bug_group>%', contains => [1,2,3,4] }, - { field => 'cc', operator => 'equals', - value => '%group.<1-bug_group>%', contains => [1,2,3,4] }, - { field => 'commenter', operator => 'equals', - value => '%group.<1-bug_group>%', contains => [1,2,3,4,5] }, -); - -use constant CUSTOM_SEARCH_TESTS => ( - { name => 'OP without CP', contains => [1], - params => [ - { f => 'OP' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - ] - }, - - { name => 'Empty OP/CP pair before criteria', contains => [1], - params => [ - { f => 'OP' }, { f => 'CP' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - ] - }, - - { name => 'Empty OP/CP pair after criteria', contains => [1], - params => [ - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'OP' }, { f => 'CP' }, - ] - }, - - { name => 'empty OP/CP mid criteria', contains => [1], - columns => ['assigned_to'], - params => [ - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'OP' }, { f => 'CP' }, - { f => 'assigned_to', o => 'substr', v => '@' }, - ] - }, - - { name => 'bug_id = 1 AND assigned_to contains @', contains => [1], - columns => ['assigned_to'], - params => [ - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'substr', v => '@' }, - ] - }, - - { name => 'NOT(bug_id = 1) AND NOT(assigned_to = 2)', - contains => [3,4,5], - columns => ['assigned_to'], - params => [ - { n => 1, f => 'bug_id', o => 'equals', v => '<1>' }, - { n => 1, f => 'assigned_to', o => 'equals', v => '<2>' }, - ] - }, - - { name => 'bug_id = 1 OR assigned_to = 2', contains => [1,2], - columns => ['assigned_to'], top_params => { j_top => 'OR' }, - params => [ - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'equals', v => '<2>' }, - ] - }, - - { name => 'NOT(bug_id = 1 AND assigned_to = 1)', contains => [2,3,4,5], - columns => ['assigned_to'], - params => [ - { f => 'OP', n => 1 }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'equals', v => '<1>' }, - { f => 'CP' }, - ] - }, - - - { name => '(bug_id = 1 AND assigned_to contains @) ' - . ' OR (bug_id = 2 AND assigned_to contains @)', - contains => [1,2], columns => ['assigned_to'], - top_params => { j_top => 'OR' }, - params => [ - { f => 'OP' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'substr', v => '@' }, - { f => 'CP' }, - { f => 'OP' }, - { f => 'bug_id', o => 'equals', v => '<2>' }, - { f => 'assigned_to', o => 'substr', v => '@' }, - { f => 'CP' }, - ] - }, - - { name => '(bug_id = 1 OR assigned_to = 2) ' - . ' AND (bug_id = 2 OR assigned_to = 1)', - contains => [1,2], columns => ['assigned_to'], - params => [ - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'equals', v => '<2>' }, - { f => 'CP' }, - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<2>' }, - { f => 'assigned_to', o => 'equals', v => '<1>' }, - { f => 'CP' }, - ] - }, - - { name => 'bug_id = 3 OR ( (bug_id = 1 OR assigned_to = 2) ' - . ' AND (bug_id = 2 OR assigned_to = 1) )', - contains => [1,2,3], columns => ['assigned_to'], - top_params => { j_top => 'OR' }, - params => [ - { f => 'bug_id', o => 'equals', v => '<3>' }, - { f => 'OP' }, - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'equals', v => '<2>' }, - { f => 'CP' }, - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<2>' }, - { f => 'assigned_to', o => 'equals', v => '<1>' }, - { f => 'CP' }, - { f => 'CP' }, - ] - }, - - { name => 'bug_id = 3 OR ( (bug_id = 1 OR assigned_to = 2) ' - . ' AND (bug_id = 2 OR assigned_to = 1) ) OR bug_id = 4', - contains => [1,2,3,4], columns => ['assigned_to'], - top_params => { j_top => 'OR' }, - params => [ - { f => 'bug_id', o => 'equals', v => '<3>' }, - { f => 'OP' }, - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<1>' }, - { f => 'assigned_to', o => 'equals', v => '<2>' }, - { f => 'CP' }, - { f => 'OP', j => 'OR' }, - { f => 'bug_id', o => 'equals', v => '<2>' }, - { f => 'assigned_to', o => 'equals', v => '<1>' }, - { f => 'CP' }, - { f => 'CP' }, - { f => 'bug_id', o => 'equals', v => '<4>' }, - ] - }, - -); - -1; diff --git a/xt/lib/Bugzilla/Test/Search/CustomTest.pm b/xt/lib/Bugzilla/Test/Search/CustomTest.pm deleted file mode 100644 index 132e5ac40..000000000 --- a/xt/lib/Bugzilla/Test/Search/CustomTest.pm +++ /dev/null @@ -1,101 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This module represents a test with custom URL parameters. -# Tests like this are specified in CUSTOM_SEARCH_TESTS in -# Bugzilla::Test::Search::Constants. -package Bugzilla::Test::Search::CustomTest; -use parent qw(Bugzilla::Test::Search::FieldTest); -use strict; -use warnings; - -use Bugzilla::Test::Search::FieldTest; -use Bugzilla::Test::Search::OperatorTest; - -use Storable qw(dclone); - -############### -# Constructor # -############### - -sub new { - my ($class, $test, $search_test) = @_; - bless { raw_test => dclone($test), search_test => $search_test }, $class; -} - -############# -# Accessors # -############# - -sub search_test { return $_[0]->{search_test} } -sub name { return 'Custom: ' . $_[0]->test->{name} } -sub test { return $_[0]->{raw_test} } - -sub operator_test { die "unimplemented" } -sub field_object { die "unimplemented" } -sub main_value { die "unimplenmented" } -sub test_value { die "unimplemented" } -# Custom tests don't use transforms. -sub transformed_value_was_equal { 0 } -sub debug_value { - my ($self) = @_; - my $string = ''; - my $params = $self->search_params; - foreach my $param (keys %$params) { - $string .= $param . "=" . $params->{$param} . '&'; - } - chop($string); - return $string; -} - -# The tests we know are broken for this operator/field combination. -sub _known_broken { return {} } -sub contains_known_broken { return undef } -sub search_known_broken { return undef } -sub field_not_yet_implemented { return undef } -sub invalid_field_operator_combination { return undef } - -######################################### -# Accessors: Bugzilla::Search Arguments # -######################################### - -# Converts the f, o, v rows into f0, o0, v0, etc. and translates -# the values appropriately. -sub search_params { - my ($self) = @_; - - my %params = %{ $self->test->{top_params} || {} }; - my $counter = 0; - foreach my $row (@{ $self->test->{params} }) { - $row->{v} = $self->translate_value($row) if exists $row->{v}; - foreach my $key (keys %$row) { - $params{"${key}$counter"} = $row->{$key}; - } - $counter++; - } - - return \%params; -} - -sub translate_value { - my ($self, $row) = @_; - my $as_test = { field => $row->{f}, operator => $row->{o}, - value => $row->{v} }; - my $operator_test = new Bugzilla::Test::Search::OperatorTest($row->{o}, - $self->search_test); - my $field = Bugzilla::Field->check($row->{f}); - my $field_test = new Bugzilla::Test::Search::FieldTest($operator_test, - $field, $as_test); - return $field_test->translated_value; -} - -sub search_columns { - my ($self) = @_; - return ['bug_id', @{ $self->test->{columns} || [] }]; -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/FieldTest.pm b/xt/lib/Bugzilla/Test/Search/FieldTest.pm deleted file mode 100644 index 5e86d92e2..000000000 --- a/xt/lib/Bugzilla/Test/Search/FieldTest.pm +++ /dev/null @@ -1,617 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This module represents the tests that get run on a single -# operator/field combination for Bugzilla::Test::Search. -# This is where all the actual testing happens. -package Bugzilla::Test::Search::FieldTest; - -use strict; -use warnings; -use Bugzilla::Search; -use Bugzilla::Test::Search::Constants; -use Bugzilla::Util qw(trim); - -use Data::Dumper; -use Scalar::Util qw(blessed); -use Test::More; -use Test::Exception; - -############### -# Constructor # -############### - -sub new { - my ($class, $operator_test, $field, $test) = @_; - return bless { operator_test => $operator_test, - field_object => $field, - raw_test => $test }, $class; -} - -############# -# Accessors # -############# - -sub num_tests { return TESTS_PER_RUN } - -# The Bugzilla::Test::Search::OperatorTest that this is a child of. -sub operator_test { return $_[0]->{operator_test} } -# The Bugzilla::Field being tested. -sub field_object { return $_[0]->{field_object} } -# The name of the field being tested, which we need much more often -# than we need the object. -sub field { - my ($self) = @_; - $self->{field_name} ||= $self->field_object->name; - return $self->{field_name}; -} -# The Bugzilla::Test::Search object that this is a child of. -sub search_test { return $_[0]->operator_test->search_test } -# The operator being tested -sub operator { return $_[0]->operator_test->operator } -# The bugs currently being tested by Bugzilla::Test::Search. -sub bugs { return $_[0]->search_test->bugs } -sub bug { - my $self = shift; - return $self->search_test->bug(@_); -} -sub number { - my ($self, $id) = @_; - foreach my $number (1..NUM_BUGS) { - return $number if $self->search_test->bug($number)->id == $id; - } - return 0; -} - -# The name displayed for this test by Test::More. Used in test descriptions. -sub name { - my ($self) = @_; - my $field = $self->field; - my $operator = $self->operator; - my $value = $self->main_value; - - my $name = "$field-$operator-$value"; - if (my $extra_name = $self->test->{extra_name}) { - $name .= "-$extra_name"; - } - return $name; -} - -# The appropriate value from the TESTS constant for this test, taking -# into account overrides. -sub test { - my $self = shift; - return $self->{test} if $self->{test}; - - my %test = %{ $self->{raw_test} }; - - # We have field name overrides... - my $override = $test{override}->{$self->field}; - # And also field type overrides. - if (!$override) { - $override = $test{override}->{$self->field_object->type} || {}; - } - - foreach my $key (%$override) { - $test{$key} = $override->{$key}; - } - - $self->{test} = \%test; - return $self->{test}; -} - -# All the values for all the bugs for this field. -sub _field_values { - my ($self) = @_; - return $self->{field_values} if $self->{field_values}; - - my %field_values; - foreach my $number (1..NUM_BUGS) { - $field_values{$number} = $self->_field_values_for_bug($number); - } - $self->{field_values} = \%field_values; - return $self->{field_values}; -} -# The values for this field for the numbered bug. -sub bug_values { - my ($self, $number) = @_; - return @{ $self->_field_values->{$number} }; -} - -# The untranslated, non-overriden value--used in the name of the test -# and other places. -sub main_value { return $_[0]->{raw_test}->{value} } -# The untranslated test value, taking into account overrides. -sub test_value { return $_[0]->test->{value} }; -# The value translated appropriately for passing to Bugzilla::Search. -sub translated_value { - my $self = shift; - if (!exists $self->{translated_value}) { - my $value = $self->search_test->value_translation_cache($self); - if (!defined $value) { - $value = $self->_translate_value(); - $self->search_test->value_translation_cache($self, $value); - } - $self->{translated_value} = $value; - } - return $self->{translated_value}; -} -# Used in failure diagnostic messages. -sub debug_fail { - my ($self, $number, $results, $sql) = @_; - my @expected = @{ $self->test->{contains} }; - my @results = sort - map { $self->number($_) } - map { $_->[0] } - @$results; - return - " Value: '" . $self->translated_value . "'\n" . - "Expected: [" . join(',', @expected) . "]\n" . - " Results: [" . join(',', @results) . "]\n" . - trim($sql) . "\n"; -} - -# True for a bug if we ran the "transform" function on it and the -# result was equal to its first value. -sub transformed_value_was_equal { - my ($self, $number, $value) = @_; - if (@_ > 2) { - $self->{transformed_value_was_equal}->{$number} = $value; - $self->search_test->was_equal_cache($self, $number, $value); - } - my $cached = $self->search_test->was_equal_cache($self, $number); - return $cached if defined $cached; - return $self->{transformed_value_was_equal}->{$number}; -} - -# True if this test is supposed to contain the numbered bug. -sub bug_is_contained { - my ($self, $number) = @_; - my $contains = $self->test->{contains}; - if ($self->transformed_value_was_equal($number) - and !$self->test->{override}->{$self->field}->{contains}) - { - $contains = $self->test->{if_equal}->{contains}; - } - return grep($_ == $number, @$contains) ? 1 : 0; -} - -################################################### -# Accessors: Ways of doing SKIP and TODO on tests # -################################################### - -# The tests we know are broken for this operator/field combination. -sub _known_broken { - my ($self, $constant, $skip_pg_check) = @_; - - $constant ||= KNOWN_BROKEN; - my $field = $self->field; - my $type = $self->field_object->type; - my $operator = $self->operator; - my $value = $self->main_value; - my $value_name = "$operator-$value"; - if (my $extra_name = $self->test->{extra_name}) { - $value_name .= "-$extra_name"; - } - - my $value_broken = $constant->{$value_name}->{$field}; - $value_broken ||= $constant->{$value_name}->{$type}; - return $value_broken if $value_broken; - my $operator_broken = $constant->{$operator}->{$field}; - $operator_broken ||= $constant->{$operator}->{$type}; - return $operator_broken if $operator_broken; - return {}; -} - -# True if the "contains" search for the numbered bug is broken. -# That is, either the result is supposed to contain it and doesn't, -# or the result is not supposed to contain it and does. -sub contains_known_broken { - my ($self, $number) = @_; - my $field = $self->field; - my $operator = $self->operator; - - my $contains_broken = $self->_known_broken->{contains} || []; - if (grep($_ == $number, @$contains_broken)) { - return "$field $operator contains $number is known to be broken"; - } - return undef; -} - -# Used by subclasses. Checks both bug_is_contained and contains_known_broken -# to tell you whether or not the bug will *actually* be found by the test. -sub will_actually_contain_bug { - my ($self, $number) = @_; - my $is_contained = $self->bug_is_contained($number) ? 1 : 0; - my $is_broken = $self->contains_known_broken($number) ? 1 : 0; - - # If the test is supposed to contain the bug and *isn't* broken, - # then the test will contain the bug. - return 1 if ($is_contained and !$is_broken); - # If this test is *not* supposed to contain the bug, but that test is - # broken, then this test *will* contain the bug. - return 1 if (!$is_contained and $is_broken); - - return 0; -} - -# Returns a string if creating a Bugzilla::Search object throws an error, -# with this field/operator/value combination. -sub search_known_broken { - my ($self) = @_; - my $field = $self->field; - my $operator = $self->operator; - if ($self->_known_broken->{search}) { - return "Bugzilla::Search for $field $operator is known to be broken"; - } - return undef; -} - -# Returns a string if we haven't yet implemented the tests for this field, -# but we plan to in the future. -sub field_not_yet_implemented { - my ($self) = @_; - my $skip_this_field = grep { $_ eq $self->field } SKIP_FIELDS; - if ($skip_this_field) { - my $field = $self->field; - return "$field testing not yet implemented"; - } - return undef; -} - -# Returns a message if this field/operator combination can't ever be run. -# At no time in the future will this field/operator combination ever work. -sub invalid_field_operator_combination { - my ($self) = @_; - my $field = $self->field; - my $operator = $self->operator; - - if ($field eq 'content' && $operator !~ /matches/) { - return "content field does not support $operator"; - } - elsif ($operator =~ /matches/ && $field ne 'content') { - return "matches operator does not support fields other than content"; - } - return undef; -} - -# True if this field is broken in an OR combination. -sub join_broken { - my ($self, $or_broken_map) = @_; - my $or_broken = $or_broken_map->{$self->field . '-' . $self->operator}; - if (!$or_broken) { - # See if this is a comment field, and in that case, if there's - # a generic entry for all comment fields. - my $is_comment_field = COMMENT_FIELDS->{$self->field}; - if ($is_comment_field) { - $or_broken = $or_broken_map->{'longdescs.-' . $self->operator}; - } - } - return $or_broken; -} - -######################################### -# Accessors: Bugzilla::Search Arguments # -######################################### - -# The data that will get passed to Bugzilla::Search as its arguments. -sub search_params { - my ($self) = @_; - return $self->{search_params} if $self->{search_params}; - - my %params = ( - "field0-0-0" => $self->field, - "type0-0-0" => $self->operator, - "value0-0-0" => $self->translated_value, - ); - - $self->{search_params} = \%params; - return $self->{search_params}; -} - -sub search_columns { - my ($self) = @_; - my $field = $self->field; - my @search_fields = qw(bug_id); - if ($self->field_object->buglist) { - my $col_name = COLUMN_TRANSLATION->{$field} || $field; - push(@search_fields, $col_name); - } - return \@search_fields; -} - - -################ -# Field Values # -################ - -sub _field_values_for_bug { - my ($self, $number) = @_; - my $field = $self->field; - - my @values; - - if ($field =~ /^attach.+\.(.+)$/ ) { - my $attach_field = $1; - $attach_field = ATTACHMENT_FIELDS->{$attach_field} || $attach_field; - @values = $self->_values_for($number, 'attachments', $attach_field); - } - elsif (my $flag_field = FLAG_FIELDS->{$field}) { - @values = $self->_values_for($number, 'flags', $flag_field); - } - elsif (my $translation = COMMENT_FIELDS->{$field}) { - @values = $self->_values_for($number, 'comments', $translation); - # We want the last value to come first, so that single-value - # searches use the last comment. - @values = reverse @values; - } - elsif ($field eq 'longdescs.count') { - @values = scalar(@{ $self->bug($number)->comments }); - } - elsif ($field eq 'work_time') { - @values = $self->_values_for($number, 'actual_time'); - } - elsif ($field eq 'bug_group') { - @values = $self->_values_for($number, 'groups_in', 'name'); - } - elsif ($field eq 'keywords') { - @values = $self->_values_for($number, 'keyword_objects', 'name'); - } - elsif ($field eq 'content') { - @values = $self->_values_for($number, 'short_desc'); - } - elsif ($field eq 'see_also') { - @values = $self->_values_for($number, 'see_also', 'name'); - } - elsif ($field eq 'tag') { - @values = $self->_values_for($number, 'tags'); - } - # Bugzilla::Bug truncates creation_ts, but we need the full value - # from the database. This has no special value for changedfrom, - # because it never changes. - elsif ($field eq 'creation_ts') { - my $bug = $self->bug($number); - my $creation_ts = Bugzilla->dbh->selectrow_array( - 'SELECT creation_ts FROM bugs WHERE bug_id = ?', - undef, $bug->id); - @values = ($creation_ts); - } - else { - @values = $self->_values_for($number, $field); - } - - # We convert user objects to their login name, here, all in one - # block for simplicity. - if (grep { $_ eq $field } USER_FIELDS) { - # requestees.login_name is empty for most bugs (but checking - # blessed(undef) handles that. - # Values that come from %original_values aren't User objects. - @values = map { blessed($_) ? $_->login : $_ } @values; - @values = grep { defined $_ } @values; - } - - return \@values; -} - -sub _values_for { - my ($self, $number, $bug_field, $item_field) = @_; - - my $item; - if ($self->operator eq 'changedfrom') { - $item = $self->search_test->bug_create_value($number, $bug_field); - } - else { - my $bug = $self->bug($number); - $item = $bug->$bug_field; - } - - if ($item_field) { - if ($bug_field eq 'flags' and $item_field eq 'name') { - return (map { $_->name . $_->status } @$item); - } - return (map { $self->_get_item($_, $item_field) } @$item); - } - - return @$item if ref($item) eq 'ARRAY'; - return $item if defined $item; - return (); -} - -sub _get_item { - my ($self, $from, $field) = @_; - if (blessed($from)) { - return $from->$field; - } - return $from->{$field}; -} - -##################### -# Value Translation # -##################### - -# This function translates the "value" specified in TESTS into an actual -# search value to pass to Search.pm. This means that we get the value -# from the current bug (or, in the case of changedfrom, from %original_values) -# and then we insert it as required into the "value" from TESTS. (For example, -# <1> becomes the value for the field from bug 1.) -sub _translate_value { - my $self = shift; - my $value = $self->test_value; - foreach my $number (1..NUM_BUGS) { - $value = $self->_translate_value_for_bug($number, $value); - } - # Sanity check to make sure that none of the <> stuff was left in. - if ($value =~ /<\d/) { - die $self->name . ": value untranslated: $value\n"; - } - return $value; -} - -sub _translate_value_for_bug { - my ($self, $number, $value) = @_; - - my $bug = $self->bug($number); - - my $bug_id = $bug->id; - $value =~ s/<$number-id>/$bug_id/g; - my $bug_delta = $bug->delta_ts; - $value =~ s/<$number-delta>/$bug_delta/g; - my $reporter = $bug->reporter->login; - $value =~ s/<$number-reporter>/$reporter/g; - if ($value =~ /<$number-bug_group>/) { - my @bug_groups = map { $_->name } @{ $bug->groups_in }; - @bug_groups = grep { $_ =~ /^\d+-group-/ } @bug_groups; - my $group = $bug_groups[0]; - $value =~ s/<$number-bug_group>/$group/g; - } - - my @bug_values = $self->bug_values($number); - return $value if !@bug_values; - - if ($self->operator =~ /substr/) { - @bug_values = map { $self->_substr_value($_) } @bug_values; - } - - my $string_value = $bug_values[0]; - if ($self->operator =~ /word/) { - $string_value = join(' ', @bug_values); - } - if (my $func = $self->test->{transform}) { - my $transformed = $func->(@bug_values); - my $is_equal = $transformed eq $bug_values[0] ? 1 : 0; - $self->transformed_value_was_equal($number, $is_equal); - $string_value = $transformed; - } - - if ($self->test->{escape}) { - $string_value = quotemeta($string_value); - } - $value =~ s/<$number>/$string_value/g; - - return $value; -} - -sub _substr_value { - my ($self, $value) = @_; - my $field = $self->field; - my $type = $self->field_object->type; - my $substr_size = SUBSTR_SIZE; - if (exists FIELD_SUBSTR_SIZE->{$field}) { - $substr_size = FIELD_SUBSTR_SIZE->{$field}; - } - elsif (exists FIELD_SUBSTR_SIZE->{$type}) { - $substr_size = FIELD_SUBSTR_SIZE->{$type}; - } - if ($substr_size > 0) { - # The field name is included in every field value, and if it's - # long, it might take up the whole substring, and we don't want that. - if (!grep { $_ eq $field or $_ eq $type } SUBSTR_NO_FIELD_ADD) { - $substr_size += length($field); - } - my $string = substr($value, 0, $substr_size); - return $string; - } - return substr($value, $substr_size); -} - -##################### -# Main Test Methods # -##################### - -sub run { - my ($self) = @_; - - my $invalid_combination = $self->invalid_field_operator_combination; - my $field_not_implemented = $self->field_not_yet_implemented; - - SKIP: { - skip($invalid_combination, $self->num_tests) if $invalid_combination; - TODO: { - todo_skip ($field_not_implemented, $self->num_tests) if $field_not_implemented; - $self->do_tests(); - } - } -} - -sub do_tests { - my ($self) = @_; - my $name = $self->name; - - my $search_broken = $self->search_known_broken; - - my $search = $self->_test_search_object_creation(); - - my $sql; - TODO: { - local $TODO = $search_broken if $search_broken; - lives_ok { $sql = $search->_sql } "$name: generate SQL"; - } - - my $results; - SKIP: { - skip "Can't run SQL without any SQL", 1 if !defined $sql; - $results = $self->_test_sql($search); - } - - $self->_test_content($results, $sql); -} - -sub _test_search_object_creation { - my ($self) = @_; - my $name = $self->name; - my @args = (fields => $self->search_columns, params => $self->search_params); - my $search; - lives_ok { $search = new Bugzilla::Search(@args) } - "$name: create search object"; - return $search; -} - -sub _test_sql { - my ($self, $search) = @_; - my $name = $self->name; - my $results; - lives_ok { $results = $search->data } "$name: Run SQL Query" - or diag($search->_sql); - return $results; -} - -sub _test_content { - my ($self, $results, $sql) = @_; - - SKIP: { - skip "Without results we can't test them", NUM_BUGS if !$results; - foreach my $number (1..NUM_BUGS) { - $self->_test_content_for_bug($number, $results, $sql); - } - } -} - -sub _test_content_for_bug { - my ($self, $number, $results, $sql) = @_; - my $name = $self->name; - - my $contains_known_broken = $self->contains_known_broken($number); - - my %result_ids = map { $_->[0] => 1 } @$results; - my $bug_id = $self->bug($number)->id; - - TODO: { - local $TODO = $contains_known_broken if $contains_known_broken; - if ($self->bug_is_contained($number)) { - ok($result_ids{$bug_id}, - "$name: contains bug $number ($bug_id)") - or diag $self->debug_fail($number, $results, $sql); - } - else { - ok(!$result_ids{$bug_id}, - "$name: does not contain bug $number ($bug_id)") - or diag $self->debug_fail($number, $results, $sql); - } - } -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/FieldTestNormal.pm b/xt/lib/Bugzilla/Test/Search/FieldTestNormal.pm deleted file mode 100644 index 888e7eb13..000000000 --- a/xt/lib/Bugzilla/Test/Search/FieldTestNormal.pm +++ /dev/null @@ -1,104 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This is the same as a FieldTest, except that it uses normal URL -# parameters instead of Boolean Charts. -package Bugzilla::Test::Search::FieldTestNormal; -use strict; -use warnings; -use parent qw(Bugzilla::Test::Search::FieldTest); - -use Scalar::Util qw(blessed); - -use constant CH_OPERATOR => { - changedafter => 'chfieldfrom', - changedbefore => 'chfieldto', - changedto => 'chfieldvalue', -}; - -use constant EMAIL_FIELDS => qw(assigned_to qa_contact cc reporter commenter); - -# Normally, we just clone a FieldTest because that's the best for performance, -# overall--that way we don't have to translate the value again. However, -# sometimes (like in Bugzilla::Test::Search's direct code) we just want -# to create a FieldTestNormal. -sub new { - my $class = shift; - my ($first_arg) = @_; - if (blessed $first_arg - and $first_arg->isa('Bugzilla::Test::Search::FieldTest')) - { - my $self = { %$first_arg }; - return bless $self, $class; - } - return $class->SUPER::new(@_); -} - -sub name { - my $self = shift; - my $name = $self->SUPER::name(@_); - return "$name (Normal Params)"; -} - -sub search_columns { - my $self = shift; - my $field = $self->field; - # For the assigned_to, qa_contact, and reporter fields, have the - # "Normal Params" test check that the _realname columns work - # all by themselves. - if (grep($_ eq $field, EMAIL_FIELDS) && $self->field_object->buglist) { - return ['bug_id', "${field}_realname"] - } - return $self->SUPER::search_columns(@_); -} - -sub search_params { - my ($self) = @_; - my $field = $self->field; - my $operator = $self->operator; - my $value = $self->translated_value; - if ($operator eq 'anyexact') { - $value = [split ',', $value]; - } - - if (my $ch_param = CH_OPERATOR->{$operator}) { - if ($field eq 'creation_ts') { - $field = '[Bug creation]'; - } - return { chfield => $field, $ch_param => $value }; - } - - if ($field eq 'delta_ts' and $operator eq 'greaterthaneq') { - return { chfieldfrom => $value }; - } - if ($field eq 'delta_ts' and $operator eq 'lessthaneq') { - return { chfieldto => $value }; - } - - if ($field eq 'deadline' and $operator eq 'greaterthaneq') { - return { deadlinefrom => $value }; - } - if ($field eq 'deadline' and $operator eq 'lessthaneq') { - return { deadlineto => $value }; - } - - if (grep { $_ eq $field } EMAIL_FIELDS) { - $field = 'longdesc' if $field eq 'commenter'; - return { - email1 => $value, - "email${field}1" => 1, - emailtype1 => $operator, - # Used to do extra tests on special sorts of email* combinations. - %{ $self->test->{extra_params} || {} }, - }; - } - - $field =~ s/\./_/g; - return { $field => $value, "${field}_type" => $operator }; -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/InjectionTest.pm b/xt/lib/Bugzilla/Test/Search/InjectionTest.pm deleted file mode 100644 index 90eaabc78..000000000 --- a/xt/lib/Bugzilla/Test/Search/InjectionTest.pm +++ /dev/null @@ -1,77 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This module represents the SQL Injection tests that get run on a single -# operator/field combination for Bugzilla::Test::Search. -package Bugzilla::Test::Search::InjectionTest; -use parent qw(Bugzilla::Test::Search::FieldTest); - -use strict; -use warnings; -use Bugzilla::Test::Search::Constants; -use Test::Exception; - -sub num_tests { return NUM_SEARCH_TESTS } - -sub _known_broken { - my ($self) = @_; - my $operator_broken = INJECTION_BROKEN_OPERATOR->{$self->operator}; - # We don't want to auto-vivify $operator_broken and thus make it true. - my @field_ok = $operator_broken ? @{ $operator_broken->{field_ok} || [] } - : (); - $operator_broken = undef if grep { $_ eq $self->field } @field_ok; - - my $field_broken = INJECTION_BROKEN_FIELD->{$self->field} - || INJECTION_BROKEN_FIELD->{$self->field_object->type}; - # We don't want to auto-vivify $field_broken and thus make it true. - my @operator_ok = $field_broken ? @{ $field_broken->{operator_ok} || [] } - : (); - $field_broken = undef if grep { $_ eq $self->operator } @operator_ok; - - return $operator_broken || $field_broken || {}; -} - -sub sql_error_ok { return $_[0]->_known_broken->{sql_error} } - -# Injection tests only skip fields on certain dbs. -sub field_not_yet_implemented { - my ($self) = @_; - # We use the constant directly because we don't want operator_ok - # or field_ok to stop us. - my $broken = INJECTION_BROKEN_FIELD->{$self->field} - || INJECTION_BROKEN_FIELD->{$self->field_object->type}; - my $skip_for_dbs = $broken->{db_skip}; - return undef if !$skip_for_dbs; - my $dbh = Bugzilla->dbh; - if (my ($skip) = grep { $dbh->isa("Bugzilla::DB::$_") } @$skip_for_dbs) { - my $field = $self->field; - return "$field injection testing is not supported with $skip"; - } - return undef; -} -# Injection tests don't do translation. -sub translated_value { $_[0]->test_value } - -sub name { return "injection-" . $_[0]->SUPER::name; } - -# Injection tests don't check content. -sub _test_content {} - -sub _test_sql { - my $self = shift; - my ($sql) = @_; - my $dbh = Bugzilla->dbh; - my $name = $self->name; - if (my $error_ok = $self->sql_error_ok) { - throws_ok { $dbh->selectall_arrayref($sql) } $error_ok, - "$name: SQL query dies, as we expect"; - return; - } - return $self->SUPER::_test_sql(@_); -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/NotTest.pm b/xt/lib/Bugzilla/Test/Search/NotTest.pm deleted file mode 100644 index 190b8567b..000000000 --- a/xt/lib/Bugzilla/Test/Search/NotTest.pm +++ /dev/null @@ -1,61 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This module runs tests just like a normal FieldTest, AndTest, -# or OrTest, but in a NOT chart instead of a normal chart. -# -# Logically this should be a mixin of some sort so that we can apply -# it to OrTest and AndTest, but without Moose there isn't much of an -# easy way to do that. -package Bugzilla::Test::Search::NotTest; -use parent qw(Bugzilla::Test::Search::FieldTest); -use strict; -use warnings; -use Bugzilla::Test::Search::Constants; - -# We just clone a FieldTest because that's the best for performance, -# overall--that way we don't have to translate the value again. -sub new { - my ($class, $field_test) = @_; - my $self = { %$field_test }; - return bless $self, $class; -} - -############# -# Accessors # -############# - -sub name { - my ($self) = @_; - return "NOT(" . $self->SUPER::name . ")"; -} - -# True if this test is supposed to contain the numbered bug. Reversed for -# NOT tests. -sub bug_is_contained { - my $self = shift; - my ($number) = @_; - # No search ever returns bug 6, because it's protected by security groups - # that the searcher isn't a member of. - return 0 if $number == 6; - return $self->SUPER::bug_is_contained(@_) ? 0 : 1; -} - -# NOT tests have their own constant for tracking broken-ness. -sub _known_broken { - my ($self) = @_; - return $self->SUPER::_known_broken(BROKEN_NOT, 'skip pg check'); -} - -sub search_params { - my ($self) = @_; - my %params = %{ $self->SUPER::search_params() }; - $params{negate0} = 1; - return \%params; -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/OperatorTest.pm b/xt/lib/Bugzilla/Test/Search/OperatorTest.pm deleted file mode 100644 index 5ab502dfc..000000000 --- a/xt/lib/Bugzilla/Test/Search/OperatorTest.pm +++ /dev/null @@ -1,103 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This module represents the tests that get run on a single operator -# from the TESTS constant in Bugzilla::Search::Test::Constants. -package Bugzilla::Test::Search::OperatorTest; - -use strict; -use warnings; -use Bugzilla::Test::Search::Constants; -use Bugzilla::Test::Search::FieldTest; -use Bugzilla::Test::Search::FieldTestNormal; -use Bugzilla::Test::Search::InjectionTest; -use Bugzilla::Test::Search::OrTest; -use Bugzilla::Test::Search::AndTest; -use Bugzilla::Test::Search::NotTest; - -############### -# Constructor # -############### - -sub new { - my ($invocant, $operator, $search_test) = @_; - $search_test ||= $invocant->search_test; - my $class = ref($invocant) || $invocant; - return bless { search_test => $search_test, operator => $operator }, $class; -} - -############# -# Accessors # -############# - -# The Bugzilla::Test::Search object that this is a child of. -sub search_test { return $_[0]->{search_test} } -# The operator being tested -sub operator { return $_[0]->{operator} } -# The tests that we're going to run on this operator. -sub tests { return @{ TESTS->{$_[0]->operator } } } -# The fields we're going to test for this operator. -sub test_fields { return $_[0]->search_test->all_fields } - -sub run { - my ($self) = @_; - - foreach my $field ($self->test_fields) { - foreach my $test ($self->tests) { - my $field_test = - new Bugzilla::Test::Search::FieldTest($self, $field, $test); - $field_test->run(); - my $normal_test = - new Bugzilla::Test::Search::FieldTestNormal($field_test); - $normal_test->run(); - my $not_test = new Bugzilla::Test::Search::NotTest($field_test); - $not_test->run(); - - next if !$self->search_test->option('long'); - - # Run the OR tests. This tests every other operator (including - # this operator itself) in combination with every other field, - # in an OR with this operator and field. - foreach my $other_operator ($self->search_test->all_operators) { - $self->run_join_tests($field_test, $other_operator); - } - } - foreach my $test (INJECTION_TESTS) { - my $injection_test = - new Bugzilla::Test::Search::InjectionTest($self, $field, $test); - $injection_test->run(); - } - } -} - -sub run_join_tests { - my ($self, $field_test, $other_operator) = @_; - - my $other_operator_test = $self->new($other_operator); - foreach my $other_test ($other_operator_test->tests) { - foreach my $other_field ($self->test_fields) { - $self->_run_one_join_test($field_test, $other_operator_test, - $other_field, $other_test); - $self->search_test->clean_test_history(); - } - } -} - -sub _run_one_join_test { - my ($self, $field_test, $other_operator_test, $other_field, $other_test) = @_; - my $other_field_test = - new Bugzilla::Test::Search::FieldTest($other_operator_test, - $other_field, $other_test); - my $or_test = new Bugzilla::Test::Search::OrTest($field_test, - $other_field_test); - $or_test->run(); - my $and_test = new Bugzilla::Test::Search::AndTest($field_test, - $other_field_test); - $and_test->run(); -} - -1; diff --git a/xt/lib/Bugzilla/Test/Search/OrTest.pm b/xt/lib/Bugzilla/Test/Search/OrTest.pm deleted file mode 100644 index 1b948f38d..000000000 --- a/xt/lib/Bugzilla/Test/Search/OrTest.pm +++ /dev/null @@ -1,141 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -# This test combines two field/operator combinations using OR in -# a single boolean chart. -package Bugzilla::Test::Search::OrTest; -use parent qw(Bugzilla::Test::Search::FieldTest); - -use Bugzilla::Test::Search::Constants; -use List::MoreUtils qw(all any uniq); - -use constant type => 'OR'; - -############### -# Constructor # -############### - -sub new { - my $class = shift; - my $self = { field_tests => [@_] }; - return bless $self, $class; -} - -############# -# Accessors # -############# - -sub field_tests { return @{ $_[0]->{field_tests} } } -sub search_test { ($_[0]->field_tests)[0]->search_test } - -sub name { - my ($self) = @_; - my @names = map { $_->name } $self->field_tests; - return join('-' . $self->type . '-', @names); -} - -# In an OR test, bugs ARE supposed to be contained if they are contained -# by ANY test. -sub bug_is_contained { - my ($self, $number) = @_; - return any { $_->bug_is_contained($number) } $self->field_tests; -} - -# Needed only for failure messages -sub debug_value { - my ($self) = @_; - my @values = map { $_->field . ' ' . $_->debug_value } $self->field_tests; - return join(' ' . $self->type . ' ', @values); -} - -######################## -# SKIP & TODO Messages # -######################## - -sub field_not_yet_implemented { - my ($self) = @_; - return $self->_join_messages('field_not_yet_implemented'); -} -sub invalid_field_operator_combination { - my ($self) = @_; - return $self->_join_messages('invalid_field_operator_combination'); -} -sub search_known_broken { - my ($self) = @_; - return $self->_join_messages('search_known_broken'); -} - -sub _join_messages { - my ($self, $message_method) = @_; - my @messages = map { $_->$message_method } $self->field_tests; - @messages = grep { $_ } @messages; - return join(' AND ', @messages); -} - -sub _bug_will_actually_be_contained { - my ($self, $number) = @_; - - foreach my $test ($self->field_tests) { - # Some tests are broken in such a way that they actually - # generate no criteria in the SQL. In this case, the only way - # the test contains the bug is if *another* test contains it. - next if $test->_known_broken->{no_criteria}; - return 1 if $test->will_actually_contain_bug($number); - } - return 0; -} - -sub contains_known_broken { - my ($self, $number) = @_; - - if ( ( $self->bug_is_contained($number) - and !$self->_bug_will_actually_be_contained($number) ) - or ( !$self->bug_is_contained($number) - and $self->_bug_will_actually_be_contained($number) ) ) - { - my @messages = map { $_->contains_known_broken($number) } - $self->field_tests; - @messages = grep { $_ } @messages; - # Sometimes, with things that break because of no_criteria, there won't - # be anything in @messages even though we need to print out a message. - if (!@messages) { - my @no_criteria = grep { $_->_known_broken->{no_criteria} } - $self->field_tests; - @messages = map { "No criteria generated by " . $_->name } - @no_criteria; - } - die "broken test with no message" if !@messages; - return join(' AND ', @messages); - } - return undef; -} - -############################## -# Bugzilla::Search arguments # -############################## - -sub search_columns { - my ($self) = @_; - my @columns = map { @{ $_->search_columns } } $self->field_tests; - return [uniq @columns]; -} - -sub search_params { - my ($self) = @_; - my @all_params = map { $_->search_params } $self->field_tests; - my %params; - my $chart = 0; - foreach my $item (@all_params) { - $params{"field0-0-$chart"} = $item->{'field0-0-0'}; - $params{"type0-0-$chart"} = $item->{'type0-0-0'}; - $params{"value0-0-$chart"} = $item->{'value0-0-0'}; - $chart++; - } - return \%params; -} - -1; |