Index: appengine/monorail/search/test/query2ast_test.py |
diff --git a/appengine/monorail/search/test/query2ast_test.py b/appengine/monorail/search/test/query2ast_test.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e25768ee9b0b31ddcf6dfcfab9d2ecc5c44bee8c |
--- /dev/null |
+++ b/appengine/monorail/search/test/query2ast_test.py |
@@ -0,0 +1,546 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is govered by a BSD-style |
+# license that can be found in the LICENSE file or at |
+# https://developers.google.com/open-source/licenses/bsd |
+ |
+"""Tests for the query2ast module.""" |
+ |
+import datetime |
+import time |
+import unittest |
+ |
+from proto import ast_pb2 |
+from search import query2ast |
+from services import fulltext_helpers |
+from tracker import tracker_bizobj |
+ |
+BOOL = query2ast.BOOL |
+DATE = query2ast.DATE |
+NUM = query2ast.NUM |
+TXT = query2ast.TXT |
+ |
+BUILTIN_ISSUE_FIELDS = query2ast.BUILTIN_ISSUE_FIELDS |
+ANY_FIELD = query2ast.BUILTIN_ISSUE_FIELDS['any_field'] |
+ |
+EQ = query2ast.EQ |
+NE = query2ast.NE |
+LT = query2ast.LT |
+GT = query2ast.GT |
+LE = query2ast.LE |
+GE = query2ast.GE |
+TEXT_HAS = query2ast.TEXT_HAS |
+NOT_TEXT_HAS = query2ast.NOT_TEXT_HAS |
+TEXT_MATCHES = query2ast.TEXT_MATCHES |
+NOT_TEXT_MATCHES = query2ast.NOT_TEXT_MATCHES |
+IS_DEFINED = query2ast.IS_DEFINED |
+IS_NOT_DEFINED = query2ast.IS_NOT_DEFINED |
+KEY_HAS = query2ast.KEY_HAS |
+ |
+MakeCond = ast_pb2.MakeCond |
+ |
+ |
+class QueryParsingUnitTest(unittest.TestCase): |
+ |
+ default_config = tracker_bizobj.MakeDefaultProjectIssueConfig(789) |
+ |
+ @unittest.skip('TODO(jrobbins): fully support OR') |
+ def skip_testParseUserQuery_OrClause(self): |
+ # ParseUserQuery extends _ParseORQuery with specialized |
+ # handling of "OR" operators in a user query |
+ |
+ # an "OR" query, which should look like two separate simple querys |
+ # joined together by a pipe. |
+ ast = query2ast.ParseUserQuery( |
+ 'ham OR fancy', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ conj1 = ast.conjunctions[0] |
+ conj2 = ast.conjunctions[1] |
+ self.assertEqual([MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], [])], |
+ conj1.conds) |
+ self.assertEqual([MakeCond(TEXT_HAS, [ANY_FIELD], ['fancy'], [])], |
+ conj2.conds) |
+ |
+ def testParseUserQuery_Words(self): |
+ # an "ORTerm" is actually anything appearing on either side of an |
+ # "OR" operator. So this could be thought of as "simple" query parsing. |
+ |
+ # a simple query with no spaces |
+ ast = query2ast.ParseUserQuery( |
+ 'hamfancy', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ fulltext_cond = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['hamfancy'], []), fulltext_cond) |
+ |
+ # negative word |
+ ast = query2ast.ParseUserQuery( |
+ '-hamfancy', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ fulltext_cond = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ # note: not NOT_TEXT_HAS. |
+ MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['hamfancy'], []), |
+ fulltext_cond) |
+ |
+ # an explicit "AND" query in the "featured" context |
+ warnings = [] |
+ query2ast.ParseUserQuery( |
+ 'ham AND fancy', 'label:featured', BUILTIN_ISSUE_FIELDS, |
+ self.default_config, warnings=warnings) |
+ self.assertEqual( |
+ ['The only supported boolean operator is OR (all capitals).'], |
+ warnings) |
+ |
+ # an implicit "AND" query |
+ ast = query2ast.ParseUserQuery( |
+ 'ham fancy', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], []), ft_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['fancy'], []), ft_cond2) |
+ |
+ # Use word with special prefix. |
+ word_with_special_prefix = '%stest' % fulltext_helpers.NON_OP_PREFIXES[0] |
+ ast = query2ast.ParseUserQuery( |
+ word_with_special_prefix, '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ fulltext_cond = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], [word_with_special_prefix], []), |
+ fulltext_cond) |
+ |
+ # mix positive and negative words |
+ ast = query2ast.ParseUserQuery( |
+ 'ham -fancy', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], []), ft_cond1) |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['fancy'], []), ft_cond2) |
+ |
+ # converts terms to lower case |
+ ast = query2ast.ParseUserQuery( |
+ 'AmDude', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, fulltext_cond = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['amdude'], []), fulltext_cond) |
+ |
+ def testParseUserQuery_Phrases(self): |
+ # positive phrases |
+ ast = query2ast.ParseUserQuery( |
+ '"one two"', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, fulltext_cond = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['"one two"'], []), fulltext_cond) |
+ |
+ # negative phrases |
+ ast = query2ast.ParseUserQuery( |
+ '-"one two"', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, fulltext_cond = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['"one two"'], []), fulltext_cond) |
+ |
+ # multiple phrases |
+ ast = query2ast.ParseUserQuery( |
+ '-"a b" "x y"', '-label:deprecated', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['deprecated'], []), |
+ scope_cond1) |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['"a b"'], []), ft_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['"x y"'], []), ft_cond2) |
+ |
+ def testParseUserQuery_CodeSyntaxThatWeNeedToCopeWith(self): |
+ # positive phrases |
+ ast = query2ast.ParseUserQuery( |
+ 'Base::Tuple', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], |
+ ['"base::tuple"'], []), |
+ cond) |
+ |
+ def testParseUserQuery_HasOperator(self): |
+ # Search for issues with at least one attachment |
+ ast = query2ast.ParseUserQuery( |
+ 'has:attachment', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-has:attachment', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'has=attachment', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-has=attachment', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []), |
+ cond1) |
+ |
+ # Search for numeric fields for searches with 'has' prefix |
+ ast = query2ast.ParseUserQuery( |
+ 'has:attachments', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachments']], [], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-has:attachments', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachments']], |
+ [], []), |
+ cond1) |
+ |
+ def testParseUserQuery_Components(self): |
+ """Parse user queries for components""" |
+ ast = query2ast.ParseUserQuery( |
+ 'component:UI', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['component']], |
+ ['ui'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'Component:UI>AboutBox', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['component']], |
+ ['ui>aboutbox'], []), |
+ cond1) |
+ |
+ def testParseUserQuery_OwnersReportersAndCc(self): |
+ """Parse user queries for owner:, reporter: and cc:.""" |
+ ast = query2ast.ParseUserQuery( |
+ 'owner:user', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['owner']], |
+ ['user'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'owner:user@example.com', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['owner']], |
+ ['user@example.com'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'owner=user@example.com', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['owner']], |
+ ['user@example.com'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-reporter=user@example.com', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(NE, [BUILTIN_ISSUE_FIELDS['reporter']], |
+ ['user@example.com'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'cc=user@example.com,user2@example.com', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['cc']], |
+ ['user@example.com', 'user2@example.com'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'cc:user,user2', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['cc']], |
+ ['user', 'user2'], []), |
+ cond1) |
+ |
+ def testParseUserQuery_SearchWithinFields(self): |
+ # Search for issues with certain filenames |
+ ast = query2ast.ParseUserQuery( |
+ 'attachment:filename', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachment']], |
+ ['filename'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-attachment:filename', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachment']], |
+ ['filename'], []), |
+ cond1) |
+ |
+ # Search for issues with a certain number of attachments |
+ ast = query2ast.ParseUserQuery( |
+ 'attachments:2', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachments']], |
+ ['2'], [2]), |
+ cond1) |
+ |
+ # Searches with '=' syntax |
+ ast = query2ast.ParseUserQuery( |
+ 'attachment=filename', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['attachment']], |
+ ['filename'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-attachment=filename', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(NE, [BUILTIN_ISSUE_FIELDS['attachment']], |
+ ['filename'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'milestone=2009', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']], ['milestone-2009'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-milestone=2009', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(NE, [BUILTIN_ISSUE_FIELDS['label']], ['milestone-2009'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'milestone=2009-Q1', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['milestone-2009-q1'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ '-milestone=2009-Q1', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(NE, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['milestone-2009-q1'], []), |
+ cond1) |
+ |
+ # Searches with ':' syntax |
+ ast = query2ast.ParseUserQuery( |
+ 'summary:foo', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, |
+ [BUILTIN_ISSUE_FIELDS['summary']], ['foo'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'summary:"greetings programs"', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, |
+ [BUILTIN_ISSUE_FIELDS['summary']], ['greetings programs'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'summary:"Ӓ"', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, |
+ [BUILTIN_ISSUE_FIELDS['summary']], ['Ӓ'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'priority:high', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(KEY_HAS, |
+ [BUILTIN_ISSUE_FIELDS['label']], ['priority-high'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'type:security', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(KEY_HAS, |
+ [BUILTIN_ISSUE_FIELDS['label']], ['type-security'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'label:priority-high', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, |
+ [BUILTIN_ISSUE_FIELDS['label']], ['priority-high'], []), |
+ cond1) |
+ |
+ def testParseUserQuery_QuickOr(self): |
+ # quick-or searches |
+ ast = query2ast.ParseUserQuery( |
+ 'milestone:2008,2009,2010', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(KEY_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['milestone-2008', 'milestone-2009', 'milestone-2010'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'label:milestone-2008,milestone-2009,milestone-2010', '', |
+ BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['milestone-2008', 'milestone-2009', 'milestone-2010'], []), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'milestone=2008,2009,2010', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ self.assertEqual( |
+ MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']], |
+ ['milestone-2008', 'milestone-2009', 'milestone-2010'], []), |
+ cond1) |
+ |
+ def testParseUserQuery_Dates(self): |
+ # query with a daterange |
+ ast = query2ast.ParseUserQuery( |
+ 'modified>=2009-5-12', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple())) |
+ self.assertEqual( |
+ MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1) |
+ |
+ # query with multiple dateranges |
+ ast = query2ast.ParseUserQuery( |
+ 'modified>=2009-5-12 opened<2008/1/1', '', |
+ BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1, cond2 = ast.conjunctions[0].conds |
+ ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple())) |
+ self.assertEqual( |
+ MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1) |
+ ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple())) |
+ self.assertEqual( |
+ MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2) |
+ |
+ # query with multiple dateranges plus a search term |
+ ast = query2ast.ParseUserQuery( |
+ 'one two modified>=2009-5-12 opened<2008/1/1', '', |
+ BUILTIN_ISSUE_FIELDS, self.default_config) |
+ ft_cond1, ft_cond2, cond1, cond2 = ast.conjunctions[0].conds |
+ ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple())) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['one'], []), ft_cond1) |
+ self.assertEqual( |
+ MakeCond(TEXT_HAS, [ANY_FIELD], ['two'], []), ft_cond2) |
+ self.assertEqual( |
+ MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1) |
+ ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple())) |
+ self.assertEqual( |
+ MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2) |
+ |
+ # query with a date field compared to "today" |
+ ast = query2ast.ParseUserQuery( |
+ 'modified<today', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ ts1 = query2ast._CalculatePastDate(0) |
+ self.assertEqual(MakeCond(LT, [BUILTIN_ISSUE_FIELDS['modified']], |
+ [], [ts1]), |
+ cond1) |
+ |
+ # query with a daterange using today-N alias |
+ ast = query2ast.ParseUserQuery( |
+ 'modified>=today-13', '', BUILTIN_ISSUE_FIELDS, |
+ self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ ts1 = query2ast._CalculatePastDate(13) |
+ self.assertEqual(MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], |
+ [], [ts1]), |
+ cond1) |
+ |
+ ast = query2ast.ParseUserQuery( |
+ 'modified>today-13', '', BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1 = ast.conjunctions[0].conds[0] |
+ ts1 = query2ast._CalculatePastDate(13) |
+ self.assertEqual(MakeCond(GT, [BUILTIN_ISSUE_FIELDS['modified']], |
+ [], [ts1]), |
+ cond1) |
+ |
+ # query with multiple old date query terms. |
+ ast = query2ast.ParseUserQuery( |
+ 'modified-after:2009-5-12 opened-before:2008/1/1 ' |
+ 'closed-after:2007-2-1', '', |
+ BUILTIN_ISSUE_FIELDS, self.default_config) |
+ cond1, cond2, cond3 = ast.conjunctions[0].conds |
+ ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple())) |
+ self.assertEqual( |
+ MakeCond(GT, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1) |
+ ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple())) |
+ self.assertEqual( |
+ MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2) |
+ ts3 = int(time.mktime(datetime.datetime(2007, 2, 1).timetuple())) |
+ self.assertEqual( |
+ MakeCond(GT, [BUILTIN_ISSUE_FIELDS['closed']], [], [ts3]), cond3) |
+ |
+ |
+if __name__ == '__main__': |
+ unittest.main() |