Merge branch 'main' into avm99963-monorail
Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266
GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/testing/api_clients.cfg b/testing/api_clients.cfg
index c9588df..9168a4b 100644
--- a/testing/api_clients.cfg
+++ b/testing/api_clients.cfg
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
# Defines fake monorail api clients for testing
diff --git a/testing/fake.py b/testing/fake.py
index f484b12..c503fa0 100644
--- a/testing/fake.py
+++ b/testing/fake.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""Fake object classes that are useful for unit tests."""
from __future__ import print_function
@@ -27,11 +26,11 @@
from framework import permissions
from framework import profiler
from framework import validate
-from proto import features_pb2
-from proto import project_pb2
-from proto import tracker_pb2
-from proto import user_pb2
-from proto import usergroup_pb2
+from mrproto import features_pb2
+from mrproto import project_pb2
+from mrproto import tracker_pb2
+from mrproto import user_pb2
+from mrproto import usergroup_pb2
from services import caches
from services import config_svc
from services import features_svc
@@ -145,13 +144,35 @@
def MakeTestIssue(
- project_id, local_id, summary, status, owner_id, labels=None,
- derived_labels=None, derived_status=None, merged_into=0, star_count=0,
- derived_owner_id=0, issue_id=None, reporter_id=None, opened_timestamp=None,
- closed_timestamp=None, modified_timestamp=None, is_spam=False,
- component_ids=None, project_name=None, field_values=None, cc_ids=None,
- derived_cc_ids=None, assume_stale=True, phases=None, approval_values=None,
- merged_into_external=None, attachment_count=0, derived_component_ids=None):
+ project_id,
+ local_id,
+ summary,
+ status,
+ owner_id,
+ labels=None,
+ derived_labels=None,
+ derived_status=None,
+ merged_into=0,
+ star_count=0,
+ derived_owner_id=0,
+ issue_id=None,
+ reporter_id=None,
+ opened_timestamp=None,
+ closed_timestamp=None,
+ modified_timestamp=None,
+ migration_modified_timestamp=None,
+ is_spam=False,
+ component_ids=None,
+ project_name=None,
+ field_values=None,
+ cc_ids=None,
+ derived_cc_ids=None,
+ assume_stale=True,
+ phases=None,
+ approval_values=None,
+ merged_into_external=None,
+ attachment_count=0,
+ derived_component_ids=None):
"""Easily make an Issue for testing."""
issue = tracker_pb2.Issue()
issue.project_id = project_id
@@ -180,6 +201,11 @@
issue.component_modified_timestamp = opened_timestamp
if modified_timestamp:
issue.modified_timestamp = modified_timestamp
+ # By default, make migration_modified_timestamp the same as
+ # modified_timestamp
+ issue.migration_modified_timestamp = modified_timestamp
+ if migration_modified_timestamp:
+ issue.migration_modified_timestamp = migration_modified_timestamp
if closed_timestamp:
issue.closed_timestamp = closed_timestamp
if labels is not None:
@@ -1041,9 +1067,9 @@
return [project_dict[pid] for pid in project_id_list
if pid in project_dict]
- def GetVisibleLiveProjects(
+ def GetVisibleProjects(
self, _cnxn, logged_in_user, effective_ids, domain=None, use_cache=True):
- project_ids = list(self.projects_by_id.keys())
+ project_ids = sorted(self.projects_by_id.keys())
visible_project_ids = []
for pid in project_ids:
can_view = permissions.UserCanViewProject(
@@ -1245,17 +1271,23 @@
return None
return 'label_%d_%d' % (project_id, label_id)
- def LookupLabelID(self, cnxn, project_id, label, autocreate=True):
+ def LookupLabelID(
+ self, cnxn, project_id, label, autocreate=True, case_sensitive=False):
if label in self.label_to_id:
return self.label_to_id[label]
+ # TODO: The condition here is specifically added to return 'None' and
+ # allow testing for label freezing. This can be removed after refactoring
+ # other dependent tests to not fail for returning 'None' instead of '1'
+ # when label is not found in 'label_to_id' dict.
+ if label == 'freeze_new_label':
+ return None
return 1
def LookupLabelIDs(self, cnxn, project_id, labels, autocreate=False):
ids = []
next_label_id = 0
if self.id_to_label.keys():
- existing_ids = self.id_to_label.keys()
- existing_ids.sort()
+ existing_ids = sorted(self.id_to_label.keys())
next_label_id = existing_ids[-1] + 1
for label in labels:
if self.label_to_id.get(label) is not None:
@@ -1550,6 +1582,17 @@
# The next id to return if it is > 0.
self.next_id = -1
+ def UpdateIssue(
+ self,
+ cnxn,
+ issue,
+ update_cols=None,
+ just_derived=False,
+ commit=True,
+ invalidate=True):
+ self.UpdateIssues(
+ cnxn, [issue], update_cols, just_derived, commit, invalidate)
+
def UpdateIssues(
self, cnxn, issues, update_cols=None, just_derived=False,
commit=True, invalidate=True):
@@ -1971,6 +2014,7 @@
timestamp = int(time.time())
new_issue.opened_timestamp = timestamp
new_issue.modified_timestamp = timestamp
+ new_issue.migration_modified_timestamp = timestamp
target_comments = self.GetCommentsForIssue(cnxn, target_issue.issue_id)
initial_summary_comment = target_comments[0]
@@ -2659,8 +2703,9 @@
hotlist = self.test_hotlists.get((name, owner_id))
if hotlist:
if not hotlist.owner_ids: # Should never happen.
- logging.warn('Unowned Hotlist: id:%r, name:%r',
- hotlist.hotlist_id, hotlist.name)
+ logging.warning(
+ 'Unowned Hotlist: id:%r, name:%r', hotlist.hotlist_id,
+ hotlist.name)
continue
id_dict[(name.lower(), owner_id)] = hotlist.hotlist_id
return id_dict
@@ -2790,7 +2835,7 @@
emails = user_ids_by_email.keys()
user_ids = user_ids_by_email.values()
project_rules_dict = collections.defaultdict(list)
- for project_id, rules in self.test_rules.iteritems():
+ for project_id, rules in self.test_rules.items():
for rule in rules:
if rule.default_owner_id in user_ids:
project_rules_dict[project_id].append(rule)
diff --git a/testing/test/fake_test.py b/testing/test/fake_test.py
index c098236..a710328 100644
--- a/testing/test/fake_test.py
+++ b/testing/test/fake_test.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""Tests for the fake module."""
from __future__ import print_function
@@ -9,6 +8,7 @@
from __future__ import absolute_import
import inspect
+import six
import unittest
from services import cachemanager_svc
@@ -44,9 +44,12 @@
to_test = [x for x in both_attrs if '__' not in x]
for name in to_test:
real_attr = getattr(real_cls, name)
- assert inspect.ismethod(real_attr)
- real_spec = inspect.getargspec(real_attr)
- fake_spec = inspect.getargspec(getattr(fake_cls, name))
+ if six.PY2:
+ assert inspect.ismethod(real_attr)
+ else:
+ assert inspect.isfunction(real_attr)
+ real_spec = inspect.getfullargspec(real_attr)
+ fake_spec = inspect.getfullargspec(getattr(fake_cls, name))
# check same number of args and kwargs
real_kw_len = len(real_spec[3]) if real_spec[3] else 0
fake_kw_len = len(fake_spec[3]) if fake_spec[3] else 0
diff --git a/testing/test/testing_helpers_test.py b/testing/test/testing_helpers_test.py
index 7493b04..6e62f4e 100644
--- a/testing/test/testing_helpers_test.py
+++ b/testing/test/testing_helpers_test.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""Tests for the testing_helpers module."""
from __future__ import print_function
diff --git a/testing/testing_helpers.py b/testing/testing_helpers.py
index 097275a..70c40fd 100644
--- a/testing/testing_helpers.py
+++ b/testing/testing_helpers.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
"""Helpers for testing."""
from __future__ import print_function
@@ -9,15 +8,15 @@
from __future__ import absolute_import
import email
+from six.moves import urllib
from framework import emailfmt
from framework import framework_bizobj
-from proto import user_pb2
+from mrproto import user_pb2
from services import service_manager
from services import template_svc
from testing import fake
from tracker import tracker_constants
-import webapp2
DEFAULT_HOST = '127.0.0.1'
@@ -71,7 +70,7 @@
def GetRequestObjects(
- headers=None, path='/', params=None, payload=None, user_info=None,
+ headers=None, path='/', params=None, user_info=None,
project=None, method='GET', perms=None, services=None, hotlist=None):
"""Make fake request and MonorailRequest objects for testing.
@@ -98,9 +97,7 @@
headers.setdefault('Host', DEFAULT_HOST)
post_items=None
- if method == 'POST' and payload:
- post_items = payload
- elif method == 'POST' and params:
+ if method == 'POST' and params:
post_items = params
if not services:
@@ -112,7 +109,7 @@
services.project.TestAddProject('proj')
services.features.TestAddHotlist('hotlist')
- request = webapp2.Request.blank(path, headers=headers, POST=post_items)
+ request = RequestStub(path, headers=headers, values=post_items)
mr = fake.MonorailRequest(
services, user_info=user_info, project=project, perms=perms,
params=params, hotlist=hotlist)
@@ -122,6 +119,33 @@
return request, mr
+class RequestStub(object):
+ """flask.Request stub object.
+
+ This stub is a drop-in replacement for flask.Request that implements all
+ fields used in MonorailRequest.ParseRequest(). Its constructor API is
+ designed to mimic webapp2.Request.blank() for backwards compatibility with
+ existing unit tests previously written for webapp2.
+ """
+
+ def __init__(self, path, headers=None, values=None):
+ self.scheme = 'http'
+ self.path = path
+ self.headers = headers or {}
+ # webapp2.Request.blank() overrides the host from the request headers.
+ self.host = self.headers.get('Host', 'localhost:80')
+ self.host_url = self.scheme + '://' + self.host + '/'
+ self.url = self.scheme + '://' + self.host + path
+
+ parsed_url = urllib.parse.urlsplit(self.url)
+ self.base_url = self.host_url + parsed_url.path # No query string.
+
+ self.values = values or {}
+ # webapp2.Request.blank() parses the query string from the path.
+ query = urllib.parse.parse_qs(parsed_url.query, True)
+ self.values.update({key: value[0] for key, value in query.items()})
+
+
class Blank(object):
"""Simple class that assigns all named args to attributes.