Пример #1
0
    def test_add__with_diff_containing_block_offset(self):
        res = Resource.loads(self.block_offset)
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')
Пример #2
0
    def test_publish_review__only_issue_comment(self):
        problems = Problems()
        problems.add(IssueComment('Very bad'))
        sha = 'abc123'

        review = Review(self.repo, self.pr, self.config)
        review.publish_review(problems, sha)

        assert self.pr.create_review.called
        assert_review(self.pr.create_review.call_args, [],
                      sha,
                      body='Very bad')
Пример #3
0
    def test_add__with_diff_containing_block_offset(self):
        res = [PullFile(f) for f in json.loads(self.block_offset)]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')
Пример #4
0
    def test_filter_existing__removes_duplicates(self, http):
        fixture_data = load_fixture('comments_current.json')
        response = Response()
        response._content = fixture_data
        http.return_value = response

        gh = Github()
        problems = Problems()
        review = Review(gh, 2)
        filename_1 = "Routing/Filter/AssetCompressor.php"
        filename_2 = "View/Helper/AssetCompressHelper.php"

        problems.add(filename_1, 87, 'A pithy remark')
        problems.add(filename_1, 87, 'Something different')
        problems.add(filename_2, 88, 'I <3 it')
        problems.add(filename_2, 89, 'Not such a good comment')

        review.load_comments()
        review.remove_existing(problems)

        res = problems.all(filename_1)
        eq_(1, len(res))
        expected = Comment(filename_1, 87, 87, 'Something different')
        eq_(res[0], expected)

        res = problems.all(filename_2)
        eq_(1, len(res))
        expected = Comment(filename_2, 88, 88, 'I <3 it')
        eq_(res[0], expected)
Пример #5
0
    def test_filter_existing__removes_duplicates(self, http):
        fixture_data = load_fixture('comments_current.json')
        response = Response()
        response._content = fixture_data
        http.return_value = response

        gh = Github()
        problems = Problems()
        review = Review(gh, 2)
        filename_1 = "Routing/Filter/AssetCompressor.php"
        filename_2 = "View/Helper/AssetCompressHelper.php"

        problems.add(filename_1, 87, 'A pithy remark')
        problems.add(filename_1, 87, 'Something different')
        problems.add(filename_2, 88, 'I <3 it')
        problems.add(filename_2, 89, 'Not such a good comment')

        review.load_comments()
        review.remove_existing(problems)

        res = problems.all(filename_1)
        eq_(1, len(res))
        expected = Comment(filename_1, 87, 87, 'Something different')
        eq_(res[0], expected)

        res = problems.all(filename_2)
        eq_(1, len(res))
        expected = Comment(filename_2, 88, 88, 'I <3 it')
        eq_(res[0], expected)
Пример #6
0
    def test_filter_existing__removes_duplicates(self):
        fixture_data = load_fixture('comments_current.json')
        self.pr.review_comments.return_value = map(
            lambda f: GhIssueComment(f),
            json.loads(fixture_data))
        problems = Problems()
        review = Review(self.gh, 2)
        filename_1 = "Routing/Filter/AssetCompressor.php"
        filename_2 = "View/Helper/AssetCompressHelper.php"

        problems.add(filename_1, 87, 'A pithy remark')
        problems.add(filename_1, 87, 'Something different')
        problems.add(filename_2, 88, 'I <3 it')
        problems.add(filename_2, 89, 'Not such a good comment')

        review.load_comments()
        review.remove_existing(problems)

        res = problems.all(filename_1)
        eq_(1, len(res))
        expected = Comment(filename_1, 87, 87, 'Something different')
        eq_(res[0], expected)

        res = problems.all(filename_2)
        eq_(1, len(res))
        expected = Comment(filename_2, 88, 88, 'I <3 it')
        eq_(res[0], expected)
Пример #7
0
    def test_filter_existing__removes_duplicates(self):
        fixture_data = load_fixture('comments_current.json')
        self.pr.review_comments.return_value = [
            GhIssueComment(f, self.session) for f in json.loads(fixture_data)
        ]
        problems = Problems()
        review = Review(self.repo, self.pr, self.config)
        filename_1 = "Routing/Filter/AssetCompressor.php"
        filename_2 = "View/Helper/AssetCompressHelper.php"

        problems.add(filename_1, 87, 'A pithy remark')
        problems.add(filename_1, 87, 'Something different')
        problems.add(filename_2, 88, 'I <3 it')
        problems.add(filename_2, 89, 'Not such a good comment')

        review.load_comments()
        review.remove_existing(problems)

        res = problems.all(filename_1)
        self.assertEqual(1, len(res))
        expected = Comment(filename_1,
                           87,
                           87,
                           'A pithy remark\nSomething different')
        self.assertEqual(res[0], expected)

        res = problems.all(filename_2)
        self.assertEqual(1, len(res))
        expected = Comment(filename_2, 88, 88, 'I <3 it')
        self.assertEqual(res[0], expected)
Пример #8
0
    def test_filter_existing__removes_duplicates(self):
        fixture_data = load_fixture('comments_current.json')
        self.pr.review_comments.return_value = [
            GhIssueComment(f) for f in json.loads(fixture_data)
        ]
        problems = Problems()
        review = Review(self.repo, self.pr, self.config)
        filename_1 = "Routing/Filter/AssetCompressor.php"
        filename_2 = "View/Helper/AssetCompressHelper.php"

        problems.add(filename_1, 87, 'A pithy remark')
        problems.add(filename_1, 87, 'Something different')
        problems.add(filename_2, 88, 'I <3 it')
        problems.add(filename_2, 89, 'Not such a good comment')

        review.load_comments()
        review.remove_existing(problems)

        res = problems.all(filename_1)
        eq_(1, len(res))
        expected = Comment(filename_1, 87, 87,
                           'A pithy remark\nSomething different')
        eq_(res[0], expected)

        res = problems.all(filename_2)
        eq_(1, len(res))
        expected = Comment(filename_2, 88, 88, 'I <3 it')
        eq_(res[0], expected)
Пример #9
0
    def test_publish_pull_review__only_issue_comment(self):
        problems = Problems()
        problems.add(IssueComment('Very bad'))
        sha = 'abc123'

        review = Review(self.repo, self.pr, self.config)
        review.publish_pull_review(problems, sha)

        assert self.pr.create_review.called
        assert_review(
            self,
            self.pr.create_review.call_args,
            [],
            sha,
            body='Very bad')
Пример #10
0
    def test_publish_checkrun__multiple_chunks(self):
        self.repo.create_checkrun = Mock()
        tst_config = build_review_config(fixer_ini,
                                         {'PULLREQUEST_STATUS': True})
        problems = Problems()
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = [
            Comment(filename_1, i, i, 'Something worse') for i in range(0, 70)
        ]
        problems.add_many(errors)
        problems.add(IssueComment('In the body'))
        run_id = 42

        review = Review(self.repo, self.pr, tst_config)
        review.publish_checkrun(problems, run_id)

        assert self.repo.update_checkrun.call_count == 2
        assert self.repo.create_status.called is False, 'no status required'

        first_call = self.repo.update_checkrun.call_args_list[0]
        assert run_id == first_call[0][0]

        first_payload = first_call[0][1]
        assert 'failure' == first_payload['conclusion']

        assert 'completed_at' in first_payload
        assert 'title' in first_payload['output']
        assert 'summary' in first_payload['output']
        assert 'annotations' in first_payload['output']

        assert 'In the body' == first_payload['output']['summary']
        assert 50 == len(first_payload['output']['annotations'])

        second_call = self.repo.update_checkrun.call_args_list[1]
        assert run_id == second_call[0][0]

        # The second payload should only contain additional annotations.
        second_payload = second_call[0][1]
        assert 'completed_at' not in second_payload
        assert 'title' in second_payload['output']
        assert 'summary' in second_payload['output']
        assert 'annotations' in second_payload['output']
        assert 'In the body' == second_payload['output']['summary']

        assert 20 == len(second_payload['output']['annotations'])
Пример #11
0
    def test_add__with_diff_containing_block_offset(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.block_offset)
        ]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        self.assertEqual(1, len(problems))

        result = problems.all('somefile.py')
        first_result = result[0]
        self.assertIsInstance(first_result, Comment)
        self.assertEqual(
            changes.line_position('somefile.py',
                                  line_num), first_result.position,
            'Offset should be transformed to match value in changes')
Пример #12
0
    def test_add__with_diff_containing_block_offset(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.block_offset)
        ]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        self.assertEqual(1, len(problems))

        result = problems.all('somefile.py')
        first_result = result[0]
        self.assertIsInstance(first_result, Comment)
        self.assertEqual(
            changes.line_position('somefile.py', line_num),
            first_result.position,
            'Offset should be transformed to match value in changes'
        )
Пример #13
0
class Processor(object):

    _repository = None
    _pull_request = None
    _target_path = None
    _changes = None
    _review = None
    _config = None
    problems = None

    def __init__(self, repository, pull_request, target_path, config):
        self._config = config
        self._repository = repository
        self._pull_request = pull_request
        self._target_path = target_path
        self.problems = Problems()
        self._review = Review(repository, pull_request, config)

    def load_changes(self):
        log.info('Loading pull request patches from github.')
        files = self._pull_request.files()
        self._changes = DiffCollection(files)
        self.problems.set_changes(self._changes)

    def run_tools(self):
        if self._changes is None:
            raise RuntimeError('No loaded changes, cannot run tools. '
                               'Try calling load_changes first.')
        config = self._config

        files_to_check = self._changes.get_files(
            ignore_patterns=config.ignore_patterns()
        )
        commits_to_check = self._pull_request.commits()

        tool_list = tools.factory(
            config,
            self.problems,
            self._target_path)

        if config.fixers_enabled():
            self.apply_fixers(tool_list, files_to_check)

        tools.run(tool_list, files_to_check, commits_to_check)

    def apply_fixers(self, tool_list, files_to_check):
        try:
            fixer_context = fixers.create_context(
                self._config,
                self._target_path,
                self._repository,
                self._pull_request,
            )
            fixer_diff = fixers.run_fixers(
                tool_list,
                self._target_path,
                files_to_check)
            fixers.apply_fixer_diff(
                self._changes,
                fixer_diff,
                fixer_context)
        except (ConfigurationError, WorkflowError) as e:
            log.info('Fixer application failed. Got %s', e)
            message = u'Unable to apply fixers. {}'.format(e)
            self.problems.add(InfoComment(message))
        except Exception as e:
            log.info('Fixer application failed, '
                     'rolling back working tree. Got %s', e)
            fixers.rollback_changes(self._target_path)

    def publish(self, check_run_id=None):
        self.problems.limit_to_changes()
        if check_run_id:
            self._review.publish_checkrun(
                self.problems,
                check_run_id)
        else:
            self._review.publish_review(
                self.problems,
                self._pull_request.head)
Пример #14
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()
        self.session = GitHubSession()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        self.assertEqual(2, len(self.problems))
        self.assertEqual(2, len(self.problems.all()))
        self.assertEqual(2, len(self.problems.all('file.py')))
        self.assertEqual(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

    def test_add__same_line_combines(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Spaces are good')
        self.assertEqual(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad\nSpaces are good'
        self.assertEqual(expected, result[0].body)

    def test_add__same_line_ignores_duplicates(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Tabs bad')
        self.assertEqual(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad'
        self.assertEqual(expected, result[0].body)

    def test_add__with_diff_containing_block_offset(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.block_offset)
        ]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        self.assertEqual(1, len(problems))

        result = problems.all('somefile.py')
        first_result = result[0]
        self.assertIsInstance(first_result, Comment)
        self.assertEqual(
            changes.line_position('somefile.py', line_num),
            first_result.position,
            'Offset should be transformed to match value in changes'
        )

    def test_add_many(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            Comment('some/file.py', 12, 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        self.assertEqual(2, len(result))
        self.assertEqual(errors, result)

    def test_error_count(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            Comment('some/file.py', 12, 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        assert 2 == len(self.problems)
        assert 2 == self.problems.error_count()

    def test_error_count_exclude_info(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            InfoComment('some content'),
        ]
        self.problems.add_many(errors)
        assert 1 == self.problems.error_count()
        assert 2 == len(self.problems)

    def test_limit_to_changes__remove_problems(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.two_files_json)
        ]
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            Comment(None, None, None, 'This is a general comment'),
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad'),
            Comment(filename_1, 130, 130, 'Filtered out, line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            Comment(filename_2, 2, 2, 'Filtered out'),
            Comment(filename_2, 3, 3, 'Something bad'),
            Comment(filename_2, 7, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        self.assertEqual(2, len(result))
        expected = [
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad')]
        self.assertEqual(len(result), len(expected))
        self.assertEqual(result, expected)

        result = self.problems.all(filename_2)
        self.assertEqual(1, len(result))
        expected = [
            Comment(filename_2, 3, 3, 'Something bad')
        ]
        self.assertEqual(result, expected)

    def test_has_changes(self):
        problems = Problems(changes=None)
        self.assertFalse(problems.has_changes())

        problems = Problems(changes=[1])
        assert problems.has_changes()
Пример #15
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        eq_(2, len(self.problems))
        eq_(2, len(self.problems.all()))
        eq_(2, len(self.problems.all('file.py')))
        eq_(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

    def test_add__same_line_combines(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Spaces are good')
        eq_(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad\nSpaces are good'
        eq_(expected, result[0].body)

    def test_add__same_line_ignores_duplicates(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Tabs bad')
        eq_(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad'
        eq_(expected, result[0].body)

    def test_add__with_diff_containing_block_offset(self):
        res = [PullFile(f) for f in json.loads(self.block_offset)]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')

    def test_add_many(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            Comment('some/file.py', 12, 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        eq_(2, len(result))
        eq_(errors, result)

    def test_limit_to_changes__remove_problems(self):
        res = [PullFile(f) for f in json.loads(self.two_files_json)]
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            Comment(None, None, None, 'This is a general comment'),
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad'),
            Comment(filename_1, 130, 130, 'Filtered out, line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            Comment(filename_2, 2, 2, 'Filtered out'),
            Comment(filename_2, 3, 3, 'Something bad'),
            Comment(filename_2, 7, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        eq_(2, len(result))
        expected = [
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad')
        ]
        eq_(len(result), len(expected))
        eq_(result, expected)

        result = self.problems.all(filename_2)
        eq_(1, len(result))
        expected = [Comment(filename_2, 3, 3, 'Something bad')]
        eq_(result, expected)

    def test_has_changes(self):
        problems = Problems(changes=None)
        self.assertFalse(problems.has_changes())

        problems = Problems(changes=[1])
        assert problems.has_changes()
Пример #16
0
 def test_add__with_base_path_no_trailing_slash(self):
     problems = Problems('/some/path')
     problems.add('/some/path/file.py', 10, 'Not good')
     eq_([], problems.all('/some/path/file.py'))
     eq_(1, len(problems.all('file.py')))
     eq_(1, len(problems))
Пример #17
0
 def test_add__with_base_path_no_trailing_slash(self):
     problems = Problems('/some/path')
     problems.add('/some/path/file.py', 10, 'Not good')
     eq_([], problems.all('/some/path/file.py'))
     eq_(1, len(problems.all('file.py')))
     eq_(1, len(problems))
Пример #18
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        eq_(2, len(self.problems))
        eq_(2, len(self.problems.all()))
        eq_(2, len(self.problems.all('file.py')))
        eq_(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

    def test_add__with_base_path(self):
        problems = Problems('/some/path/')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_base_path_no_trailing_slash(self):
        problems = Problems('/some/path')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_diff_containing_block_offset(self):
        res = Resource.loads(self.block_offset)
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')

    def test_add_many(self):
        errors = [
            ('some/file.py', 10, 'Thing is wrong'),
            ('some/file.py', 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        eq_(2, len(result))
        expected = [
            Comment(errors[0][0], errors[0][1], errors[0][1], errors[0][2]),
            Comment(errors[1][0], errors[1][1], errors[1][1], errors[1][2]),
        ]
        eq_(expected, result)

    def test_limit_to_changes__remove_problems(self):
        res = Resource.loads(self.two_files_json)
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad'),
            (filename_1, 130, 'Filtered out, as line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            (filename_2, 2, 'Filtered out'),
            (filename_2, 3, 'Something bad'),
            (filename_2, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        eq_(2, len(result))
        expected = [(filename_1, 117, 'Something bad'),
                    (filename_1, 119, 'Something else bad')]
        eq_(result.sort(), expected.sort())

        result = self.problems.all(filename_2)
        eq_(1, len(result))
        expected = [Comment(filename_2, 3, 3, 'Something bad')]
        eq_(result, expected)

    def test_publish_problems(self):
        gh = Mock()
        problems = Problems()

        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something bad'),
        )
        problems.add_many(errors)
        sha = 'abc123'

        review = Review(gh, 3)
        review.publish_problems(problems, sha)

        assert gh.pull_requests.comments.create.called
        eq_(2, gh.pull_requests.comments.create.call_count)
        calls = gh.pull_requests.comments.create.call_args_list

        expected = call(
            3, {
                'commit_id': sha,
                'path': errors[0][0],
                'position': errors[0][1],
                'body': errors[0][2]
            })
        eq_(calls[0], expected)

        expected = call(
            3, {
                'commit_id': sha,
                'path': errors[1][0],
                'position': errors[1][1],
                'body': errors[1][2]
            })
        eq_(calls[1], expected)

    @patch('lintreview.review.time')
    def test_publish_with__wait_time(self, time):
        gh = Mock()
        problems = Problems()
        review = Review(gh, 3)

        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something bad'),
        )
        problems.add_many(errors)
        sha = 'abc123'

        review.publish_problems(problems, sha, 1)
        assert time.sleep.called
        eq_(2, time.sleep.call_count)
        calls = time.sleep.call_args_list

        expected = call(1)
        eq_(calls[0], expected)
        eq_(calls[1], expected)

    def test_publish_ok_comment(self):
        gh = Mock()
        problems = Problems()
        review = Review(gh, 3)

        sha = 'abc123'
        review.publish(problems, sha)

        assert not (gh.pull_requests.comments.create.called)
        assert gh.issues.comments.create.called

        calls = gh.issues.comments.create.call_args_list

        expected = call(3, ':+1: No lint errors found.')
        eq_(calls[0], expected)
Пример #19
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        eq_(2, len(self.problems))
        eq_(2, len(self.problems.all()))
        eq_(2, len(self.problems.all('file.py')))
        eq_(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

    def test_add__with_base_path(self):
        problems = Problems('/some/path/')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_base_path_no_trailing_slash(self):
        problems = Problems('/some/path')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_diff_containing_block_offset(self):
        res = map(lambda f: PullFile(f),
                  json.loads(self.block_offset))
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')

    def test_add_many(self):
        errors = [
            ('some/file.py', 10, 'Thing is wrong'),
            ('some/file.py', 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        eq_(2, len(result))
        expected = [
            Comment(errors[0][0], errors[0][1], errors[0][1], errors[0][2]),
            Comment(errors[1][0], errors[1][1], errors[1][1], errors[1][2]),
        ]
        eq_(expected, result)

    def test_limit_to_changes__remove_problems(self):
        res = map(lambda f: PullFile(f),
                  json.loads(self.two_files_json))
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (None, None, 'This is a general comment'),
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad'),
            (filename_1, 130, 'Filtered out, as line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            (filename_2, 2, 'Filtered out'),
            (filename_2, 3, 'Something bad'),
            (filename_2, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        eq_(2, len(result))
        expected = [
            (None, None, 'This is a general comment'),
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad')]
        eq_(result.sort(), expected.sort())

        result = self.problems.all(filename_2)
        eq_(1, len(result))
        expected = [
            Comment(filename_2, 3, 3, 'Something bad')
        ]
        eq_(result, expected)

    def test_has_changes(self):
        problems = Problems(changes=None)
        self.assertFalse(problems.has_changes())

        problems = Problems(changes=[1])
        assert problems.has_changes()
Пример #20
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        eq_(2, len(self.problems))
        eq_(2, len(self.problems.all()))
        eq_(2, len(self.problems.all('file.py')))
        eq_(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

    def test_add__with_base_path(self):
        problems = Problems('/some/path/')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_base_path_no_trailing_slash(self):
        problems = Problems('/some/path')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_diff_containing_block_offset(self):
        res = Resource.loads(self.block_offset)
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')

    def test_add_many(self):
        errors = [
            ('some/file.py', 10, 'Thing is wrong'),
            ('some/file.py', 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        eq_(2, len(result))
        expected = [
            Comment(errors[0][0], errors[0][1], errors[0][1], errors[0][2]),
            Comment(errors[1][0], errors[1][1], errors[1][1], errors[1][2]),
        ]
        eq_(expected, result)

    def test_limit_to_changes__remove_problems(self):
        res = Resource.loads(self.two_files_json)
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad'),
            (filename_1, 130, 'Filtered out, as line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            (filename_2, 2, 'Filtered out'),
            (filename_2, 3, 'Something bad'),
            (filename_2, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        eq_(2, len(result))
        expected = [(filename_1, 117, 'Something bad'),
                    (filename_1, 119, 'Something else bad')]
        eq_(result.sort(), expected.sort())

        result = self.problems.all(filename_2)
        eq_(1, len(result))
        expected = [Comment(filename_2, 3, 3, 'Something bad')]
        eq_(result, expected)

    def test_has_changes(self):
        problems = Problems(changes=None)
        self.assertFalse(problems.has_changes())

        problems = Problems(changes=[1])
        assert problems.has_changes()
Пример #21
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()
        self.session = GitHubSession()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        self.assertEqual(2, len(self.problems))
        self.assertEqual(2, len(self.problems.all()))
        self.assertEqual(2, len(self.problems.all('file.py')))
        self.assertEqual(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        self.assertEqual(1, len(self.problems))

    def test_add__same_line_combines(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Spaces are good')
        self.assertEqual(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad\nSpaces are good'
        self.assertEqual(expected, result[0].body)

    def test_add__same_line_ignores_duplicates(self):
        self.problems.add('file.py', 10, 'Tabs bad')
        self.problems.add('file.py', 10, 'Tabs bad')
        self.assertEqual(1, len(self.problems))

        result = self.problems.all()
        expected = 'Tabs bad'
        self.assertEqual(expected, result[0].body)

    def test_add__with_diff_containing_block_offset(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.block_offset)
        ]
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        self.assertEqual(1, len(problems))

        result = problems.all('somefile.py')
        first_result = result[0]
        self.assertIsInstance(first_result, Comment)
        self.assertEqual(
            changes.line_position('somefile.py',
                                  line_num), first_result.position,
            'Offset should be transformed to match value in changes')

    def test_add_zero(self):
        self.problems.add('file.py', 0, 'Not good')
        result = self.problems.all('file.py')
        assert len(result) == 1, problems
        assert result[0].line == Comment.FIRST_LINE_IN_DIFF

    def test_add_many(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            Comment('some/file.py', 12, 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        self.assertEqual(2, len(result))
        self.assertEqual(errors, result)

    def test_error_count(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            Comment('some/file.py', 12, 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        assert 2 == len(self.problems)
        assert 2 == self.problems.error_count()

    def test_error_count_exclude_info(self):
        errors = [
            Comment('some/file.py', 10, 10, 'Thing is wrong'),
            InfoComment('some content'),
        ]
        self.problems.add_many(errors)
        assert 1 == self.problems.error_count()
        assert 2 == len(self.problems)

    def test_limit_to_changes__remove_problems(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.two_files_json)
        ]
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            Comment(None, None, None, 'This is a general comment'),
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad'),
            Comment(filename_1, 130, 130, 'Filtered out, line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            Comment(filename_2, 2, 2, 'Filtered out'),
            Comment(filename_2, 3, 3, 'Something bad'),
            Comment(filename_2, 7, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        self.assertEqual(2, len(result))
        expected = [
            Comment(filename_1, 117, 117, 'Something bad'),
            Comment(filename_1, 119, 119, 'Something else bad')
        ]
        self.assertEqual(len(result), len(expected))
        self.assertEqual(result, expected)

        result = self.problems.all(filename_2)
        self.assertEqual(1, len(result))
        expected = [Comment(filename_2, 3, 3, 'Something bad')]
        self.assertEqual(result, expected)

    def test_limit_to_changes__first_line_in_diff(self):
        res = [
            PullFile(f, self.session) for f in json.loads(self.two_files_json)
        ]
        changes = DiffCollection(res)

        # Add problems
        filename = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            Comment(filename, 5, 5, 'Something bad'),
            Comment(filename, Comment.FIRST_LINE_IN_DIFF, 0, 'First line!'),
            Comment(filename, 7, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename)
        self.assertEqual(2, len(result))
        expected = [
            Comment(filename, 5, 5, 'Something bad'),
            Comment(filename, 3, 3, 'First line!'),
        ]
        self.assertEqual(result, expected)

    def test_has_changes(self):
        problems = Problems(changes=None)
        self.assertFalse(problems.has_changes())

        problems = Problems(changes=[1])
        assert problems.has_changes()
Пример #22
0
class Processor(object):

    _repository = None
    _pull_request = None
    _target_path = None
    _changes = None
    _review = None
    _config = None
    problems = None

    def __init__(self, repository, pull_request, target_path, config):
        self._config = config
        self._repository = repository
        self._pull_request = pull_request
        self._target_path = target_path
        self.problems = Problems()
        self._review = Review(repository, pull_request, config)

    def load_changes(self):
        log.info('Loading pull request patches from github.')
        files = self._pull_request.files()
        self._changes = DiffCollection(files)
        self.problems.set_changes(self._changes)

    def run_tools(self):
        if self._changes is None:
            raise RuntimeError('No loaded changes, cannot run tools. '
                               'Try calling load_changes first.')
        config = self._config

        files_to_check = self._changes.get_files(
            ignore_patterns=config.ignore_patterns())
        commits_to_check = self._pull_request.commits()

        tool_list = tools.factory(config, self.problems, self._target_path)

        if config.fixers_enabled():
            self.apply_fixers(tool_list, files_to_check)

        tools.run(tool_list, files_to_check, commits_to_check)

    def apply_fixers(self, tool_list, files_to_check):
        try:
            fixer_context = fixers.create_context(
                self._config,
                self._target_path,
                self._repository,
                self._pull_request,
            )
            fixer_diff = fixers.run_fixers(tool_list, self._target_path,
                                           files_to_check)
            fixers.apply_fixer_diff(self._changes, fixer_diff, fixer_context)
        except (ConfigurationError, WorkflowError) as e:
            log.info('Fixer application failed. Got %s', e)
            message = u'Unable to apply fixers. {}'.format(e)
            self.problems.add(InfoComment(message))
            fixers.rollback_changes(self._target_path, self._pull_request.head)
        except Exception as e:
            log.info(
                'Fixer application failed, '
                'rolling back working tree. Got %s', e)
            fixers.rollback_changes(self._target_path, self._pull_request.head)

    def publish(self, check_run_id=None):
        self.problems.limit_to_changes()
        if check_run_id:
            self._review.publish_checkrun(self.problems, check_run_id)
        else:
            self._review.publish_review(self.problems, self._pull_request.head)
Пример #23
0
class TestProblems(TestCase):

    two_files_json = load_fixture('two_file_pull_request.json')

    # Block offset so lines don't match offsets
    block_offset = load_fixture('pull_request_line_offset.json')

    def setUp(self):
        self.problems = Problems()

    def test_add(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 11, 'Not good')
        eq_(2, len(self.problems))
        eq_(2, len(self.problems.all()))
        eq_(2, len(self.problems.all('file.py')))
        eq_(0, len(self.problems.all('not there')))

    def test_add__duplicate_is_ignored(self):
        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

        self.problems.add('file.py', 10, 'Not good')
        eq_(1, len(self.problems))

    def test_add__with_base_path(self):
        problems = Problems('/some/path/')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_base_path_no_trailing_slash(self):
        problems = Problems('/some/path')
        problems.add('/some/path/file.py', 10, 'Not good')
        eq_([], problems.all('/some/path/file.py'))
        eq_(1, len(problems.all('file.py')))
        eq_(1, len(problems))

    def test_add__with_diff_containing_block_offset(self):
        res = Resource.loads(self.block_offset)
        changes = DiffCollection(res)

        problems = Problems(changes=changes)
        line_num = 32
        problems.add('somefile.py', line_num, 'Not good')
        eq_(1, len(problems))

        result = problems.all('somefile.py')
        eq_(changes.line_position('somefile.py', line_num), result[0].position,
            'Offset should be transformed to match value in changes')

    def test_add_many(self):
        errors = [
            ('some/file.py', 10, 'Thing is wrong'),
            ('some/file.py', 12, 'Not good'),
        ]
        self.problems.add_many(errors)
        result = self.problems.all('some/file.py')
        eq_(2, len(result))
        expected = [
            Comment(errors[0][0], errors[0][1], errors[0][1], errors[0][2]),
            Comment(errors[1][0], errors[1][1], errors[1][1], errors[1][2]),
        ]
        eq_(expected, result)

    def test_limit_to_changes__remove_problems(self):
        res = Resource.loads(self.two_files_json)
        changes = DiffCollection(res)

        # Setup some fake problems.
        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad'),
            (filename_1, 130, 'Filtered out, as line is not changed'),
        )
        self.problems.add_many(errors)
        filename_2 = 'Test/test_files/View/Parse/single.ctp'
        errors = (
            (filename_2, 2, 'Filtered out'),
            (filename_2, 3, 'Something bad'),
            (filename_2, 7, 'Filtered out'),
        )
        self.problems.add_many(errors)
        self.problems.set_changes(changes)
        self.problems.limit_to_changes()

        result = self.problems.all(filename_1)
        eq_(2, len(result))
        expected = [
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something else bad')]
        eq_(result.sort(), expected.sort())

        result = self.problems.all(filename_2)
        eq_(1, len(result))
        expected = [Comment(filename_2, 3, 3, 'Something bad')]
        eq_(result, expected)

    def test_publish_problems(self):
        gh = Mock()
        problems = Problems()

        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something bad'),
        )
        problems.add_many(errors)
        sha = 'abc123'

        review = Review(gh, 3)
        review.publish_problems(problems, sha)

        assert gh.pull_requests.comments.create.called
        eq_(2, gh.pull_requests.comments.create.call_count)
        calls = gh.pull_requests.comments.create.call_args_list

        expected = call(3, {
            'commit_id': sha,
            'path': errors[0][0],
            'position': errors[0][1],
            'body': errors[0][2]
        })
        eq_(calls[0], expected)

        expected = call(3, {
            'commit_id': sha,
            'path': errors[1][0],
            'position': errors[1][1],
            'body': errors[1][2]
        })
        eq_(calls[1], expected)

    @patch('lintreview.review.time')
    def test_publish_with__wait_time(self, time):
        gh = Mock()
        problems = Problems()
        review = Review(gh, 3)

        filename_1 = 'Console/Command/Task/AssetBuildTask.php'
        errors = (
            (filename_1, 117, 'Something bad'),
            (filename_1, 119, 'Something bad'),
        )
        problems.add_many(errors)
        sha = 'abc123'

        review.publish_problems(problems, sha, 1)
        assert time.sleep.called
        eq_(2, time.sleep.call_count)
        calls = time.sleep.call_args_list

        expected = call(1)
        eq_(calls[0], expected)
        eq_(calls[1], expected)

    def test_publish_ok_comment(self):
        gh = Mock()
        problems = Problems()
        review = Review(gh, 3)

        sha = 'abc123'
        review.publish(problems, sha)

        assert not(gh.pull_requests.comments.create.called)
        assert gh.issues.comments.create.called

        calls = gh.issues.comments.create.call_args_list

        expected = call(3, ':+1: No lint errors found.')
        eq_(calls[0], expected)