Title: [288038] trunk/Tools
Revision
288038
Author
[email protected]
Date
2022-01-14 16:48:46 -0800 (Fri, 14 Jan 2022)

Log Message

[webkitbugspy] Support radar as issue tracker type
https://bugs.webkit.org/show_bug.cgi?id=234993
<rdar://problem/87276178>

Reviewed by Stephanie Lewis.

* Tools/Scripts/libraries/webkitbugspy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/__init__.py: Ditto.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/__init__.py:
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/base.py:
(Base): Radar mock should not inherit from mocks.Requests.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/bugzilla.py:
(Bugzilla): Inherit from both Base and mocks.Requests.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/github.py:
(GitHub): Inherit from both Base and mocks.Requests.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/radar.py: Added.
(AppleDirectoryUserEntry): Mock AppleDirectoryUserEntry from radarclient.
(AppleDirectoryQuery): Mock AppleDirectoryQuery from radarclient.
(RadarModel): Mock various Model objects from radarclient.
(RadarClient): Mock RadarClient from radarclient.
(Radar): Mock radarclient library.
(NoRadar): Mock unavailable radarclient library.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/radar.py: Added.
(Tracker):
(Tracker.radarclient): Optionally import radarclient.
(Tracker.__init__):
(Tracker.authentication): Determine which authentication strategy to use.
(Tracker.from_string): Construct issue given Radar string
(Tracker.user): Find user given name, DSID or email.
(Tracker.issue): Construct issue given Radar ID.
(Tracker.populate): Populate Issue arguments.
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tests/radar_unittest.py: Added.
(TestGitHub):
(TestGitHub.test_no_radar):
(TestGitHub.test_users):
(TestGitHub.test_link):
(TestGitHub.test_title):
(TestGitHub.test_timestamp):
(TestGitHub.test_creator):
(TestGitHub.test_description):
(TestGitHub.test_assignee):
(TestGitHub.test_comments):
(TestGitHub.test_watchers):
(TestGitHub.test_references):
(TestGitHub.test_reference_parse):
* Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tracker.py:
(Tracker): Match single integer issue URLs.

Canonical link: https://commits.webkit.org/246064@main

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (288037 => 288038)


--- trunk/Tools/ChangeLog	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/ChangeLog	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,3 +1,53 @@
+2022-01-07  Jonathan Bedard  <[email protected]>
+
+        [webkitbugspy] Support radar as issue tracker type
+        https://bugs.webkit.org/show_bug.cgi?id=234993
+        <rdar://problem/87276178>
+
+        Reviewed by Stephanie Lewis.
+
+        * Scripts/libraries/webkitbugspy/setup.py: Bump version.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/__init__.py: Ditto.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/mocks/__init__.py:
+        * Scripts/libraries/webkitbugspy/webkitbugspy/mocks/base.py:
+        (Base): Radar mock should not inherit from mocks.Requests.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/mocks/bugzilla.py:
+        (Bugzilla): Inherit from both Base and mocks.Requests.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/mocks/github.py:
+        (GitHub): Inherit from both Base and mocks.Requests.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/mocks/radar.py: Added.
+        (AppleDirectoryUserEntry): Mock AppleDirectoryUserEntry from radarclient.
+        (AppleDirectoryQuery): Mock AppleDirectoryQuery from radarclient.
+        (RadarModel): Mock various Model objects from radarclient.
+        (RadarClient): Mock RadarClient from radarclient.
+        (Radar): Mock radarclient library.
+        (NoRadar): Mock unavailable radarclient library.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/radar.py: Added.
+        (Tracker):
+        (Tracker.radarclient): Optionally import radarclient.
+        (Tracker.__init__):
+        (Tracker.authentication): Determine which authentication strategy to use.
+        (Tracker.from_string): Construct issue given Radar string
+        (Tracker.user): Find user given name, DSID or email.
+        (Tracker.issue): Construct issue given Radar ID.
+        (Tracker.populate): Populate Issue arguments.
+        * Scripts/libraries/webkitbugspy/webkitbugspy/tests/radar_unittest.py: Added.
+        (TestGitHub):
+        (TestGitHub.test_no_radar):
+        (TestGitHub.test_users):
+        (TestGitHub.test_link):
+        (TestGitHub.test_title):
+        (TestGitHub.test_timestamp):
+        (TestGitHub.test_creator):
+        (TestGitHub.test_description):
+        (TestGitHub.test_assignee):
+        (TestGitHub.test_comments):
+        (TestGitHub.test_watchers):
+        (TestGitHub.test_references):
+        (TestGitHub.test_reference_parse):
+        * Scripts/libraries/webkitbugspy/webkitbugspy/tracker.py:
+        (Tracker): Match single integer issue URLs.
+
 2022-01-14  Wenson Hsieh  <[email protected]>
 
         Avoid redundant text analysis requests when long pressing inside an image that contains Live Text

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/setup.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/setup.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/setup.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -30,7 +30,7 @@
 
 setup(
     name='webkitbugspy',
-    version='0.0.1',
+    version='0.0.2',
     description='Library containing a shared API for various bug trackers.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/__init__.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/__init__.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/__init__.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -46,7 +46,7 @@
         "Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
     )
 
-version = Version(0, 0, 1)
+version = Version(0, 0, 2)
 
 from .user import User
 from .issue import Issue

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/__init__.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/__init__.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/__init__.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -23,3 +23,4 @@
 from .bugzilla import Bugzilla
 from .data import ISSUES, USERS
 from .github import GitHub
+from .radar import Radar, NoRadar

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/base.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/base.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/base.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -21,10 +21,9 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from webkitbugspy import User
-from webkitcorepy import mocks
 
 
-class Base(mocks.Requests):
+class Base(object):
     @classmethod
     def transform_user(cls, user):
         return User(
@@ -33,12 +32,7 @@
             emails=user.emails,
         )
 
-    def __init__(self, *hosts, **kwargs):
-        super(Base, self).__init__(*hosts)
-
-        users = kwargs.get('users', None)
-        issues = kwargs.get('issues', None)
-
+    def __init__(self, users=None, issues=None):
         self.users = User.Mapping()
         for user in users or []:
             self.users.add(type(self).transform_user(user))

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/bugzilla.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/bugzilla.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/bugzilla.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -29,7 +29,7 @@
 from webkitcorepy import mocks
 
 
-class Bugzilla(Base):
+class Bugzilla(Base, mocks.Requests):
     top = None
 
     @classmethod
@@ -46,7 +46,8 @@
         )
 
     def __init__(self, hostname='bugs.example.com', users=None, issues=None):
-        super(Bugzilla, self).__init__(hostname, users=users, issues=issues)
+        Base.__init__(self, users=users, issues=issues)
+        mocks.Requests.__init__(self, hostname)
 
     def _user(self, url, username):
         user = self.users[username]

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/github.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/github.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/github.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -30,7 +30,7 @@
 from webkitcorepy import mocks
 
 
-class GitHub(Base):
+class GitHub(Base, mocks.Requests):
     top = None
 
     @classmethod
@@ -50,7 +50,8 @@
         hostname, repo = hostname.split('/', 1)
         self.api_host = 'api.{hostname}/repos/{repo}'.format(hostname=hostname, repo=repo)
 
-        super(GitHub, self).__init__(hostname, 'api.{}'.format(hostname), users=users, issues=issues)
+        Base.__init__(self, users=users, issues=issues)
+        mocks.Requests.__init__(self, hostname, 'api.{}'.format(hostname))
 
         self._environment = None
 

Added: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/radar.py (0 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/radar.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/mocks/radar.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -0,0 +1,184 @@
+# Copyright (C) 2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from .base import Base
+
+from webkitbugspy import User
+from webkitcorepy import string_utils
+from webkitcorepy.mocks import ContextStack
+
+
+class AppleDirectoryUserEntry(object):
+    def __init__(self, user):
+        self.first_name = lambda: user.name.split(' ', 1)[0]
+        self.last_name = lambda: user.name.split(' ', 1)[1]
+        self.email = lambda: user.email
+        self.dsid = lambda: user.username
+
+
+class AppleDirectoryQuery(object):
+    def __init__(self, parent):
+        self.parent = parent
+
+    def user_entry_for_dsid(self, dsid):
+        return self.user_entry_for_attribute_value('dsid', dsid)
+
+    def user_entry_for_attribute_value(self, name, value):
+        if name not in ('cn', 'dsid', 'mail'):
+            raise ValueError("'{}' is not a valid user attribute value".format(name))
+        found = self.parent.users.get(value)
+        if not found:
+            return None
+        if name == 'cn' and value != found.name:
+            return None
+        if name == 'dsid' and value != found.username:
+            return None
+        if name == 'mail' and value not in found.emails:
+            return None
+        return AppleDirectoryUserEntry(found)
+
+
+class RadarModel(object):
+    class Person(object):
+        def __init__(self, user):
+            self.firstName = user.name.split(' ', 1)[0]
+            self.lastName = user.name.split(' ', 1)[1]
+            self.email = user.email
+            self.dsid = user.username
+
+    class CCMembership(object):
+        def __init__(self, user):
+            self.person = RadarModel.Person(user)
+
+    class CollectionProperty(object):
+        def __init__(self, *properties):
+            self._properties = properties
+
+        def items(self, type=None):
+            for property in self._properties:
+                yield property
+
+    class DescriptionEntry(object):
+        def __init__(self, text):
+            self.text = text
+
+    class CommentAuthor(object):
+        def __init__(self, user):
+            self.name = user.name
+            self.email = user.email
+
+    class DiagnosisEntry(object):
+        def __init__(self, text, addedAt, addedBy):
+            self.text = text
+            self.addedAt = addedAt
+            self.addedBy = addedBy
+
+    def __init__(self, client, issue):
+        from datetime import datetime, timedelta
+
+        self.client = client
+        self._issue = issue
+        self.title = issue['title']
+        self.id = issue['id']
+        self.createdAt = datetime.utcfromtimestamp(issue['timestamp'] - timedelta(hours=7).seconds)
+        self.assignee = self.Person(Radar.transform_user(issue['assignee']))
+        self.description = self.CollectionProperty(self.DescriptionEntry(issue['description']))
+        self.state = 'Investigate' if issue['opened'] else 'Verify'
+        self.originator = self.Person(Radar.transform_user(issue['creator']))
+        self.diagnosis = self.CollectionProperty(*[
+            self.DiagnosisEntry(
+                text=comment.content,
+                addedAt=datetime.utcfromtimestamp(comment.timestamp - timedelta(hours=7).seconds),
+                addedBy=self.CommentAuthor(Radar.transform_user(comment.user)),
+            ) for comment in issue.get('comments', [])
+        ])
+        self.cc_memberships = self.CollectionProperty(*[
+            self.CCMembership(Radar.transform_user(watcher)) for watcher in issue.get('watchers', [])
+        ])
+
+    def related_radars(self):
+        for reference in self._issue.get('references', []):
+            ref = self.client.radar_for_id(reference)
+            if ref:
+                yield ref
+
+
+class RadarClient(object):
+    def __init__(self, parent):
+        self.parent = parent
+
+    def radar_for_id(self, problem_id):
+        found = self.parent.issues.get(problem_id)
+        if not found:
+            return None
+        return RadarModel(self, found)
+
+
+class Radar(Base, ContextStack):
+    top = None
+
+    class AuthenticationStrategySystemAccount(object):
+        def __init__(self, _, __, ___, ____):
+            pass
+
+    class AuthenticationStrategySPNego(object):
+        pass
+
+    class ClientSystemIdentifier(object):
+        def __init__(self, name, version):
+            pass
+
+    @classmethod
+    def transform_user(cls, user):
+        return User(
+            name=user.name,
+            username=sum(bytearray(string_utils.encode(user.email))) % 1000,
+            emails=user.emails,
+        )
+
+    def __init__(self, users=None, issues=None):
+        Base.__init__(self, users=users, issues=issues)
+        ContextStack.__init__(self, Radar)
+
+        self.users = User.Mapping()
+        for name in sorted([user.name for user in users or []]):
+            self.users.add(self.transform_user(users[name]))
+
+        self.issues = {}
+        for issue in issues or []:
+            self.add(issue)
+
+        self.AppleDirectoryQuery = AppleDirectoryQuery(self)
+        self.RadarClient = lambda authentication_strategy, client_system_identifier: RadarClient(self)
+
+        from mock import patch
+        self.patches.append(patch('webkitbugspy.radar.Tracker.radarclient', new=lambda s=None: self))
+
+
+class NoRadar(ContextStack):
+    top = None
+
+    def __init__(self):
+        super(NoRadar, self).__init__(NoRadar)
+
+        from mock import patch
+        self.patches.append(patch('webkitbugspy.radar.Tracker.radarclient', new=lambda s=None: None))

Added: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/radar.py (0 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/radar.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/radar.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -0,0 +1,176 @@
+# Copyright (C) 2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import calendar
+import re
+import sys
+
+from webkitcorepy import Environment
+from webkitbugspy import Issue, Tracker as GenericTracker, User, name as library_name, version as library_version
+
+
+class Tracker(GenericTracker):
+    RES = [
+        re.compile(r'<?rdar://problem/(?P<id>\d+)>?'),
+        re.compile(r'<?radar://problem/(?P<id>\d+)>?'),
+        re.compile(r'<?rdar:\/\/(?P<id>\d+)>?'),
+        re.compile(r'<?radar:\/\/(?P<id>\d+)>?'),
+    ]
+
+    @staticmethod
+    def radarclient():
+        try:
+            import radarclient
+            return radarclient
+        except ImportError:
+            return None
+
+    def __init__(self, users=None, authentication=None):
+        super(Tracker, self).__init__(users=users)
+
+        self.library = self.radarclient()
+        if self.library:
+            self.client = self.library.RadarClient(
+                authentication or self.authentication(),
+                self.library.ClientSystemIdentifier(library_name, str(library_version)),
+            )
+        else:
+            self.client = None
+
+    def authentication(self):
+        username = Environment.instance().get('RADAR_USERNAME')
+        password = Environment.instance().get('RADAR_PASSWORD')
+        totp_secret = Environment.instance().get('RADAR_TOTP_SECRET')
+        totp_id = Environment.instance().get('RADAR_TOTP_ID') or 1
+
+        if username and password and totp_secret and totp_id:
+            return self.library.AuthenticationStrategySystemAccount(
+                username, password, totp_secret, totp_id,
+            )
+        return self.library.AuthenticationStrategySPNego()
+
+    def from_string(self, string):
+        for regex in self.RES:
+            match = regex.match(string)
+            if match:
+                return self.issue(int(match.group('id')))
+        return None
+
+    def user(self, name=None, username=None, email=None):
+        user = super(Tracker, self).user(name=name, username=username, email=email)
+        if user:
+            return user
+        if not name or not username or not email:
+            found = None
+            if username:
+                found = self.library.AppleDirectoryQuery.user_entry_for_dsid(int(username))
+            elif email:
+                found = self.library.AppleDirectoryQuery.user_entry_for_attribute_value('mail', email)
+            elif name:
+                found = self.library.AppleDirectoryQuery.user_entry_for_attribute_value('cn', name)
+            if not found:
+                raise RuntimeError("Failed to find '{}'".format(User(
+                    name, username, [email],
+                )))
+            name = '{} {}'.format(found.first_name(), found.last_name())
+            username = found.dsid()
+            email = found.email()
+        return self.users.create(
+            name=name,
+            username=username,
+            emails=[email],
+        )
+
+    def issue(self, id):
+        return Issue(id=int(id), tracker=self)
+
+    def populate(self, issue, member=None):
+        issue._link = '<rdar://{}>'.format(issue.id)
+        if (not self.client or not self.library) and member:
+            sys.stderr.write('radarclient inaccessible on this machine\n')
+            return issue
+
+        if not member:
+            return issue
+
+        radar = self.client.radar_for_id(issue.id)
+        if not radar:
+            sys.stderr.write("Failed to fetch '{}'\n".format(issue.link))
+            return issue
+
+        issue._title = radar.title
+        issue._timestamp = int(calendar.timegm(radar.createdAt.timetuple()))
+        issue._assignee = self.user(
+            name='{} {}'.format(radar.assignee.firstName, radar.assignee.lastName),
+            username=radar.assignee.dsid,
+            email=radar.assignee.email,
+        )
+        issue._description = '\n'.join([desc.text for desc in radar.description.items()])
+        issue._opened = False if radar.state in ('Verify', 'Closed') else True
+        issue._creator = self.user(
+            name='{} {}'.format(radar.originator.firstName, radar.originator.lastName),
+            username=radar.originator.dsid,
+            email=radar.originator.email,
+        )
+
+        if member == 'watchers':
+            issue._watchers = []
+            for member in radar.cc_memberships.items():
+                if member.person.dsid == radar.originator.dsid:
+                    continue
+                issue._watchers.append(self.user(
+                    name='{} {}'.format(member.person.firstName, member.person.lastName),
+                    username=member.person.dsid,
+                    email=member.person.email,
+                ))
+
+        if member == 'comments':
+            issue._comments = []
+            for item in radar.diagnosis.items(type='user'):
+                issue._comments.append(Issue.Comment(
+                    user=self.user(
+                        name=item.addedBy.name,
+                        email=item.addedBy.email,
+                    ), timestamp=int(calendar.timegm(item.addedAt.timetuple())),
+                    content=item.text,
+                ))
+
+        if member == 'references':
+            issue._references = []
+            refs = set()
+
+            for text in [issue.description] + [comment.content for comment in issue.comments]:
+                for match in self.REFERENCE_RE.findall(text):
+                    candidate = GenericTracker.from_string(match[0]) or self.from_string(match[0])
+                    if not candidate or candidate.link in refs or candidate.id == issue.id:
+                        continue
+                    issue._references.append(candidate)
+                    refs.add(candidate.link)
+
+            for r in radar.related_radars():
+                candidate = self.issue(r.id)
+                if candidate.link in refs or candidate.id == issue.id:
+                    continue
+                issue._references.append(candidate)
+                refs.add(candidate.link)
+
+        return issue

Added: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tests/radar_unittest.py (0 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tests/radar_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tests/radar_unittest.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -0,0 +1,136 @@
+# Copyright (C) 2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitbugspy import Issue, User, radar, mocks
+
+
+class TestGitHub(unittest.TestCase):
+    def test_no_radar(self):
+        with mocks.NoRadar():
+            tracker = radar.Tracker()
+            self.assertIsNone(tracker.library)
+            self.assertIsNone(tracker.client)
+
+    def test_users(self):
+        with mocks.Radar(users=mocks.USERS):
+            tracker = radar.Tracker()
+            self.assertEqual(
+                User.Encoder().default(tracker.user(username=504)),
+                dict(name='Tim Contributor', username=504, emails=['[email protected]']),
+            )
+            self.assertEqual(
+                User.Encoder().default(tracker.user(email='[email protected]')),
+                dict(name='Tim Contributor', username=504, emails=['[email protected]']),
+            )
+            self.assertEqual(
+                User.Encoder().default(tracker.user(name='Felix Filer')),
+                dict(name='Felix Filer', username=809, emails=['[email protected]']),
+            )
+
+    def test_link(self):
+        with mocks.Radar(users=mocks.USERS):
+            tracker = radar.Tracker()
+            self.assertEqual(tracker.issue(1234).link, '<rdar://1234>')
+            self.assertEqual(
+                tracker.from_string('<rdar://problem/1234>').link,
+                '<rdar://1234>',
+            )
+            self.assertEqual(
+                tracker.from_string('<radar://1234>').link,
+                '<rdar://1234>',
+            )
+            self.assertEqual(
+                tracker.from_string('<radar://problem/1234>').link,
+                '<rdar://1234>',
+            )
+
+    def test_title(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            tracker = radar.Tracker()
+            self.assertEqual(tracker.issue(1).title, 'Example issue 1')
+            self.assertEqual(str(tracker.issue(1)), '<rdar://1> Example issue 1')
+
+    def test_timestamp(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            self.assertEqual(radar.Tracker().issue(1).timestamp, 1639510960)
+
+    def test_creator(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            self.assertEqual(
+                User.Encoder().default(radar.Tracker().issue(1).creator),
+                dict(name='Felix Filer', username=809, emails=['[email protected]']),
+            )
+
+    def test_description(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            self.assertEqual(
+                radar.Tracker().issue(1).description,
+                'An example issue for testing',
+            )
+
+    def test_assignee(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            self.assertEqual(
+                User.Encoder().default(radar.Tracker().issue(1).assignee),
+                dict(name='Tim Contributor', username=504, emails=['[email protected]']),
+            )
+
+    def test_comments(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            comments = radar.Tracker().issue(1).comments
+            self.assertEqual(len(comments), 2)
+            self.assertEqual(comments[0].timestamp, 1639511020)
+            self.assertEqual(comments[0].content, 'Was able to reproduce on version 1.2.3')
+            self.assertEqual(
+                User.Encoder().default(comments[0].user),
+                dict(name='Felix Filer', username=809, emails=['[email protected]']),
+            )
+
+    def test_watchers(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            self.assertEqual(
+                User.Encoder().default(radar.Tracker().issue(1).watchers), [
+                    dict(name='Tim Contributor', username=504, emails=['[email protected]']),
+                    dict(name='Wilma Watcher', username=46, emails=['[email protected]']),
+                ],
+            )
+
+    def test_references(self):
+        with mocks.Radar(issues=mocks.ISSUES):
+            tracker = radar.Tracker()
+            self.assertEqual(tracker.issue(1).references, [])
+            self.assertEqual(tracker.issue(2).references, [tracker.issue(3)])
+            self.assertEqual(tracker.issue(3).references, [tracker.issue(2)])
+
+    def test_reference_parse(self):
+        with mocks.Radar(issues=mocks.ISSUES) as mock:
+            tracker = radar.Tracker()
+            mock.issues[1]['comments'].append(
+                Issue.Comment(
+                    user=mocks.USERS['Wilma Watcher'],
+                    timestamp=1639539630,
+                    content='Is this related to <rdar://2> ?',
+                ),
+            )
+            self.assertEqual(tracker.issue(1).references, [tracker.issue(2)])

Modified: trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tracker.py (288037 => 288038)


--- trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tracker.py	2022-01-15 00:44:17 UTC (rev 288037)
+++ trunk/Tools/Scripts/libraries/webkitbugspy/webkitbugspy/tracker.py	2022-01-15 00:48:46 UTC (rev 288038)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -28,7 +28,7 @@
 
 
 class Tracker(object):
-    REFERENCE_RE = re.compile(r'((https|http|rdar|radar)://[^\s><,\'\"}{\]\[)(]+[^\s><,\'\"}{\]\[)(\.\?])')
+    REFERENCE_RE = re.compile(r'((https|http|rdar|radar)://[^\s><,\'\"}{\]\[)(]*[^\s><,\'\"}{\]\[)(\.\?])')
 
     _trackers = []
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to