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_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')
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_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)
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)
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)
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)
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')
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'])
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__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' )
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)
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()
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()
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))
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)
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()
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()
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()
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)
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)