class Processor(object): def __init__(self, client, number, head, target_path): self._client = client self._number = number self._head = head self._target_path = target_path self._changes = None self._problems = Problems(target_path) self._review = Review(client, number) def load_changes(self): log.info('Loading pull request patches from github.') files = self._client.pull_requests.list_files(self._number) pull_request_patches = files.all() self._changes = DiffCollection(pull_request_patches) self._problems.set_changes(self._changes) def run_tools(self, repo_config): if not self._changes: raise RuntimeError('No loaded changes, cannot run tools. ' 'Try calling load_changes first.') files_to_check = self._changes.get_files(append_base=self._target_path) tools.run(repo_config, self._problems, files_to_check, self._target_path) def publish(self, wait_time=0): self._problems.limit_to_changes() self._review.publish(self._problems, self._head, wait_time)
class Processor(object): def __init__(self, client, number, head, target_path): self._client = client self._number = number self._head = head self._target_path = target_path self._changes = None self._problems = Problems(target_path) self._review = Review(client, number) def load_changes(self): log.info('Loading pull request patches from github.') files = self._client.pull_requests.list_files(self._number) pull_request_patches = files.all() self._changes = DiffCollection(pull_request_patches) self._problems.set_changes(self._changes) def run_tools(self, review_config): if self._changes is None: raise RuntimeError('No loaded changes, cannot run tools. ' 'Try calling load_changes first.') files_to_check = self._changes.get_files( append_base=self._target_path, ignore_patterns=review_config.ignore_patterns()) tools.run( review_config, self._problems, files_to_check, self._target_path) def publish(self, wait_time=0): self._problems.limit_to_changes() self._review.publish(self._problems, self._head, wait_time)
class Processor(object): _client = None _number = None _head = None _target_path = None _changes = None _problems = None _review = None _config = None def __init__(self, client, number, head, target_path, config=None): self._client = client self._number = number self._head = head self._target_path = target_path self._problems = Problems(target_path) self._review = Review(client, number) if config is None: config = {} self._config = config def load_changes(self): log.info('Loading pull request patches from github.') files = self._client.pull_requests.list_files(self._number) pull_request_patches = files.all() self._changes = DiffCollection(pull_request_patches) self._problems.set_changes(self._changes) def run_tools(self, review_config): if self._changes is None: raise RuntimeError('No loaded changes, cannot run tools. ' 'Try calling load_changes first.') files_to_check = self._changes.get_files( append_base=self._target_path, ignore_patterns=review_config.ignore_patterns()) commits_to_check = self.get_commits(self._number) tools.run( review_config, self._problems, files_to_check, commits_to_check, self._target_path) def publish(self): self._problems.limit_to_changes() self._review.publish( self._problems, self._head, self._config.get('SUMMARY_THRESHOLD')) def get_commits(self, number): return self._client.pull_requests.list_commits(number).all()
class Processor(object): _repository = None _pull_request = None _target_path = None _changes = None _problems = None _review = None _config = None def __init__(self, repository, pull_request, target_path, config=None): config = config if config else {} self._config = config self._repository = repository self._pull_request = pull_request self._target_path = target_path self._problems = Problems(target_path) 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, review_config): if self._changes is None: raise RuntimeError('No loaded changes, cannot run tools. ' 'Try calling load_changes first.') files_to_check = self._changes.get_files( append_base=self._target_path, ignore_patterns=review_config.ignore_patterns()) commits_to_check = self._pull_request.commits() log.debug("_problems before tools: %s" % len(self._problems)) self._problems = tools.run( review_config, self._problems, files_to_check, commits_to_check, self._target_path) log.debug("_problems after tools: %s" % len(self._problems)) def publish(self): self._problems.limit_to_changes() self._review.publish( self._problems, self._pull_request.head, self._config.get('SUMMARY_THRESHOLD'))
class Processor(object): _repository = None _pull_request = None _target_path = None _changes = None _problems = None _review = None _config = None def __init__(self, repository, pull_request, target_path, config=None): config = config if config else {} self._config = config self._repository = repository self._pull_request = pull_request self._target_path = target_path self._problems = Problems(target_path) 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, review_config): if self._changes is None: raise RuntimeError('No loaded changes, cannot run tools. ' 'Try calling load_changes first.') files_to_check = self._changes.get_files( append_base=self._target_path, ignore_patterns=review_config.ignore_patterns()) commits_to_check = self._pull_request.commits() tools.run(review_config, self._problems, files_to_check, commits_to_check, self._target_path) def publish(self): self._problems.limit_to_changes() self._review.publish(self._problems, self._pull_request.head, self._config.get('SUMMARY_THRESHOLD'))
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()
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 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)
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 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() 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() 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 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)