| # 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. |
| |
| """Unittest for the autolink feature.""" |
| from __future__ import print_function |
| from __future__ import division |
| from __future__ import absolute_import |
| |
| import re |
| import unittest |
| |
| from features import autolink |
| from features import autolink_constants |
| from framework import template_helpers |
| from mrproto import tracker_pb2 |
| from testing import fake |
| from testing import testing_helpers |
| |
| |
| SIMPLE_EMAIL_RE = re.compile(r'([a-z]+)@([a-z]+)\.com') |
| OVER_AMBITIOUS_DOMAIN_RE = re.compile(r'([a-z]+)\.(com|net|org)') |
| |
| |
| class AutolinkTest(unittest.TestCase): |
| |
| def RegisterEmailCallbacks(self, aa): |
| |
| def LookupUsers(_mr, all_addresses): |
| """Return user objects for only users who are at trusted domains.""" |
| return [addr for addr in all_addresses |
| if addr.endswith('@example.com')] |
| |
| def Match2Addresses(_mr, match): |
| return [match.group(0)] |
| |
| def MakeMailtoLink(_mr, match, comp_ref_artifacts): |
| email = match.group(0) |
| if comp_ref_artifacts and email in comp_ref_artifacts: |
| return [template_helpers.TextRun( |
| tag='a', href='mailto:%s' % email, content=email)] |
| else: |
| return [template_helpers.TextRun('%s AT %s.com' % match.group(1, 2))] |
| |
| aa.RegisterComponent('testcomp', |
| LookupUsers, |
| Match2Addresses, |
| {SIMPLE_EMAIL_RE: MakeMailtoLink}) |
| |
| def RegisterDomainCallbacks(self, aa): |
| |
| def LookupDomains(_mr, _all_refs): |
| """Return business objects for only real domains. Always just True.""" |
| return True # We don't have domain business objects, accept anything. |
| |
| def Match2Domains(_mr, match): |
| return [match.group(0)] |
| |
| def MakeHyperLink(_mr, match, _comp_ref_artifacts): |
| domain = match.group(0) |
| return [template_helpers.TextRun(tag='a', href=domain, content=domain)] |
| |
| aa.RegisterComponent('testcomp2', |
| LookupDomains, |
| Match2Domains, |
| {OVER_AMBITIOUS_DOMAIN_RE: MakeHyperLink}) |
| |
| def setUp(self): |
| self.aa = autolink.Autolink() |
| self.RegisterEmailCallbacks(self.aa) |
| self.comment1 = ('Feel free to contact me at a@other.com, ' |
| 'or b@example.com, or c@example.org.') |
| self.comment2 = 'no matches in this comment' |
| self.comment3 = 'just matches with no ref: a@other.com, c@example.org' |
| self.comments = [self.comment1, self.comment2, self.comment3] |
| |
| def testRegisterComponent(self): |
| self.assertIn('testcomp', self.aa.registry) |
| |
| def testGetAllReferencedArtifacts(self): |
| all_ref_artifacts = self.aa.GetAllReferencedArtifacts( |
| None, self.comments) |
| |
| self.assertIn('testcomp', all_ref_artifacts) |
| comp_refs = all_ref_artifacts['testcomp'] |
| self.assertIn('b@example.com', comp_refs) |
| self.assertTrue(len(comp_refs) == 1) |
| |
| def testGetAllReferencedArtifacts_TooBig(self): |
| all_ref_artifacts = self.aa.GetAllReferencedArtifacts( |
| None, self.comments, max_total_length=10) |
| |
| self.assertEqual(autolink.SKIP_LOOKUPS, all_ref_artifacts) |
| |
| def testMarkupAutolinks(self): |
| all_ref_artifacts = self.aa.GetAllReferencedArtifacts(None, self.comments) |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment1)], all_ref_artifacts) |
| self.assertEqual('Feel free to contact me at ', result[0].content) |
| self.assertEqual('a AT other.com', result[1].content) |
| self.assertEqual(', or ', result[2].content) |
| self.assertEqual('b@example.com', result[3].content) |
| self.assertEqual('mailto:b@example.com', result[3].href) |
| self.assertEqual(', or c@example.org.', result[4].content) |
| |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment2)], all_ref_artifacts) |
| self.assertEqual('no matches in this comment', result[0].content) |
| |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment3)], all_ref_artifacts) |
| self.assertEqual('just matches with no ref: ', result[0].content) |
| self.assertEqual('a AT other.com', result[1].content) |
| self.assertEqual(', c@example.org', result[2].content) |
| |
| def testNonnestedAutolinks(self): |
| """Test that when a substitution yields plain text, others are applied.""" |
| self.RegisterDomainCallbacks(self.aa) |
| all_ref_artifacts = self.aa.GetAllReferencedArtifacts(None, self.comments) |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment1)], all_ref_artifacts) |
| self.assertEqual('Feel free to contact me at ', result[0].content) |
| self.assertEqual('a AT ', result[1].content) |
| self.assertEqual('other.com', result[2].content) |
| self.assertEqual('other.com', result[2].href) |
| self.assertEqual(', or ', result[3].content) |
| self.assertEqual('b@example.com', result[4].content) |
| self.assertEqual('mailto:b@example.com', result[4].href) |
| self.assertEqual(', or c@', result[5].content) |
| self.assertEqual('example.org', result[6].content) |
| self.assertEqual('example.org', result[6].href) |
| self.assertEqual('.', result[7].content) |
| |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment2)], all_ref_artifacts) |
| self.assertEqual('no matches in this comment', result[0].content) |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment3)], all_ref_artifacts) |
| self.assertEqual('just matches with no ref: ', result[0].content) |
| self.assertEqual('a AT ', result[1].content) |
| self.assertEqual('other.com', result[2].content) |
| self.assertEqual('other.com', result[2].href) |
| self.assertEqual(', c@', result[3].content) |
| self.assertEqual('example.org', result[4].content) |
| self.assertEqual('example.org', result[4].href) |
| |
| def testMarkupAutolinks_TooBig(self): |
| """If the issue has too much text, we just do regex-based autolinking.""" |
| all_ref_artifacts = self.aa.GetAllReferencedArtifacts( |
| None, self.comments, max_total_length=10) |
| result = self.aa.MarkupAutolinks( |
| None, [template_helpers.TextRun(self.comment1)], all_ref_artifacts) |
| self.assertEqual(5, len(result)) |
| self.assertEqual('Feel free to contact me at ', result[0].content) |
| # The test autolink handlers in this file do not link email addresses. |
| self.assertEqual('a AT other.com', result[1].content) |
| self.assertIsNone(result[1].href) |
| |
| class EmailAutolinkTest(unittest.TestCase): |
| |
| def setUp(self): |
| self.user_1 = 'fake user' # Note: no User fields are accessed. |
| |
| def DoLinkify( |
| self, content, filter_re=autolink_constants.IS_IMPLIED_EMAIL_RE): |
| """Calls the LinkifyEmail method and returns the result. |
| |
| Args: |
| content: string with a hyperlink. |
| |
| Returns: |
| A list of TextRuns with some runs having the embedded email hyperlinked. |
| Or, None if no link was detected. |
| """ |
| match = filter_re.search(content) |
| if not match: |
| return None |
| |
| return autolink.LinkifyEmail(None, match, {'one@example.com': self.user_1}) |
| |
| def testLinkifyEmail(self): |
| """Test that an address is autolinked when put in the given context.""" |
| test = 'one@ or @one' |
| result = self.DoLinkify('Have you met %s' % test) |
| self.assertEqual(None, result) |
| |
| test = 'one@example.com' |
| result = self.DoLinkify('Have you met %s' % test) |
| self.assertEqual('/u/' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'alias@example.com' |
| result = self.DoLinkify('Please also CC %s' % test) |
| self.assertEqual('mailto:' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| result = self.DoLinkify('Reviewed-By: Test Person <%s>' % test) |
| self.assertEqual('mailto:' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| |
| class URLAutolinkTest(unittest.TestCase): |
| |
| def DoLinkify(self, content, filter_re=autolink_constants.IS_A_LINK_RE): |
| """Calls the linkify method and returns the result. |
| |
| Args: |
| content: string with a hyperlink. |
| |
| Returns: |
| A list of TextRuns with some runs will have the embedded URL hyperlinked. |
| Or, None if no link was detected. |
| """ |
| match = filter_re.search(content) |
| if not match: |
| return None |
| |
| return autolink.Linkify(None, match, None) |
| |
| def testLinkify(self): |
| """Test that given url is autolinked when put in the given context.""" |
| # Disallow the linking of URLs with user names and passwords. |
| test = 'http://user:pass@www.yahoo.com' |
| result = self.DoLinkify('What about %s' % test) |
| self.assertEqual(None, result[0].tag) |
| self.assertEqual(None, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # Disallow the linking of non-HTTP(S) links |
| test = 'nntp://news.google.com' |
| result = self.DoLinkify('%s' % test) |
| self.assertEqual(None, result) |
| |
| # Disallow the linking of file links |
| test = 'file://C:/Windows/System32/cmd.exe' |
| result = self.DoLinkify('%s' % test) |
| self.assertEqual(None, result) |
| |
| # Test some known URLs |
| test = 'http://www.example.com' |
| result = self.DoLinkify('What about %s' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| def testLinkify_FTP(self): |
| """Test that FTP urls are linked.""" |
| # Check for a standard ftp link |
| test = 'ftp://ftp.example.com' |
| result = self.DoLinkify('%s' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| def testLinkify_Email(self): |
| """Test that mailto: urls are linked.""" |
| test = 'mailto:user@example.com' |
| result = self.DoLinkify('%s' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| def testLinkify_ShortLink(self): |
| """Test that shortlinks are linked.""" |
| test = 'http://go/monorail' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_SHORT_LINK_RE) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'go/monorail' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_SHORT_LINK_RE) |
| self.assertEqual('http://' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'b/12345' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_NUMERIC_SHORT_LINK_RE) |
| self.assertEqual('http://' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'http://b/12345' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_NUMERIC_SHORT_LINK_RE) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = '/b/12345' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_SHORT_LINK_RE) |
| self.assertIsNone(result) |
| |
| test = '/b/12345' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_NUMERIC_SHORT_LINK_RE) |
| self.assertIsNone(result) |
| |
| test = 'b/secondFileInDiff' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_A_NUMERIC_SHORT_LINK_RE) |
| self.assertIsNone(result) |
| |
| def testLinkify_ImpliedLink(self): |
| """Test that text with .com, .org, .net, and .edu are linked.""" |
| test = 'google.org' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_IMPLIED_LINK_RE) |
| self.assertEqual('http://' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'code.google.com/p/chromium' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_IMPLIED_LINK_RE) |
| self.assertEqual('http://' + test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # This is not a domain, it is a directory or something. |
| test = 'build.out/p/chromium' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_IMPLIED_LINK_RE) |
| self.assertEqual(None, result) |
| |
| # We do not link the NNTP scheme, and the domain name part of it will not |
| # be linked as an HTTP link because it is preceeded by "/". |
| test = 'nntp://news.google.com' |
| result = self.DoLinkify( |
| '%s' % test, filter_re=autolink_constants.IS_IMPLIED_LINK_RE) |
| self.assertIsNone(result) |
| |
| def testLinkify_Context(self): |
| """Test that surrounding syntax is not considered part of the url.""" |
| test = 'http://www.example.com' |
| |
| # Check for a link followed by a comma at end of English phrase. |
| result = self.DoLinkify('The URL %s, points to a great website.' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(',', result[1].content) |
| |
| # Check for a link followed by a period at end of English sentence. |
| result = self.DoLinkify('The best site ever, %s.' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('.', result[1].content) |
| |
| # Check for a link in paranthesis (), [], or {} |
| result = self.DoLinkify('My fav site (%s).' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(').', result[1].content) |
| |
| result = self.DoLinkify('My fav site [%s].' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('].', result[1].content) |
| |
| result = self.DoLinkify('My fav site {%s}.' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('}.', result[1].content) |
| |
| # Check for a link with trailing colon |
| result = self.DoLinkify('Hit %s: you will love it.' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(':', result[1].content) |
| |
| # Check link with commas in query string, but don't include trailing comma. |
| test = 'http://www.example.com/?v=1,2,3' |
| result = self.DoLinkify('Try %s, ok?' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # Check link surrounded by angle-brackets. |
| result = self.DoLinkify('<%s>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('>', result[1].content) |
| |
| # Check link surrounded by double-quotes. |
| result = self.DoLinkify('"%s"' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('"', result[1].content) |
| |
| # Check link with embedded double-quotes. |
| test = 'http://www.example.com/?q="a+b+c"' |
| result = self.DoLinkify('Try %s, ok?' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(',', result[1].content) |
| |
| # Check link surrounded by single-quotes. |
| result = self.DoLinkify("'%s'" % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual("'", result[1].content) |
| |
| # Check link with embedded single-quotes. |
| test = "http://www.example.com/?q='a+b+c'" |
| result = self.DoLinkify('Try %s, ok?' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(',', result[1].content) |
| |
| # Check link with embedded parens. |
| test = 'http://www.example.com/funky(foo)and(bar).asp' |
| result = self.DoLinkify('Try %s, ok?' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(',', result[1].content) |
| |
| test = 'http://www.example.com/funky(foo)and(bar).asp' |
| result = self.DoLinkify('My fav site <%s>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('>', result[1].content) |
| |
| # Check link with embedded brackets and braces. |
| test = 'http://www.example.com/funky[foo]and{bar}.asp' |
| result = self.DoLinkify('My fav site <%s>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('>', result[1].content) |
| |
| # Check link with mismatched delimeters inside it or outside it. |
| test = 'http://www.example.com/funky"(foo]and>bar}.asp' |
| result = self.DoLinkify('My fav site <%s>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('>', result[1].content) |
| |
| test = 'http://www.example.com/funky"(foo]and>bar}.asp' |
| result = self.DoLinkify('My fav site {%s' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| test = 'http://www.example.com/funky"(foo]and>bar}.asp' |
| result = self.DoLinkify('My fav site %s}' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('}', result[1].content) |
| |
| # Link as part of an HTML example. |
| test = 'http://www.example.com/' |
| result = self.DoLinkify('<a href="%s">' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual('">', result[1].content) |
| |
| # Link nested in an HTML tag. |
| result = self.DoLinkify('<span>%s</span>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # Link followed by HTML tag - same bug as above. |
| result = self.DoLinkify('%s<span>foo</span>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # Link followed by unescaped HTML tag. |
| result = self.DoLinkify('%s<span>foo</span>' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| # Link surrounded by multiple delimiters. |
| result = self.DoLinkify('(e.g. <%s>)' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| result = self.DoLinkify('(e.g. <%s>),' % test) |
| self.assertEqual(test, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| def testLinkify_ContextOnBadLink(self): |
| """Test that surrounding text retained in cases where we don't link url.""" |
| test = 'http://bad=example' |
| result = self.DoLinkify('<a href="%s">' % test) |
| self.assertEqual(None, result[0].href) |
| self.assertEqual(test + '">', result[0].content) |
| self.assertEqual(1, len(result)) |
| |
| def testLinkify_UnicodeContext(self): |
| """Test that unicode context does not mess up the link.""" |
| test = 'http://www.example.com' |
| |
| # This string has a non-breaking space \xa0. |
| result = self.DoLinkify(u'The correct RFC link is\xa0%s' % test) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(test, result[0].href) |
| |
| def testLinkify_UnicodeLink(self): |
| """Test that unicode in a link is OK.""" |
| test = u'http://www.example.com?q=division\xc3\xb7sign' |
| |
| # This string has a non-breaking space \xa0. |
| result = self.DoLinkify(u'The unicode link is %s' % test) |
| self.assertEqual(test, result[0].content) |
| self.assertEqual(test, result[0].href) |
| |
| def testLinkify_LinkTextEscapingDisabled(self): |
| """Test that url-like things that miss validation aren't linked.""" |
| # Link matched by the regex but not accepted by the validator. |
| test = 'http://bad_domain/reportdetail?reportid=35aa03e04772358b' |
| result = self.DoLinkify('<span>%s</span>' % test) |
| self.assertEqual(None, result[0].href) |
| self.assertEqual(test, result[0].content) |
| |
| |
| def _Issue(project_name, local_id, summary, status): |
| issue = tracker_pb2.Issue() |
| issue.project_name = project_name |
| issue.local_id = local_id |
| issue.summary = summary |
| issue.status = status |
| return issue |
| |
| |
| class TrackerAutolinkTest(unittest.TestCase): |
| |
| COMMENT_TEXT = ( |
| 'This relates to issue 1, issue #2, and issue3 \n' |
| 'as well as bug 4, bug #5, and bug6 \n' |
| 'with issue other-project:12 and issue other-project#13. \n' |
| 'Watch out for issues 21, 22, and 23 with oxford comma. \n' |
| 'And also bugs 31, 32 and 33 with no oxford comma.\n' |
| 'Here comes crbug.com/123 and crbug.com/monorail/456.\n' |
| 'We do not match when an issue\n' |
| '999. Is split across lines.' |
| ) |
| |
| def testExtractProjectAndIssueIdNormal(self): |
| mr = testing_helpers.MakeMonorailRequest( |
| path='/p/proj/issues/detail?id=1') |
| ref_batches = [] |
| for match in autolink._ISSUE_REF_RE.finditer(self.COMMENT_TEXT): |
| new_refs = autolink.ExtractProjectAndIssueIdsNormal(mr, match) |
| ref_batches.append(new_refs) |
| |
| self.assertEqual( |
| ref_batches, [ |
| [(None, 1)], |
| [(None, 2)], |
| [(None, 3)], |
| [(None, 4)], |
| [(None, 5)], |
| [(None, 6)], |
| [('other-project', 12)], |
| [('other-project', 13)], |
| [(None, 21), (None, 22), (None, 23)], |
| [(None, 31), (None, 32), (None, 33)], |
| ]) |
| |
| |
| def testExtractProjectAndIssueIdCrbug(self): |
| mr = testing_helpers.MakeMonorailRequest( |
| path='/p/proj/issues/detail?id=1') |
| ref_batches = [] |
| for match in autolink._CRBUG_REF_RE.finditer(self.COMMENT_TEXT): |
| new_refs = autolink.ExtractProjectAndIssueIdsCrBug(mr, match) |
| ref_batches.append(new_refs) |
| |
| self.assertEqual(ref_batches, [ |
| [('chromium', 123)], |
| [('monorail', 456)], |
| ]) |
| |
| def DoReplaceIssueRef( |
| self, content, regex=autolink._ISSUE_REF_RE, |
| single_issue_regex=autolink._SINGLE_ISSUE_REF_RE, |
| default_project_name=None): |
| """Calls the ReplaceIssueRef method and returns the result. |
| |
| Args: |
| content: string that may have a textual reference to an issue. |
| regex: optional regex to use instead of _ISSUE_REF_RE. |
| |
| Returns: |
| A list of TextRuns with some runs will have the reference hyperlinked. |
| Or, None if no reference detected. |
| """ |
| match = regex.search(content) |
| if not match: |
| return None |
| |
| open_dict = {'proj:1': _Issue('proj', 1, 'summary-PROJ-1', 'New'), |
| # Assume there is no issue 3 in PROJ |
| 'proj:4': _Issue('proj', 4, 'summary-PROJ-4', 'New'), |
| 'proj:6': _Issue('proj', 6, 'summary-PROJ-6', 'New'), |
| 'other-project:12': _Issue('other-project', 12, |
| 'summary-OP-12', 'Accepted'), |
| } |
| closed_dict = {'proj:2': _Issue('proj', 2, 'summary-PROJ-2', 'Fixed'), |
| 'proj:5': _Issue('proj', 5, 'summary-PROJ-5', 'Fixed'), |
| 'other-project:13': _Issue('other-project', 13, |
| 'summary-OP-12', 'Invalid'), |
| 'chromium:13': _Issue('chromium', 13, |
| 'summary-Cr-13', 'Invalid'), |
| } |
| comp_ref_artifacts = (open_dict, closed_dict,) |
| |
| replacement_runs = autolink._ReplaceIssueRef( |
| match, comp_ref_artifacts, single_issue_regex, default_project_name) |
| return replacement_runs |
| |
| def testReplaceIssueRef_NoMatch(self): |
| result = self.DoReplaceIssueRef('What is this all about?') |
| self.assertIsNone(result) |
| |
| def testReplaceIssueRef_Normal(self): |
| result = self.DoReplaceIssueRef( |
| 'This relates to issue 1', default_project_name='proj') |
| self.assertEqual('/p/proj/issues/detail?id=1', result[0].href) |
| self.assertEqual('issue 1', result[0].content) |
| self.assertEqual(None, result[0].css_class) |
| self.assertEqual('summary-PROJ-1', result[0].title) |
| self.assertEqual('a', result[0].tag) |
| |
| result = self.DoReplaceIssueRef( |
| ', issue #2', default_project_name='proj') |
| self.assertEqual('/p/proj/issues/detail?id=2', result[0].href) |
| self.assertEqual(' issue #2 ', result[0].content) |
| self.assertEqual('closed_ref', result[0].css_class) |
| self.assertEqual('summary-PROJ-2', result[0].title) |
| self.assertEqual('a', result[0].tag) |
| |
| result = self.DoReplaceIssueRef( |
| ', and issue3 ', default_project_name='proj') |
| self.assertEqual(None, result[0].href) # There is no issue 3 |
| self.assertEqual('issue3', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'as well as bug 4', default_project_name='proj') |
| self.assertEqual('/p/proj/issues/detail?id=4', result[0].href) |
| self.assertEqual('bug 4', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| ', bug #5, ', default_project_name='proj') |
| self.assertEqual('/p/proj/issues/detail?id=5', result[0].href) |
| self.assertEqual(' bug #5 ', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'and bug6', default_project_name='proj') |
| self.assertEqual('/p/proj/issues/detail?id=6', result[0].href) |
| self.assertEqual('bug6', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'with issue other-project:12', default_project_name='proj') |
| self.assertEqual('/p/other-project/issues/detail?id=12', result[0].href) |
| self.assertEqual('issue other-project:12', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'and issue other-project#13', default_project_name='proj') |
| self.assertEqual('/p/other-project/issues/detail?id=13', result[0].href) |
| self.assertEqual(' issue other-project#13 ', result[0].content) |
| |
| def testReplaceIssueRef_CrBug(self): |
| result = self.DoReplaceIssueRef( |
| 'and crbug.com/other-project/13', regex=autolink._CRBUG_REF_RE, |
| single_issue_regex=autolink._CRBUG_REF_RE, |
| default_project_name='chromium') |
| self.assertEqual('/p/other-project/issues/detail?id=13', result[0].href) |
| self.assertEqual(' crbug.com/other-project/13 ', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'and http://crbug.com/13', regex=autolink._CRBUG_REF_RE, |
| single_issue_regex=autolink._CRBUG_REF_RE, |
| default_project_name='chromium') |
| self.assertEqual('/p/chromium/issues/detail?id=13', result[0].href) |
| self.assertEqual(' http://crbug.com/13 ', result[0].content) |
| |
| result = self.DoReplaceIssueRef( |
| 'and http://crbug.com/13#c17', regex=autolink._CRBUG_REF_RE, |
| single_issue_regex=autolink._CRBUG_REF_RE, |
| default_project_name='chromium') |
| self.assertEqual('/p/chromium/issues/detail?id=13#c17', result[0].href) |
| self.assertEqual(' http://crbug.com/13#c17 ', result[0].content) |
| |
| def testParseProjectNameMatch(self): |
| golden = 'project-name' |
| variations = ['%s', ' %s', '%s ', '%s:', '%s#', '%s#:', '%s:#', '%s :#', |
| '\t%s', '%s\t', '\t%s\t', '\t\t%s\t\t', '\n%s', '%s\n', |
| '\n%s\n', '\n\n%s\n\n', '\t\n%s', '\n\t%s', '%s\t\n', |
| '%s\n\t', '\t\n%s#', '\n\t%s#', '%s\t\n#', '%s\n\t#', |
| '\t\n%s:', '\n\t%s:', '%s\t\n:', '%s\n\t:' |
| ] |
| |
| # First pass checks all valid project name results |
| for pattern in variations: |
| self.assertEqual( |
| golden, autolink._ParseProjectNameMatch(pattern % golden)) |
| |
| # Second pass tests all inputs that should result in None |
| for pattern in variations: |
| self.assertTrue( |
| autolink._ParseProjectNameMatch(pattern % '') in [None, '']) |
| |
| |
| class VCAutolinkTest(unittest.TestCase): |
| |
| GIT_HASH_1 = '1' * 40 |
| GIT_HASH_2 = '2' * 40 |
| GIT_HASH_3 = 'a1' * 20 |
| GIT_COMMENT_TEXT = ( |
| 'This is a fix for r%s and R%s, by r2d2, who also authored revision %s, ' |
| 'revision #%s, revision %s, and revision %s' % ( |
| GIT_HASH_1, GIT_HASH_2, GIT_HASH_3, |
| GIT_HASH_1.upper(), GIT_HASH_2.upper(), GIT_HASH_3.upper())) |
| SVN_COMMENT_TEXT = ( |
| 'This is a fix for r12 and R3400, by r2d2, who also authored ' |
| 'revision r4, ' |
| 'revision #1234567, revision 789, and revision 9025. If you have ' |
| 'questions, call me at 18005551212') |
| |
| def testGetReferencedRevisions(self): |
| refs = ['1', '2', '3'] |
| # For now, we do not look up revision objects, result is always None |
| self.assertIsNone(autolink.GetReferencedRevisions(None, refs)) |
| |
| def testExtractGitHashes(self): |
| refs = [] |
| for match in autolink._GIT_HASH_RE.finditer(self.GIT_COMMENT_TEXT): |
| new_refs = autolink.ExtractRevNums(None, match) |
| refs.extend(new_refs) |
| |
| self.assertEqual( |
| refs, [ |
| self.GIT_HASH_1, self.GIT_HASH_2, self.GIT_HASH_3, |
| self.GIT_HASH_1.upper(), |
| self.GIT_HASH_2.upper(), |
| self.GIT_HASH_3.upper() |
| ]) |
| |
| def testExtractRevNums(self): |
| refs = [] |
| for match in autolink._SVN_REF_RE.finditer(self.SVN_COMMENT_TEXT): |
| new_refs = autolink.ExtractRevNums(None, match) |
| refs.extend(new_refs) |
| |
| # Note that we only autolink rNNNN with at least 4 digits. |
| self.assertEqual(refs, ['3400', '1234567', '9025']) |
| |
| |
| def DoReplaceRevisionRef(self, content, project=None): |
| """Calls the ReplaceRevisionRef method and returns the result. |
| |
| Args: |
| content: string with a hyperlink. |
| project: optional project. |
| |
| Returns: |
| A list of TextRuns with some runs will have the embedded URL hyperlinked. |
| Or, None if no link was detected. |
| """ |
| match = autolink._GIT_HASH_RE.search(content) |
| if not match: |
| return None |
| |
| mr = testing_helpers.MakeMonorailRequest( |
| path='/p/proj/source/detail?r=1', project=project) |
| replacement_runs = autolink.ReplaceRevisionRef(mr, match, None) |
| return replacement_runs |
| |
| def testReplaceRevisionRef(self): |
| result = self.DoReplaceRevisionRef( |
| 'This is a fix for r%s' % self.GIT_HASH_1) |
| self.assertEqual('https://crrev.com/%s' % self.GIT_HASH_1, result[0].href) |
| self.assertEqual('r%s' % self.GIT_HASH_1, result[0].content) |
| |
| result = self.DoReplaceRevisionRef( |
| 'and R%s, by r2d2, who ' % self.GIT_HASH_2) |
| self.assertEqual('https://crrev.com/%s' % self.GIT_HASH_2, result[0].href) |
| self.assertEqual('R%s' % self.GIT_HASH_2, result[0].content) |
| |
| result = self.DoReplaceRevisionRef('by r2d2, who ') |
| self.assertEqual(None, result) |
| |
| result = self.DoReplaceRevisionRef( |
| 'also authored revision %s, ' % self.GIT_HASH_3) |
| self.assertEqual('https://crrev.com/%s' % self.GIT_HASH_3, result[0].href) |
| self.assertEqual('revision %s' % self.GIT_HASH_3, result[0].content) |
| |
| result = self.DoReplaceRevisionRef( |
| 'revision #%s, ' % self.GIT_HASH_1.upper()) |
| self.assertEqual( |
| 'https://crrev.com/%s' % self.GIT_HASH_1.upper(), result[0].href) |
| self.assertEqual( |
| 'revision #%s' % self.GIT_HASH_1.upper(), result[0].content) |
| |
| result = self.DoReplaceRevisionRef( |
| 'revision %s, ' % self.GIT_HASH_2.upper()) |
| self.assertEqual( |
| 'https://crrev.com/%s' % self.GIT_HASH_2.upper(), result[0].href) |
| self.assertEqual('revision %s' % self.GIT_HASH_2.upper(), result[0].content) |
| |
| result = self.DoReplaceRevisionRef( |
| 'and revision %s' % self.GIT_HASH_3.upper()) |
| self.assertEqual( |
| 'https://crrev.com/%s' % self.GIT_HASH_3.upper(), result[0].href) |
| self.assertEqual('revision %s' % self.GIT_HASH_3.upper(), result[0].content) |
| |
| def testReplaceRevisionRef_CustomURL(self): |
| """A project can override the URL used for revision links.""" |
| project = fake.Project() |
| project.revision_url_format = 'http://example.com/+/{revnum}' |
| result = self.DoReplaceRevisionRef( |
| 'This is a fix for r%s' % self.GIT_HASH_1, project=project) |
| self.assertEqual( |
| 'http://example.com/+/%s' % self.GIT_HASH_1, result[0].href) |
| self.assertEqual('r%s' % self.GIT_HASH_1, result[0].content) |