Ejemplo n.º 1
0
    def test_parse_errors(self):
        test_string = """\
<?xml version='1.0' encoding='UTF-8'?>
    <checkstyle version='4.3'>
        <file name='scripts/run&#95;tests.sh' >
            <error line='31'
             column='26' severity='info'
             message='Double quote to prevent globbing and word splitting.'
             source='ShellCheck.SC2086' />
        </file>
        <file name='scripts/setup.sh' >
            <error
             line='3'
             column='1'
             severity='warning'
             message='FOO appears unused. Verify it or export it.'
             source='ShellCheck.SC2034' />
        </file>
</checkstyle>
"""

        result = checkstyle.parse(test_string)
        self.assertEqual(2, len(result))
        self.assertIn(
            Problem(
                'scripts/run_tests.sh', 31,
                'ShellCheck.SC2086: Double quote to prevent '
                'globbing and word splitting.'), result)

        self.assertIn(
            Problem(
                'scripts/setup.sh', 3,
                'ShellCheck.SC2034: FOO appears unused. '
                'Verify it or export it.'), result)
Ejemplo n.º 2
0
    def test_parse_errors(self):
        test_string = [
            '{}/Classes/Foo + Bar/Foo + Menu/Controllers/'
            'SomeController.swift:42: warning: '
            'Documentation Comment Violation: Needs documentation '
            'comment'.format(os.path.curdir),
            '{}/Classes/Foo + Bar/Foo + Menu/Controllers/'
            'AnotherController.swift:50:5: warning: '
            'Documentation Comment Violation: Needs documentation '
            'comment'.format(os.path.curdir)
        ]
        result = swiftlint.parse('\n'.join(test_string))
        self.assertEqual(2, len(result))

        self.assertIn(
            Problem(
                'Classes/Foo + Bar/Foo + Menu/Controllers/'
                'SomeController.swift', 42, 'Documentation Comment Violation: '
                'Needs documentation comment'), result)

        self.assertIn(
            Problem(
                'Classes/Foo + Bar/Foo + Menu/Controllers/'
                'AnotherController.swift', 50,
                'Documentation Comment Violation: '
                'Needs documentation comment'), result)
Ejemplo n.º 3
0
    def test_parse_errors(self):
        test_string = """\
<?xml version="1.0" encoding="UTF-8"?>
<pmd version="5.5.0" timestamp="2016-07-06T11:29:03.360">
    <file name="{0}/DriverRideDrivingToWaypointController.java">
        <violation beginline="287" endline="287" begincolumn="58" endcolumn="61" rule="UnusedFormalParameter" ruleset="Unused Code" package="me.lyft.android.ui.driver.ridescreens" class="DriverRideDrivingToWaypointController" method="displayWaypointAddress" variable="ride" externalInfoUrl="https://pmd.github.io/pmd-5.5.0/pmd-java/rules/java/unusedcode.html#UnusedFormalParameter" priority="3">
        Avoid unused method parameters such as 'ride'.
    </violation>
    </file>
    <file name="{0}/AddCouponView.java">
        <violation beginline="24" endline="24" begincolumn="1" endcolumn="38" rule="UnusedImports" ruleset="Import Statements" package="me.lyft.android.ui.payment" externalInfoUrl="https://pmd.github.io/pmd-5.5.0/pmd-java/rules/java/imports.html#UnusedImports" priority="4">
        Avoid unused imports such as 'me.lyft.android.common.Strings'
        </violation>
    </file>
</pmd>
    """.format(os.path.curdir)

        result = pmd.parse(test_string)
        self.assertEqual(2, len(result))
        self.assertIn(
            Problem(
                './DriverRideDrivingToWaypointController.java', 287,
                'UnusedFormalParameter: Avoid unused method'
                " parameters such as 'ride'."), result)

        self.assertIn(
            Problem(
                './AddCouponView.java', 24,
                'UnusedImports: Avoid unused imports such as'
                " 'me.lyft.android.common.Strings'"), result)
Ejemplo n.º 4
0
    def test_parse_errors(self):
        test_string = [
            'Something happened!',
            "More stuff 'happened'",
        ]

        result = passthrough.parse('\n'.join(test_string))
        self.assertEqual(2, len(result))

        self.assertIn(Problem('', 0, 'Something happened!'), result)
        self.assertIn(Problem('', 0, "More stuff 'happened'"), result)
Ejemplo n.º 5
0
    def test_parse_errors(self):
        test_string = '''
src/linters/pylint.py:10: [E302] expected 2 blank lines, found 1
tests/linters/pylint.py:42: [E302] expected 2 blank lines, found 1
'''
        result = pylint.parse(test_string)
        self.assertEqual(2, len(result))

        self.assertIn(
            Problem('src/linters/pylint.py', 10,
                    '[E302] expected 2 blank lines, found 1'), result)

        self.assertIn(
            Problem('tests/linters/pylint.py', 42,
                    '[E302] expected 2 blank lines, found 1'), result)
Ejemplo n.º 6
0
    def test_parse_errors(self):
        test_string = '''
src/linters/pylint.py: note: expected 2 blank lines, found 1
tests/linters/pylint.py:42: error: "module" has no attribute "foo"
tests/linters/pylint.py:33: error: "module" has no attribute "bar"
'''

        result = mypy.parse(test_string)
        self.assertEqual(2, len(result))
        self.assertIn(
            Problem('tests/linters/pylint.py', 42,
                    'error: "module" has no attribute "foo"'), result)

        self.assertIn(
            Problem('tests/linters/pylint.py', 33,
                    'error: "module" has no attribute "bar"'), result)
Ejemplo n.º 7
0
    async def get_existing_problems(self) -> Set[Problem]:
        note_ref = ''
        result = set()
        if self.remote:
            fetch_notes = await asyncio.create_subprocess_exec(
                'git', 'fetch', self.remote, '{0}:{0}'.format(NOTES_REF),
                **GIT_SUBPROCESS_KWARGS)
            await fetch_notes.wait()

        last_n_revisions_proc = await asyncio.create_subprocess_exec(
            'git', 'log', '--skip=1', '-{}'.format(MAX_REVISIONS),
            '--pretty=%H', **GIT_SUBPROCESS_KWARGS)

        note_ref = self.get_note_ref(last_n_revisions_proc.stdout)

        await last_n_revisions_proc.wait()
        if note_ref:
            notes_proc = await asyncio.create_subprocess_exec(
                'git', 'show', note_ref, **GIT_SUBPROCESS_KWARGS)
            async for line in notes_proc.stdout:
                try:
                    problems = json.loads(line.decode())
                    for problem in problems:
                        result.add(Problem.from_json(problem))
                except Exception:
                    pass
            await notes_proc.wait()
        return result
Ejemplo n.º 8
0
    def test_comment_overflow(self, mock_getenv, mock_client_session):
        mock_args, fake_client_session = self.create_mock_pr(
            mock_getenv, mock_client_session)

        reporter = github_reporter.create_reporter(mock_args)

        problems = [Problem('another_file', x, 'Wat') for x in range(1, 13)]

        async_report = reporter.report('unit-test-linter', problems)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(async_report)

        overflow_message = textwrap.dedent('''\
            unit-test-linter says:

            Too many lint errors to report inline!  12 lines have a problem.
            Only reporting the first 10.''')
        overflow_call = call.post(
            'https://api.github.com/repos/foo/bar/issues/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps({
                'body': overflow_message,
            }, sort_keys=True))

        self.assertIn(overflow_call, fake_client_session.calls)
        self.assertEqual(3 + github_reporter.MAX_LINT_ERROR_REPORTS,
                         len(fake_client_session.calls))
Ejemplo n.º 9
0
    def report(self, problems: List[Problem]) -> None:
        grouped_problems = Problem.group_by_path_and_line(problems)

        headers = {
            'Authorization': 'token {}'.format(self.auth_token),
        }
        with aiohttp.ClientSession(headers=headers) as client_session:
            (line_map, existing_messages) = yield from asyncio.gather(
                self.create_line_to_position_map(client_session),
                self.get_existing_messages(client_session))
            lint_errors = 0
            review_comment_awaitable = []
            pr_url = self._get_pr_url()
            for location, problems_for_line in grouped_problems:
                message_for_line = [':sparkles:Linty Fresh Says:sparkles::',
                                    '',
                                    '```']

                reported_problems_for_line = set()
                for problem in problems_for_line:
                    if problem.message not in reported_problems_for_line:
                        message_for_line.append(problem.message)
                        reported_problems_for_line.add(problem.message)
                message_for_line.append('```')

                path = location[0]
                line_number = location[1]
                position = line_map.get(path, {}).get(line_number, None)
                if position is not None:
                    message = '\n'.join(message_for_line)
                    if (path, position, message) not in existing_messages:
                        lint_errors += 1
                        if lint_errors <= MAX_LINT_ERROR_REPORTS:
                            data = json.dumps({
                                'body': message,
                                'commit_id': self.commit,
                                'path': path,
                                'position': position,
                            }, sort_keys=True)
                            review_comment_awaitable.append(
                                client_session.post(pr_url, data=data))
            if lint_errors > MAX_LINT_ERROR_REPORTS:
                message = ''':sparkles:Linty Fresh Says:sparkles::

Too many lint errors to report inline!  {0} lines have a problem.
Only reporting the first {1}.'''.format(
                    lint_errors, MAX_LINT_ERROR_REPORTS)
                data = json.dumps({
                    'body': message
                })
                review_comment_awaitable.append(
                    asyncio.async(client_session.post(pr_url, data=data)))

            responses = yield from asyncio.gather(
                *review_comment_awaitable
            )  # type: List[aiohttp.ClientResponse]

            for response in responses:
                response.close()
Ejemplo n.º 10
0
    def test_parse_all(self):
        result = android.parse(test_string, pass_warnings=False)
        self.assertEqual(2, len(result))
        self.assertIn(Problem('scripts/run_tests.sh',
                                      15,
                                      'ScrollView size validation: This LinearLayout '
                                      'should use '
                                      '`android:layout_height="wrap_content"`'),
                      result)

        self.assertIn(Problem('scripts/setup.sh',
                              238,
                              'Implied default locale in case conversion: '
                              'Implicitly using the default locale is a '
                              'common source of bugs: Use '
                              '`toLowerCase(Locale)` instead'),
                      result)
Ejemplo n.º 11
0
    def report(self, problems: List[Problem]) -> None:
        grouped_problems = Problem.group_by_path_and_line(problems)

        headers = {
            'Authorization': 'token {}'.format(self.auth_token),
        }
        with aiohttp.ClientSession(headers=headers) as client_session:
            (line_map, existing_messages) = yield from asyncio.gather(
                self.create_line_to_position_map(client_session),
                self.get_existing_messages(client_session))
            lint_errors = 0
            review_comment_awaitable = []
            pr_url = self._get_pr_url()
            for location, problems_for_line in grouped_problems:
                message_for_line = [
                    ':sparkles:Linty Fresh Says:sparkles::', '', '```'
                ]

                reported_problems_for_line = set()
                for problem in problems_for_line:
                    if problem.message not in reported_problems_for_line:
                        message_for_line.append(problem.message)
                        reported_problems_for_line.add(problem.message)
                message_for_line.append('```')

                path = location[0]
                line_number = location[1]
                position = line_map.get(path, {}).get(line_number, None)
                if position is not None:
                    message = '\n'.join(message_for_line)
                    if (path, position, message) not in existing_messages:
                        lint_errors += 1
                        if lint_errors <= MAX_LINT_ERROR_REPORTS:
                            data = json.dumps(
                                {
                                    'body': message,
                                    'commit_id': self.commit,
                                    'path': path,
                                    'position': position,
                                },
                                sort_keys=True)
                            review_comment_awaitable.append(
                                client_session.post(pr_url, data=data))
            if lint_errors > MAX_LINT_ERROR_REPORTS:
                message = ''':sparkles:Linty Fresh Says:sparkles::

Too many lint errors to report inline!  {0} lines have a problem.
Only reporting the first {1}.'''.format(lint_errors, MAX_LINT_ERROR_REPORTS)
                data = json.dumps({'body': message})
                review_comment_awaitable.append(asyncio. async (
                    client_session.post(pr_url, data=data)))

            responses = yield from asyncio.gather(
                *review_comment_awaitable
            )  # type: List[aiohttp.ClientResponse]

            for response in responses:
                response.close()
Ejemplo n.º 12
0
def parse(contents: str, **kwargs) -> Set[Problem]:
    result = set()  # type: Set[Problem]
    for line in contents.splitlines():
        match = PYLINT_LINE_REGEX.match(line)
        if match:
            groups = match.groupdict()
            result.add(
                Problem(groups['path'], groups['line'], groups['message']))
    return result
Ejemplo n.º 13
0
 def test_parse_errors_only(self):
     result = android.parse(test_string, pass_warnings=True)
     self.assertEqual(1, len(result))
     self.assertIn(Problem('scripts/run_tests.sh',
                                   15,
                                   'ScrollView size validation: This LinearLayout '
                                   'should use '
                                   '`android:layout_height="wrap_content"`'),
                   result)
Ejemplo n.º 14
0
    def test_parse_errors(self):
        test_string = [
            "<unknown>:0: error: no such file or directory: 'foo.swift'",
            "<unknown>:0: ERROR: no such file or directory: 'case.swift'",
            "{}/Classes/foo/bar baz/qux.swift:201:21: "
            "error: use of unresolved identifier 'FooBar'".format(
                os.path.curdir),
            "{}/Classes/foo/bar/Protocols/SomeProtocol.swift:7:10: "
            "note: did you mean 'SomeOtherProtocol'?".format(os.path.curdir),
            "{}/Resources/Storyboards & XIBs/Foo.storyboard:kB7-Bl-wC0: "
            "warning: Unsupported configuration of constraint attributes. "
            "This may produce unexpected results at runtime before Xcode 5.1".
            format(os.path.curdir),
        ]

        result = xcodebuild.parse('\n'.join(test_string))
        self.assertEqual(5, len(result))

        self.assertIn(
            Problem('<unknown>', 0, "no such file or directory: 'foo.swift'"),
            result)

        self.assertIn(
            Problem('<unknown>', 0, "no such file or directory: 'case.swift'"),
            result)

        self.assertIn(
            Problem('Classes/foo/bar baz/'
                    'qux.swift', 201, "use of unresolved identifier 'FooBar'"),
            result)

        self.assertIn(
            Problem('Classes/foo/bar/Protocols/'
                    'SomeProtocol.swift', 7,
                    "did you mean 'SomeOtherProtocol'?"), result)

        self.assertIn(
            Problem(
                'Resources/Storyboards & XIBs/'
                'Foo.storyboard', 0,
                'kB7-Bl-wC0: Unsupported configuration of '
                'constraint attributes. This may produce unexpected '
                'results at runtime before Xcode 5.1'), result)
Ejemplo n.º 15
0
def parse(contents: str) -> Set[Problem]:
    result = set()  # type: Set[Problem]
    for line in contents.splitlines():
        match = MYPY_LINE_REGEX.match(line)
        if match:
            groups = match.groupdict()
            if groups['code'] != 'note':
                result.add(
                    Problem(groups['path'], groups['line'],
                            '{}: {}'.format(groups['code'],
                                            groups['message'])))
    return result
Ejemplo n.º 16
0
def parse(contents: str, **kwargs) -> Set[Problem]:
    result = set()  # type: Set[Problem]
    for line in contents.splitlines():
        match = XCODEBUILD_LINE_REGEX.match(line)
        if match:
            groups = match.groupdict()
            line = groups['line'] or 0
            message = groups['message']
            reference = groups['reference']
            if reference:
                message = '{}: {}'.format(reference, message)

            result.add(Problem(os.path.relpath(groups['path']),
                               line, message))
    return result
Ejemplo n.º 17
0
def parse(contents: str) -> Set[Problem]:
    result = set()  # type: Set[Problem]
    try:
        root = ElementTree.fromstring(contents)
    except ElementTree.ParseError:
        return result
    for file in root.findall('file'):
        file_name = file.get('name')
        for error in file.findall('error'):
            result.add(
                Problem(
                    file_name,
                    error.get('line'), '{}: {}'.format(error.get('source'),
                                                       error.get('message'))))
    return result
Ejemplo n.º 18
0
def parse(contents: str, **kwargs) -> Set[Problem]:
    result = set()  # type: Set[Problem]
    try:
        root = ElementTree.fromstring(contents)
    except ElementTree.ParseError:
        return result
    for file in root.findall('file'):
        file_name = file.get('name')
        for violation in file.findall('violation'):
            result.add(
                Problem(
                    file_name, violation.get('beginline'),
                    '{}: {}'.format(violation.get('rule'),
                                    violation.text.strip())))
    return result
Ejemplo n.º 19
0
    def get_existing_problems(self) -> Set[Problem]:
        note_ref = ''
        result = set()
        if self.remote:
            fetch_notes = yield from asyncio.create_subprocess_exec(
                'git', 'fetch', self.remote, '{0}:{0}'.format(NOTES_REF),
                **GIT_SUBPROCESS_KWARGS)
            yield from fetch_notes.wait()

        last_n_revisions_proc = yield from asyncio.create_subprocess_exec(
            'git', 'log', '--skip=1', '-{}'.format(MAX_REVISIONS),
            '--pretty=%H',
            **GIT_SUBPROCESS_KWARGS)
        for line in last_n_revisions_proc.stdout:
            notes_ref_proc = yield from asyncio.create_subprocess_exec(
                'git', 'notes', 'list', line.decode().strip(),
                **GIT_SUBPROCESS_KWARGS)
            yield from notes_ref_proc.wait()
            if notes_ref_proc.returncode == 0:
                note_ref = yield from notes_ref_proc.stdout.readline()
                note_ref = note_ref.decode().strip()
                if note_ref:
                    break
        yield from last_n_revisions_proc.wait()
        if note_ref:
            notes_proc = yield from asyncio.create_subprocess_exec(
                'git', 'show', note_ref,
                **GIT_SUBPROCESS_KWARGS)
            for line in notes_proc.stdout:
                try:
                    problems = json.loads(line.decode())
                    for problem in problems:
                        result.add(Problem.from_json(problem))
                except Exception:
                    pass
            yield from notes_proc.wait()
        return result
Ejemplo n.º 20
0
    def test_comment_on_pr(self, mock_getenv, mock_client_session):
        mock_args, fake_client_session = self.create_mock_pr(
            mock_getenv, mock_client_session)

        reporter = github_reporter.create_reporter(mock_args)
        async_report = reporter.report('unit-test-linter', [
            Problem('some_dir/some_file', 40, 'this made me sad'),
            Problem('some_dir/some_file', 40, 'really sad'),
            Problem('another_file', 2, 'This is OK'),
            Problem('another_file', 2, 'This is OK'),
            Problem('another_file', 3, 'I am a duplicate!'),
            Problem('another_file', 52, "#close_enough!!!"),
            Problem('missing_file', 42, "Missing file comment!!!"),
        ])

        loop = asyncio.get_event_loop()
        loop.run_until_complete(async_report)

        diff_request = call.get(
            'https://api.github.com/repos/foo/bar/pulls/1234',
            headers={
                'Accept': 'application/vnd.github.diff',
                'Authorization': 'token MY_TOKEN'
            })
        existing_comments_request = call.get(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'})
        first_comment = call.post(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'commit_id':
                    'abc123',
                    'path':
                    'another_file',
                    'body':
                    textwrap.dedent('''\
                    unit-test-linter says:

                    ```
                    This is OK
                    ```'''),
                    'position':
                    2
                },
                sort_keys=True))
        second_comment = call.post(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'commit_id':
                    'abc123',
                    'path':
                    'some_dir/some_file',
                    'body':
                    textwrap.dedent('''\
                    unit-test-linter says:

                    ```
                    this made me sad
                    really sad
                    ```'''),
                    'position':
                    3
                },
                sort_keys=True))
        close_enough_comment = call.post(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'commit_id':
                    'abc123',
                    'path':
                    'another_file',
                    'body':
                    textwrap.dedent('''\
                    unit-test-linter says:

                    (From line 52)
                    ```
                    #close_enough!!!
                    ```'''),
                    'position':
                    12
                },
                sort_keys=True))
        missing_file_call = call.post(
            'https://api.github.com/repos/foo/bar/issues/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'body':
                    textwrap.dedent('''\
                    unit-test-linter found some problems with lines not modified by this commit:
                    ```
                    missing_file:42:
                    \tMissing file comment!!!
                    ```'''),
                },
                sort_keys=True))

        self.assertEqual(6, len(fake_client_session.calls))
        self.assertIn(diff_request, fake_client_session.calls)
        self.assertIn(existing_comments_request, fake_client_session.calls)
        self.assertIn(first_comment, fake_client_session.calls)
        self.assertIn(second_comment, fake_client_session.calls)
        self.assertIn(close_enough_comment, fake_client_session.calls)
        self.assertIn(missing_file_call, fake_client_session.calls)
Ejemplo n.º 21
0
def parse(contents: str, **kwargs) -> Set[Problem]:
    if contents:
        return set([Problem('', 0, contents.strip())])
    else:
        return set()
Ejemplo n.º 22
0
    def test_comment_on_pr(self, mock_getenv, mock_client_session):
        mock_args, fake_client_session = self.create_mock_pr(
            mock_getenv, mock_client_session)

        reporter = github_reporter.create_reporter(mock_args)
        async_report = reporter.report([
            Problem('some_dir/some_file', 40, 'this made me sad'),
            Problem('some_dir/some_file', 40, 'really sad'),
            Problem('another_file', 2, 'This is OK'),
            Problem('another_file', 2, 'This is OK'),
            Problem('another_file', 3, 'I am a duplicate!'),
            Problem('missing_file', 42, "Don't report me!!!"),
        ])

        loop = asyncio.get_event_loop()
        loop.run_until_complete(async_report)

        diff_request = call.get(
            'https://api.github.com/repos/foo/bar/pulls/1234',
            headers={
                'Accept': 'application/vnd.github.diff',
                'Authorization': 'token MY_TOKEN'
            })
        existing_comments_request = call.get(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'})
        first_comment = call.post(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'commit_id':
                    'abc123',
                    'path':
                    'another_file',
                    'body':
                    textwrap.dedent('''\
                    :sparkles:Linty Fresh Says:sparkles::

                    ```
                    This is OK
                    ```'''),
                    'position':
                    2
                },
                sort_keys=True))
        second_comment = call.post(
            'https://api.github.com/repos/foo/bar/pulls/1234/comments',
            headers={'Authorization': 'token MY_TOKEN'},
            data=json.dumps(
                {
                    'commit_id':
                    'abc123',
                    'path':
                    'some_dir/some_file',
                    'body':
                    textwrap.dedent('''\
                    :sparkles:Linty Fresh Says:sparkles::

                    ```
                    this made me sad
                    really sad
                    ```'''),
                    'position':
                    3
                },
                sort_keys=True))

        self.assertEqual(4, len(fake_client_session.calls))
        self.assertIn(diff_request, fake_client_session.calls)
        self.assertIn(existing_comments_request, fake_client_session.calls)
        self.assertIn(first_comment, fake_client_session.calls)
        self.assertIn(second_comment, fake_client_session.calls)
Ejemplo n.º 23
0
def parse(contents: str, **kwargs) -> Set[Problem]:
    return set(Problem('', 0, x) for x in contents.splitlines())
Ejemplo n.º 24
0
    async def report(self, linter_name: str,
                     problems: List[GenericProblem]) -> None:
        if not problems:
            grouped_problems = {}
        elif isinstance(list(problems)[0], TestProblem):
            grouped_problems = TestProblem.group_by_group(problems)
        else:
            grouped_problems = Problem.group_by_path_and_line(problems)

        headers = {
            'Authorization': 'token {}'.format(self.auth_token),
        }
        with aiohttp.ClientSession(headers=headers) as client_session:
            (line_map, existing_messages, message_ids) = await asyncio.gather(
                self.create_line_to_position_map(client_session),
                self.get_existing_pr_messages(client_session, linter_name),
                self.get_existing_issue_message_ids(client_session,
                                                    linter_name))
            lint_errors = 0
            review_comment_awaitable = []
            pr_url = self._get_pr_url()
            no_matching_line_number = []
            for location, problems_for_line in grouped_problems:
                message_for_line = ['{0} says:'.format(linter_name), '']

                reported_problems_for_line = set()

                path = location[0]
                line_number = location[1]
                position = line_map.get(path, {}).get(line_number, None)
                if position is None and path in line_map:
                    file_map = line_map[path]
                    closest_line = min(file_map.keys(),
                                       key=lambda x: abs(x - line_number))
                    position = file_map[closest_line]
                    message_for_line.append(
                        '(From line {})'.format(line_number))
                message_for_line.append('```')
                if position is not None:
                    for problem in problems_for_line:
                        if problem.message not in reported_problems_for_line:
                            message_for_line.append(problem.message)
                            reported_problems_for_line.add(problem.message)
                    message_for_line.append('```')
                    message = '\n'.join(message_for_line)
                    try:
                        existing_messages.remove(
                            ExistingGithubMessage(None, path, position,
                                                  message))
                    except KeyError:
                        lint_errors += 1
                        if lint_errors <= MAX_LINT_ERROR_REPORTS:
                            data = json.dumps(
                                {
                                    'body': message,
                                    'commit_id': self.commit,
                                    'path': path,
                                    'position': position,
                                },
                                sort_keys=True)
                            review_comment_awaitable.append(
                                client_session.post(pr_url, data=data))
                else:
                    no_matching_line_number.append(
                        (location, problems_for_line))

            if lint_errors > MAX_LINT_ERROR_REPORTS:
                message = """{0} says:

Too many lint errors to report inline!  {1} lines have a problem.
Only reporting the first {2}.""".format(linter_name, lint_errors,
                                        MAX_LINT_ERROR_REPORTS)
                data = json.dumps({'body': message})
                review_comment_awaitable.append(
                    asyncio.ensure_future(
                        client_session.post(self._get_issue_url(), data=data)))

            if self.delete_previous_comments:
                for message_id in message_ids:
                    review_comment_awaitable.append(
                        asyncio.ensure_future(
                            client_session.delete(
                                self._get_delete_issue_comment_url(
                                    message_id))))
                for message in existing_messages:
                    review_comment_awaitable.append(
                        asyncio.ensure_future(
                            client_session.delete(
                                self._get_delete_pr_comment_url(
                                    message.comment_id))))

            if no_matching_line_number:
                no_matching_line_messages = []
                for location, problems_for_line in no_matching_line_number:
                    lint_errors += 1
                    path = location[0]
                    line_number = location[1]
                    no_matching_line_messages.append('{0}:{1}:'.format(
                        path, line_number))
                    for problem in problems_for_line:
                        no_matching_line_messages.append('\t{0}'.format(
                            problem.message))
                message = ('{0} says: I found some problems with lines not '
                           'modified by this commit:\n```\n{1}\n```'.format(
                               linter_name,
                               '\n'.join(no_matching_line_messages)))
                data = json.dumps({'body': message})
                review_comment_awaitable.append(
                    asyncio.ensure_future(
                        client_session.post(self._get_issue_url(), data=data)))

            responses = await asyncio.gather(
                *review_comment_awaitable
            )  # type: List[aiohttp.ClientResponse]
            for response in responses:
                response.close()

            if lint_errors > 0:
                raise HadLintErrorsException()
Ejemplo n.º 25
0
    async def report(self, problems: List[Problem]) -> None:
        grouped_problems = Problem.group_by_path_and_line(problems)

        headers = {
            'Authorization': 'token {}'.format(self.auth_token),
        }
        with aiohttp.ClientSession(headers=headers) as client_session:
            (line_map, existing_messages) = await asyncio.gather(
                self.create_line_to_position_map(client_session),
                self.get_existing_messages(client_session))
            lint_errors = 0
            review_comment_awaitable = []
            pr_url = self._get_pr_url()
            no_matching_line_number = []
            for location, problems_for_line in grouped_problems:
                message_for_line = [
                    ':sparkles:Linty Fresh Says:sparkles::', ''
                ]

                reported_problems_for_line = set()

                path = location[0]
                line_number = location[1]
                position = line_map.get(path, {}).get(line_number, None)
                if position is None and path in line_map:
                    file_map = line_map[path]
                    closest_line = min(file_map.keys(),
                                       key=lambda x: abs(x - line_number))
                    position = file_map[closest_line]
                    message_for_line.append(
                        '(From line {})'.format(line_number))
                message_for_line.append('```')
                if position is not None:
                    for problem in problems_for_line:
                        if problem.message not in reported_problems_for_line:
                            message_for_line.append(problem.message)
                            reported_problems_for_line.add(problem.message)
                    message_for_line.append('```')
                    message = '\n'.join(message_for_line)
                    if (path, position, message) not in existing_messages:
                        lint_errors += 1
                        if lint_errors <= MAX_LINT_ERROR_REPORTS:
                            data = json.dumps(
                                {
                                    'body': message,
                                    'commit_id': self.commit,
                                    'path': path,
                                    'position': position,
                                },
                                sort_keys=True)
                            review_comment_awaitable.append(
                                client_session.post(pr_url, data=data))
                else:
                    no_matching_line_number.append(
                        (location, problems_for_line))
            if lint_errors > MAX_LINT_ERROR_REPORTS:
                message = ''':sparkles:Linty Fresh Says:sparkles::

Too many lint errors to report inline!  {0} lines have a problem.
Only reporting the first {1}.'''.format(lint_errors, MAX_LINT_ERROR_REPORTS)
                data = json.dumps({'body': message})
                review_comment_awaitable.append(
                    asyncio.ensure_future(
                        client_session.post(self._get_issue_url(), data=data)))
            if no_matching_line_number:
                no_matching_line_messages = []
                for location, problems_for_line in no_matching_line_number:
                    path = location[0]
                    line_number = location[1]
                    no_matching_line_messages.append('{0}:{1}:'.format(
                        path, line_number))
                    for problem in problems_for_line:
                        no_matching_line_messages.append('\t{0}'.format(
                            problem.message))
                message = ('Linters found some problems with lines not '
                           'modified by this commit:\n```\n{0}\n```'.format(
                               '\n'.join(no_matching_line_messages)))
                data = json.dumps({'body': message})
                review_comment_awaitable.append(
                    asyncio.ensure_future(
                        client_session.post(self._get_issue_url(), data=data)))

            responses = await asyncio.gather(
                *review_comment_awaitable
            )  # type: List[aiohttp.ClientResponse]
            for response in responses:
                response.close()