def test_apply(self): uut = ApplyPatchAction() file_dict = { "f_a": ["1", "2", "3"], "f_b": ["1", "2", "3"], "f_c": ["1", "2", "3"] } expected_file_dict = { "f_a": ["1", "3_changed"], "f_b": ["1", "2", "3_changed"], "f_c": ["1", "2", "3"] } file_diff_dict = {} diff = Diff() diff.delete_line(2) uut.apply_from_section(PatchResult("origin", "msg", {"f_a": diff}), file_dict, file_diff_dict, Section("t")) diff = Diff() diff.change_line(3, "3", "3_changed") uut.apply_from_section(PatchResult("origin", "msg", {"f_a": diff}), file_dict, file_diff_dict, Section("t")) diff = Diff() diff.change_line(3, "3", "3_changed") uut.apply(PatchResult("origin", "msg", {"f_b": diff}), file_dict, file_diff_dict) for filename in file_diff_dict: file_dict[filename] = file_diff_dict[filename].apply(file_dict[filename]) self.assertEqual(file_dict, expected_file_dict)
def test_add(self): file_dict = { "f_a": ["1", "2", "3"], "f_b": ["1", "2", "3"], "f_c": ["1", "2", "3"] } expected_file_dict = { "f_a": ["1", "3_changed"], "f_b": ["1", "2", "3_changed"], "f_c": ["1", "2", "3"] } diff = Diff() diff.delete_line(2) uut1 = PatchResult("origin", "msg", {"f_a": diff}) diff = Diff() diff.change_line(3, "3", "3_changed") uut2 = PatchResult("origin", "msg", {"f_a": diff}) diff = Diff() diff.change_line(3, "3", "3_changed") uut3 = PatchResult("origin", "msg", {"f_b": diff}) uut1 += uut2 + uut3 uut1.apply(file_dict) self.assertEqual(file_dict, expected_file_dict)
def process_output(self, output, filename, file): output = json.loads(output) for issue in output: diff = Diff(file) from_lines = issue['from'].splitlines() to_lines = issue['to'].splitlines() assert len(from_lines) == len(to_lines) for other_lines in range(1, len(from_lines)): assert from_lines[other_lines] == to_lines[other_lines] line_nr = issue['startLine'] line_to_change = file[line_nr-1] newline = line_to_change.replace(from_lines[0], to_lines[0]) diff.change_line(line_nr, line_to_change, newline) yield Result.from_values( origin=self, message=issue['hint'], file=filename, severity=self.severity_map[issue['severity']], line=issue['startLine'], column=issue['startColumn'], end_line=issue['endLine'], end_column=issue['endColumn'], diffs={filename: diff})
def test_apply_rename(self): uut = ApplyPatchAction() with make_temp() as f_a: file_dict = {f_a: ['1\n', '2\n', '3\n']} expected_file_dict = {f_a+'.renamed': ['1\n', '2_changed\n', '3_changed\n']} file_diff_dict = {} diff = Diff(file_dict[f_a], rename=f_a+'.renamed') diff.change_line(3, '3\n', '3_changed\n') uut.apply(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict) self.assertTrue(isfile(f_a+'.orig')) self.assertTrue(isfile(f_a+'.renamed')) self.assertFalse(isfile(f_a)) diff = Diff(file_dict[f_a]) diff.change_line(2, '2\n', '2_changed\n') uut.apply(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict) self.assertFalse(isfile(f_a+'.renamed.orig')) file_dict = {f_a+'.renamed': open(f_a+'.renamed').readlines()} self.assertEqual(file_dict, expected_file_dict) # Recreate file so that context manager make_temp() can delete it open(f_a, 'w').close()
def test_apply(self): uut = ShowAppliedPatchesAction() with make_temp() as f_a, make_temp() as f_b, make_temp() as f_c: file_dict = { f_a: ['1\n', '2\n', '3\n'], f_b: ['1\n', '2\n', '3\n'], f_c: ['1\n', '2\n', '3\n'] } expected_file_dict = { f_a: ['1\n', '3_changed\n'], f_b: ['1\n', '2\n', '3_changed\n'], f_c: ['1\n', '2\n', '3\n'] } with make_temp() as testfile_path: file_diff_dict = {} file_dict = {testfile_path: ['1\n', '2\n', '3\n']} diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, '3\n', '3_changed\n') result = Result('origin', 'msg', diffs={f_a: diff}, applied_actions={'ApplyPatchAction': [Result( 'origin', 'message', diffs={testfile_path: diff}), file_dict, file_diff_dict, Section('')]}) self.assertTrue(uut.apply(result, file_dict, file_diff_dict))
def test_apply_orig_option(self): uut = ApplyPatchAction() with make_temp() as f_a, make_temp() as f_b: file_dict = { f_a: ['1\n', '2\n', '3\n'], f_b: ['1\n', '2\n', '3\n'] } expected_file_dict = { f_a: ['1\n', '2\n', '3_changed\n'], f_b: ['1\n', '2\n', '3_changed\n'] } file_diff_dict = {} diff = Diff(file_dict[f_a]) diff.change_line(3, '3\n', '3_changed\n') uut.apply(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict, no_orig=True) diff = Diff(file_dict[f_b]) diff.change_line(3, '3\n', '3_changed\n') uut.apply(Result('origin', 'msg', diffs={f_b: diff}), file_dict, file_diff_dict, no_orig=False) self.assertFalse(isfile(f_a+'.orig')) self.assertTrue(isfile(f_b+'.orig')) for filename in file_diff_dict: file_dict[filename] = file_diff_dict[filename].modified self.assertEqual(file_dict, expected_file_dict)
def test_apply_rename(self): uut = ApplyPatchAction() with make_temp() as f_a: file_dict = {f_a: ["1\n", "2\n", "3\n"]} expected_file_dict = {f_a+".renamed": ["1\n", "2_changed\n", "3_changed\n"]} file_diff_dict = {} diff = Diff(file_dict[f_a], rename=f_a+".renamed") diff.change_line(3, "3\n", "3_changed\n") uut.apply(Result("origin", "msg", diffs={f_a: diff}), file_dict, file_diff_dict) self.assertTrue(isfile(f_a+".orig")) self.assertTrue(isfile(f_a+".renamed")) self.assertFalse(isfile(f_a)) diff = Diff(file_dict[f_a]) diff.change_line(2, "2\n", "2_changed\n") uut.apply(Result("origin", "msg", diffs={f_a: diff}), file_dict, file_diff_dict) self.assertTrue(isfile(f_a+".renamed.orig")) file_dict = {f_a+".renamed": open(f_a+".renamed").readlines()} self.assertEqual(file_dict, expected_file_dict) # Recreate file so that context manager make_temp() can delete it open(f_a, 'w').close()
def test_print_result_no_input(self): with make_temp() as testfile_path: file_dict = {testfile_path: ["1\n", "2\n", "3\n"]} diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, "3\n", "3_changed\n") with simulate_console_inputs(1, 2, 3) as generator, retrieve_stdout() as stdout: ApplyPatchAction.is_applicable = staticmethod(lambda *args: True) print_results_no_input( self.log_printer, Section("someSection"), [Result("origin", "message", diffs={testfile_path: diff})], file_dict, self.file_diff_dict, color=False, ) self.assertEqual(generator.last_input, -1) self.assertEqual( stdout.getvalue(), """ Project wide: | | [NORMAL] origin: | | message """, )
def test_add(self): file_dict = { 'f_a': ['1', '2', '3'], 'f_b': ['1', '2', '3'], 'f_c': ['1', '2', '3'] } expected_file_dict = { 'f_a': ['1', '3_changed'], 'f_b': ['1', '2', '3_changed'], 'f_c': ['1', '2', '3'] } diff = Diff(file_dict['f_a']) diff.delete_line(2) uut1 = Result('origin', 'msg', diffs={'f_a': diff}) diff = Diff(file_dict['f_a']) diff.change_line(3, '3', '3_changed') uut2 = Result('origin', 'msg', diffs={'f_a': diff}) diff = Diff(file_dict['f_b']) diff.change_line(3, '3', '3_changed') uut3 = Result('origin', 'msg', diffs={'f_b': diff}) uut1 += uut2 + uut3 uut1.apply(file_dict) self.assertEqual(file_dict, expected_file_dict)
def test_apply_rename(self): # Initial file contents, *before* a patch was applied file_dict = { self.fa: ["1\n", "2\n", "3\n"]} # A patch that was applied for some reason to make things complicated file_diff_dict = {} diff = Diff(file_dict[self.fa], rename=self.fa+".renamed") diff.change_line(3, "3\n", "3_changed\n") ApplyPatchAction().apply( Result("origin", "msg", diffs={self.fa: diff}), file_dict, file_diff_dict) # End file contents after the patch and the OpenEditorAction was # applied expected_file_dict = { self.fa: ["1\n", "3_changed\n"]} section = Section("") section.append(Setting("editor", "")) uut = OpenEditorAction() subprocess.call = self.fake_edit diff_dict = uut.apply_from_section( Result.from_values("origin", "msg", self.fa), file_dict, file_diff_dict, section) for filename in diff_dict: file_dict[filename] = ( file_diff_dict[filename].modified) self.assertEqual(file_dict, expected_file_dict) open(self.fa, 'w').close()
def generate_spacing_diff(file, filename, line, line_number, match_object, required_spacing): """ Generate a diff for incorrectly spaced control or variable tags. :param match_object: A Match object containing the groups ``open``, ``close`` containing the opening and closing delimiters of a Jinja2 tag and ``content`` containing everything in between. :param required_spacing: The number of spaces expected after the ``open`` delimiter and before the ``close`` delimiter """ diff = Diff(file) content_before = line[:match_object.start('open')] content_after = line[match_object.end('close'):] spacing = ' ' * required_spacing replacement = ( '{before}{open}{spacing}{content}{spacing}{close}{after}'.format( before=content_before, spacing=spacing, after=content_after, content=match_object.group('content').strip(), open=match_object.group('open'), close=match_object.group('close'))) diff.change_line( line_number, line, replacement) return {filename: diff}
def apply(self, result, original_file_dict, file_diff_dict, language: str, no_orig: bool=False): """ Add ignore comment """ ignore_comment = self.get_ignore_comment(result.origin, language) if not ignore_comment: return file_diff_dict source_range = next(filter(lambda sr: exists(sr.file), result.affected_code)) filename = source_range.file ignore_diff = Diff(original_file_dict[filename]) ignore_diff.change_line( source_range.start.line, original_file_dict[filename][source_range.start.line-1], original_file_dict[filename][source_range.start.line-1].rstrip() + ' ' + ignore_comment) if filename in file_diff_dict: ignore_diff = file_diff_dict[filename] + ignore_diff else: if not no_orig and isfile(filename): shutil.copy2(filename, filename + '.orig') file_diff_dict[filename] = ignore_diff new_filename = ignore_diff.rename if ignore_diff.rename else filename with open(new_filename, mode='w', encoding='utf-8') as file: file.writelines(ignore_diff.modified) return file_diff_dict
def test_apply_orig_option(self): uut = ApplyPatchAction() with make_temp() as f_a, make_temp() as f_b: file_dict = { f_a: ["1\n", "2\n", "3\n"], f_b: ["1\n", "2\n", "3\n"] } expected_file_dict = { f_a: ["1\n", "2\n", "3_changed\n"], f_b: ["1\n", "2\n", "3_changed\n"] } file_diff_dict = {} diff = Diff(file_dict[f_a]) diff.change_line(3, "3\n", "3_changed\n") uut.apply(Result("origin", "msg", diffs={f_a: diff}), file_dict, file_diff_dict, no_orig=True) diff = Diff(file_dict[f_b]) diff.change_line(3, "3\n", "3_changed\n") uut.apply(Result("origin", "msg", diffs={f_b: diff}), file_dict, file_diff_dict, no_orig=False) self.assertFalse(isfile(f_a+".orig")) self.assertTrue(isfile(f_b+".orig"))
def test_apply(self): uut = ApplyPatchAction() fh_a, f_a = mkstemp() fh_b, f_b = mkstemp() fh_c, f_c = mkstemp() os.close(fh_a) os.close(fh_b) os.close(fh_c) file_dict = { f_a: ["1\n", "2\n", "3\n"], f_b: ["1\n", "2\n", "3\n"], f_c: ["1\n", "2\n", "3\n"] } expected_file_dict = { f_a: ["1\n", "3_changed\n"], f_b: ["1\n", "2\n", "3_changed\n"], f_c: ["1\n", "2\n", "3\n"] } file_diff_dict = {} diff = Diff(file_dict[f_a]) diff.delete_line(2) uut.apply_from_section(Result("origin", "msg", diffs={f_a: diff}), file_dict, file_diff_dict, Section("t")) diff = Diff(file_dict[f_a]) diff.change_line(3, "3\n", "3_changed\n") uut.apply_from_section(Result("origin", "msg", diffs={f_a: diff}), file_dict, file_diff_dict, Section("t")) diff = Diff(file_dict[f_b]) diff.change_line(3, "3\n", "3_changed\n") uut.apply(Result("origin", "msg", diffs={f_b: diff}), file_dict, file_diff_dict) for filename in file_diff_dict: file_dict[filename] = file_diff_dict[filename].modified self.assertEqual(file_dict, expected_file_dict) with open(f_a) as fa: self.assertEqual(file_dict[f_a], fa.readlines()) with open(f_b) as fb: self.assertEqual(file_dict[f_b], fb.readlines()) with open(f_c) as fc: # File c is unchanged and should be untouched self.assertEqual([], fc.readlines()) os.remove(f_a) os.remove(f_b) os.remove(f_c)
def run(self, filename, file, timeout: int=DEFAULT_TIMEOUT, ignore_regex: str="[.\/]example\.com"): """ Find links in any text file and check if they are valid. A link is considered valid if the server responds with a 2xx code. This bear can automatically fix redirects, but ignores redirect URLs that have a huge difference with the original URL. :param timeout: Request timeout period. :param ignore_regex: A regex for urls to ignore. """ for line_number, link, code in InvalidLinkBear.find_links_in_file( file, timeout, ignore_regex): if code is None: yield Result.from_values( origin=self, message=('Broken link - unable to connect to ' '{url}').format(url=link), file=filename, line=line_number, severity=RESULT_SEVERITY.MAJOR) elif not 200 <= code < 300: # HTTP status 404, 410 or 50x if code in (404, 410) or 500 <= code < 600: yield Result.from_values( origin=self, message=('Broken link - unable to connect to {url} ' '(HTTP Error: {code})' ).format(url=link, code=code), file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL) if 300 <= code < 400: # HTTP status 30x redirect_url = requests.head(link, allow_redirects=True).url matcher = SequenceMatcher( None, redirect_url, link) if (matcher.real_quick_ratio() > 0.7 and matcher.ratio()) > 0.7: diff = Diff(file) current_line = file[line_number - 1] start = current_line.find(link) end = start + len(link) replacement = current_line[:start] + \ redirect_url + current_line[end:] diff.change_line(line_number, current_line, replacement) yield Result.from_values( self, 'This link redirects to ' + redirect_url, diffs={filename: diff}, file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL)
def test_apply(self): file_dict = {"f_a": ["1", "2", "3"], "f_b": ["1", "2", "3"]} expected_file_dict = {"f_a": ["1", "3_changed"], "f_b": ["1", "2", "3"]} diff = Diff(file_dict["f_a"]) diff.delete_line(2) diff.change_line(3, "3", "3_changed") uut = Result("origin", "msg", diffs={"f_a": diff}) uut.apply(file_dict) self.assertEqual(file_dict, expected_file_dict)
def test_acquire_actions_and_apply_single(self): with make_temp() as testfile_path: file_dict = {testfile_path: ['1\n', '2\n', '3\n']} diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, '3\n', '3_changed\n') with simulate_console_inputs('a', 'n') as generator: with retrieve_stdout() as sio: ApplyPatchAction.is_applicable = staticmethod( lambda *args: True) acquire_actions_and_apply(self.console_printer, Section(''), self.file_diff_dict, Result( 'origin', 'message', diffs={testfile_path: diff}), file_dict, apply_single=True) self.assertEqual(generator.last_input, -1) self.assertIn('', sio.getvalue()) class InvalidateTestAction(ResultAction): is_applicable = staticmethod(lambda *args: True) def apply(*args, **kwargs): ApplyPatchAction.is_applicable = staticmethod( lambda *args: 'ApplyPatchAction cannot be applied.') old_applypatch_is_applicable = ApplyPatchAction.is_applicable ApplyPatchAction.is_applicable = staticmethod(lambda *args: True) cli_actions = [ApplyPatchAction(), InvalidateTestAction()] with simulate_console_inputs('a') as generator: with retrieve_stdout() as sio: acquire_actions_and_apply(self.console_printer, Section(''), self.file_diff_dict, Result( 'origin', 'message', diffs={testfile_path: diff}), file_dict, cli_actions=cli_actions, apply_single=True) self.assertEqual(generator.last_input, -1) action_fail = 'Failed to execute the action' self.assertNotIn(action_fail, sio.getvalue()) apply_path_desc = ApplyPatchAction().get_metadata().desc self.assertEqual(sio.getvalue().count(apply_path_desc), 0) ApplyPatchAction.is_applicable = old_applypatch_is_applicable
def test_acquire_actions_and_apply(self): with make_temp() as testfile_path: file_dict = {testfile_path: ["1\n", "2\n", "3\n"]} diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, "3\n", "3_changed\n") with simulate_console_inputs(1, 0) as generator, \ retrieve_stdout() as sio: ApplyPatchAction.is_applicable = staticmethod( lambda *args: True) acquire_actions_and_apply(self.console_printer, self.log_printer, Section(""), self.file_diff_dict, Result("origin", "message", diffs={ testfile_path: diff}), file_dict) self.assertEqual(generator.last_input, 1) self.assertIn(ApplyPatchAction.success_message, sio.getvalue()) class InvalidateTestAction(ResultAction): is_applicable = staticmethod(lambda *args: True) def apply(*args, **kwargs): ApplyPatchAction.is_applicable = staticmethod( lambda *args: False) old_applypatch_is_applicable = ApplyPatchAction.is_applicable ApplyPatchAction.is_applicable = staticmethod(lambda *args: True) cli_actions = [ApplyPatchAction(), InvalidateTestAction()] with simulate_console_inputs(2, 1, 0) as generator, \ retrieve_stdout() as sio: acquire_actions_and_apply(self.console_printer, self.log_printer, Section(""), self.file_diff_dict, Result("origin", "message", diffs={testfile_path: diff}), file_dict, cli_actions=cli_actions) self.assertEqual(generator.last_input, 2) action_fail = "Failed to execute the action" self.assertNotIn(action_fail, sio.getvalue()) apply_path_desc = ApplyPatchAction().get_metadata().desc self.assertEqual(sio.getvalue().count(apply_path_desc), 1) ApplyPatchAction.is_applicable = old_applypatch_is_applicable
def test_apply(self): uut = ApplyPatchAction() with make_temp() as f_a, make_temp() as f_b, make_temp() as f_c: file_dict = { f_a: ['1\n', '2\n', '3\n'], f_b: ['1\n', '2\n', '3\n'], f_c: ['1\n', '2\n', '3\n'] } expected_file_dict = { f_a: ['1\n', '3_changed\n'], f_b: ['1\n', '2\n', '3_changed\n'], f_c: ['1\n', '2\n', '3\n'] } file_diff_dict = {} diff = Diff(file_dict[f_a]) diff.delete_line(2) uut.apply_from_section(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict, Section('t')) diff = Diff(file_dict[f_a]) diff.change_line(3, '3\n', '3_changed\n') uut.apply_from_section(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict, Section('t')) diff = Diff(file_dict[f_b]) diff.change_line(3, '3\n', '3_changed\n') uut.apply(Result('origin', 'msg', diffs={f_b: diff}), file_dict, file_diff_dict) for filename in file_diff_dict: file_dict[filename] = file_diff_dict[filename].modified self.assertEqual(file_dict, expected_file_dict) with open(f_a) as fa: self.assertEqual(file_dict[f_a], fa.readlines()) with open(f_b) as fb: self.assertEqual(file_dict[f_b], fb.readlines()) with open(f_c) as fc: # File c is unchanged and should be untouched self.assertEqual([], fc.readlines())
def run(self, filename, file, timeout: int=DEFAULT_TIMEOUT): """ Find links in any text file and check if they are valid. A link is considered valid if the server responds with a 2xx code. This bear can automatically fix redirects. :param timeout: Request timeout period. """ for line_number, link, code in InvalidLinkBear.find_links_in_file( file, timeout): if code is None: yield Result.from_values( origin=self, message=('Broken link - unable to connect to ' '{url}').format(url=link), file=filename, line=line_number, severity=RESULT_SEVERITY.MAJOR) elif not 200 <= code < 300: if 400 <= code < 600: # HTTP status 40x or 50x yield Result.from_values( origin=self, message=('Broken link - unable to connect to {url} ' '(HTTP Error: {code})' ).format(url=link, code=code), file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL) if 300 <= code < 400: # HTTP status 30x redirect_url = requests.head(link, allow_redirects=True).url diff = Diff(file) current_line = file[line_number - 1] start = current_line.find(link) end = start + len(link) replacement = current_line[:start] + \ redirect_url + current_line[end:] diff.change_line(line_number, current_line, replacement) yield Result.from_values( self, 'This link redirects to ' + redirect_url, diffs={filename: diff}, file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL)
def test_json_diff(self): file_dict = { "f_a": ["1", "2", "3"], "f_b": ["1", "2", "3"] } diff = Diff(file_dict['f_a']) diff.delete_line(2) diff.change_line(3, "3", "3_changed") uut = Result("origin", "msg", diffs={"f_a": diff}).__json__(True) self.assertEqual(uut["diffs"]['f_a'].__json__(), "--- \n" "+++ \n" "@@ -1,3 +1,2 @@\n" " 1-2-3+3_changed") JSONEncoder = create_json_encoder(use_relpath=True) json_dump = json.dumps(diff, cls=JSONEncoder, sort_keys=True) self.assertEqual( json_dump, '"--- \\n+++ \\n@@ -1,3 +1,2 @@\\n 1-2-3+3_changed"')
def test_acquire_actions_and_apply(self): with make_temp() as testfile_path: file_dict = {testfile_path: ["1\n", "2\n", "3\n"]} diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, "3\n", "3_changed\n") with simulate_console_inputs(1, 0) as generator: ApplyPatchAction.is_applicable = staticmethod( lambda *args: True) acquire_actions_and_apply(self.console_printer, self.log_printer, Section(""), self.file_diff_dict, Result("origin", "message", diffs={ testfile_path: diff}), file_dict) self.assertEqual(generator.last_input, 1)
def test_json_diff(self): file_dict = { 'f_a': ['1', '2', '3'], 'f_b': ['1', '2', '3'] } diff = Diff(file_dict['f_a']) diff.delete_line(2) diff.change_line(3, '3', '3_changed') uut = Result('origin', 'msg', diffs={'f_a': diff}).__json__(True) self.assertEqual(uut['diffs']['f_a'].__json__(), '--- \n' '+++ \n' '@@ -1,3 +1,2 @@\n' ' 1-2-3+3_changed') JSONEncoder = create_json_encoder(use_relpath=True) json_dump = json.dumps(diff, cls=JSONEncoder, sort_keys=True) self.assertEqual( json_dump, '"--- \\n+++ \\n@@ -1,3 +1,2 @@\\n 1-2-3+3_changed"')
def process_output(self, output, filename, file): output = json.loads(output) for issue in output: assert issue["startLine"] == issue["endLine"] diff = Diff(file) line_nr = issue["startLine"] line_to_change = file[line_nr-1] newline = line_to_change.replace(issue["from"], issue["to"]) diff.change_line(line_nr, line_to_change, newline) yield Result.from_values( origin=self, message=issue["hint"], file=filename, severity=self.severity_map[issue["severity"]], line=issue["startLine"], diffs={filename: diff})
def test_addition(self): self.assertRaises(TypeError, self.uut.__add__, 5) result_file = ["1", "2", "2"] other = Diff(self.file) other.delete_line(1) other.change_line(2, "1", "2") other.add_lines(0, ["1"]) self.uut.delete_line(1) self.uut.delete_line(3) self.uut.change_line(4, "4", "2") result = self.uut + other self.assertEqual(result.modified, result_file) # Make sure it didn't happen in place! self.assertNotEqual(self.uut.modified, result_file)
def run(self, filename, file, use_spaces: bool, allow_trailing_whitespace: bool=False, tab_width: int=SpacingHelper.DEFAULT_TAB_WIDTH): """ Checks the space consistency for each line. :param use_spaces: True if spaces are to be used instead of tabs. :param allow_trailing_whitespace: Whether to allow trailing whitespace or not. :param tab_width: Number of spaces representing one tab. """ results = [] spacing_helper = SpacingHelper(tab_width) for line_number, line in enumerate(file): replacement = line if not allow_trailing_whitespace: replacement = replacement.rstrip(" \t\n") + "\n" if use_spaces: replacement = spacing_helper.replace_tabs_with_spaces( replacement) else: replacement = spacing_helper.replace_spaces_with_tabs( replacement) if replacement != line: diff = Diff() diff.change_line(line_number + 1, line, replacement) results.append(PatchResult(self, _("Line contains spacing " "inconsistencies."), {filename: diff}, filename, line_nr=line_number+1)) return results
def generate_diff(comments, file, filename, line, line_number, pos): todo_source_range = SourceRange.from_values(filename, line_number, pos + 1) affected_comment_sourcerange = [ c for c in comments if todo_source_range in c] affected_len = len(affected_comment_sourcerange) if affected_len == 0: return {} assert affected_len == 1, 'More than 1 affected comment source ranges' comment_sourcerange = affected_comment_sourcerange[0] comment_start = comment_sourcerange.start.column comment_end = comment_sourcerange.end.column in_multi_line_comment = ( comment_sourcerange.start.line != comment_sourcerange.end.line ) line_before_todo_comment = line[:comment_start - 1].rstrip() line_behind_todo_comment = line[comment_end:].rstrip() line_replacement = line_before_todo_comment + line_behind_todo_comment diff = Diff(file) if line_replacement and not in_multi_line_comment: diff.change_line( line_number, line, line_replacement + '\n') elif line_replacement and in_multi_line_comment: text_replacement = line[pos:] diff.change_line( line_number, line, line.replace(text_replacement, '').rstrip() + '\n') else: diff.delete_line(line_number) return {filename: diff}
def test_apply_delete(self): uut = ApplyPatchAction() with make_temp() as f_a: file_dict = {f_a: ['1\n', '2\n', '3\n']} file_diff_dict = {} diff = Diff(file_dict[f_a], delete=True) uut.apply(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict) self.assertFalse(isfile(f_a)) self.assertTrue(isfile(f_a+'.orig')) os.remove(f_a+'.orig') diff = Diff(file_dict[f_a]) diff.change_line(3, '3\n', '3_changed\n') uut.apply(Result('origin', 'msg', diffs={f_a: diff}), file_dict, file_diff_dict) self.assertFalse(isfile(f_a+'.orig')) # Recreate file so that context manager make_temp() can delete it open(f_a, 'w').close()
def run(self, filename, file): ''' Yields results for all invalid links in a file. ''' for line_number, link, code in InvalidLinkBear.find_links_in_file(file): if code is None: yield Result.from_values( origin=self, message=('Broken link - unable to connect to ' '{url}').format(url=link), file=filename, line=line_number, severity=RESULT_SEVERITY.MAJOR) elif not 200 <= code < 300: if 400 <= code < 600: # HTTP status 40x or 50x yield Result.from_values( origin=self, message=('Broken link - unable to connect to {url} ' '(HTTP Error: {code})' ).format(url=link, code=code), file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL) if 300 <= code < 400: # HTTP status 30x redirect_url = requests.head(link, allow_redirects=True).url diff = Diff(file) current_line = file[line_number - 1] start = current_line.find(link) end = start + len(link) replacement = current_line[:start] + \ redirect_url + current_line[end:] diff.change_line(line_number, current_line, replacement) yield Result.from_values( self, 'This link redirects to ' + redirect_url, diffs={filename: diff}, file=filename, line=line_number, severity=RESULT_SEVERITY.NORMAL)
def test_apply(self): file_dict = { "f_a": ["1", "2", "3"], "f_b": ["1", "2", "3"] } expected_file_dict = { "f_a": ["1", "3_changed"], "f_b": ["1", "2", "3"] } diff = Diff() diff.delete_line(2) diff.change_line(3, "3", "3_changed") uut = PatchResult("origin", "msg", {"f_a": diff}) uut.apply(file_dict) self.assertEqual(file_dict, expected_file_dict) uut = PatchResult("origin", "msg", {}) for action in uut.get_actions(): action.apply(uut, {}, {}) # All those actions should be able to apply this result self.assertEqual(len(uut.get_actions()), 1)
def run(self, filename, file, use_spaces: bool, allow_trailing_whitespace: bool=False, indent_size: int=SpacingHelper.DEFAULT_TAB_WIDTH, enforce_newline_at_EOF: bool=True): ''' Check and correct spacing for all textual data. This includes usage of tabs vs. spaces, trailing whitespace and (missing) newlines before the end of the file. :param use_spaces: True if spaces are to be used instead of tabs. :param allow_trailing_whitespace: Whether to allow trailing whitespace or not. :param indent_size: Number of spaces per indentation level. :param enforce_newline_at_EOF: Whether to enforce a newline at the End Of File. ''' spacing_helper = SpacingHelper(indent_size) result_texts = [] additional_info_texts = [] for line_number, line in enumerate(file, start=1): replacement = line if enforce_newline_at_EOF: # Since every line contains at the end at least one \n, only # the last line could potentially not have one. So we don't # need to check whether the current line_number is the last # one. if replacement[-1] != '\n': replacement += '\n' result_texts.append('No newline at EOF.') additional_info_texts.append( "A trailing newline character ('\\n') is missing from " 'your file. ' '<http://stackoverflow.com/a/5813359/3212182> gives ' 'more information about why you might need one.') if not allow_trailing_whitespace: replacement = replacement.rstrip(' \t\n') + '\n' if replacement != line.rstrip('\n') + '\n': result_texts.append('Trailing whitespaces.') additional_info_texts.append( 'Your source code contains trailing whitespaces. ' 'Those usually have no meaning. Please consider ' 'removing them.') if use_spaces: pre_replacement = replacement replacement = spacing_helper.replace_tabs_with_spaces( replacement) if replacement != pre_replacement: result_texts.append('Tabs used instead of spaces.') else: pre_replacement = replacement replacement = spacing_helper.replace_spaces_with_tabs( replacement) if replacement != pre_replacement: result_texts.append('Spaces used instead of tabs.') if len(result_texts) > 0: diff = Diff(file) diff.change_line(line_number, line, replacement) inconsistencies = ''.join('\n- ' + string for string in result_texts) yield Result.from_values( self, 'Line contains following spacing inconsistencies:' + inconsistencies, diffs={filename: diff}, file=filename, line=line_number, additional_info='\n\n'.join(additional_info_texts)) result_texts = [] additional_info_texts = []
def test_print_result(self): print_result(self.console_printer, None, self.file_diff_dict, 'illegal value', {}) with simulate_console_inputs(0): print_result(self.console_printer, self.section, self.file_diff_dict, Result('origin', 'msg', diffs={}), {}) with make_temp() as testfile_path: file_dict = { testfile_path: ['1\n', '2\n', '3\n'], 'f_b': ['1', '2', '3'] } diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, '3\n', '3_changed\n') ApplyPatchAction.is_applicable = staticmethod(lambda *args: True) # Interaction must be closed by the user with `0` if it's not a # param with simulate_console_inputs('INVALID', -1, 1, 0, 3) as input_generator: curr_section = Section('') print_section_beginning(self.console_printer, curr_section) print_result( self.console_printer, curr_section, self.file_diff_dict, Result('origin', 'msg', diffs={testfile_path: diff}), file_dict) self.assertEqual(input_generator.last_input, 3) self.file_diff_dict.clear() with open(testfile_path) as f: self.assertEqual(f.readlines(), ['1\n', '3_changed\n']) os.remove(testfile_path + '.orig') name, section = get_action_info(curr_section, TestAction().get_metadata(), failed_actions=set()) self.assertEqual(input_generator.last_input, 4) self.assertEqual(str(section), " {param : '3'}") self.assertEqual(name, 'TestAction') # Check if the user is asked for the parameter only the first time. # Use OpenEditorAction that needs this parameter (editor command). with simulate_console_inputs(1, 'test_editor', 0, 1, 0) as generator: OpenEditorAction.is_applicable = staticmethod(lambda *args: True) patch_result = Result('origin', 'msg', diffs={testfile_path: diff}) patch_result.file = 'f_b' print_result(self.console_printer, curr_section, self.file_diff_dict, patch_result, file_dict) # choose action, choose editor, choose no action (-1 -> 2) self.assertEqual(generator.last_input, 2) # It shoudn't ask for parameter again print_result(self.console_printer, curr_section, self.file_diff_dict, patch_result, file_dict) self.assertEqual(generator.last_input, 4)
def run(self, filename, file, use_spaces: bool, allow_trailing_whitespace: bool = False, tab_width: int = SpacingHelper.DEFAULT_TAB_WIDTH, enforce_newline_at_EOF: bool = True): ''' Checks the space consistency for each line. :param use_spaces: True if spaces are to be used instead of tabs :param allow_trailing_whitespace: Whether to allow trailing whitespace or not :param tab_width: Number of spaces representing one tab :param enforce_newline_at_EOF: Whether to enforce a newline at the End Of File ''' spacing_helper = SpacingHelper(tab_width) result_texts = [] for line_number, line in enumerate(file, start=1): replacement = line if enforce_newline_at_EOF: # Since every line contains at the end at least one \n, only # the last line could potentially not have one. So we don't # need to check whether the current line_number is the last # one. if replacement[-1] != '\n': replacement += '\n' result_texts.append('No newline at EOF.') if not allow_trailing_whitespace: replacement = replacement.rstrip(' \t\n') + '\n' if replacement != line.rstrip('\n') + '\n': result_texts.append('Trailing whitespaces.') if use_spaces: pre_replacement = replacement replacement = spacing_helper.replace_tabs_with_spaces( replacement) if replacement != pre_replacement: result_texts.append('Tabs used instead of spaces.') else: pre_replacement = replacement replacement = spacing_helper.replace_spaces_with_tabs( replacement) if replacement != pre_replacement: result_texts.append('Spaces used instead of tabs.') if len(result_texts) > 0: diff = Diff(file) diff.change_line(line_number, line, replacement) inconsistencies = ''.join('\n- ' + string for string in result_texts) yield Result.from_values( self, 'Line contains following spacing inconsistencies:' + inconsistencies, diffs={filename: diff}, file=filename, line=line_number) result_texts = []
def test_print_result(self): print_result(self.console_printer, self.log_printer, None, self.file_diff_dict, "illegal value", {}) with simulate_console_inputs(0): print_result(self.console_printer, self.log_printer, None, self.file_diff_dict, Result("origin", "msg", diffs={}), {}) with make_temp() as testfile_path: file_dict = { testfile_path: ["1\n", "2\n", "3\n"], "f_b": ["1", "2", "3"] } diff = Diff(file_dict[testfile_path]) diff.delete_line(2) diff.change_line(3, "3\n", "3_changed\n") with simulate_console_inputs(1), self.assertRaises(ValueError): ApplyPatchAction.is_applicable = staticmethod( lambda *args: True) print_result( self.console_printer, self.log_printer, None, self.file_diff_dict, Result("origin", "msg", diffs={testfile_path: diff}), file_dict) # Interaction must be closed by the user with `0` if it's not a # param with simulate_console_inputs("INVALID", -1, 1, 0, 3) as input_generator: curr_section = Section("") print_section_beginning(self.console_printer, curr_section) print_result( self.console_printer, self.log_printer, curr_section, self.file_diff_dict, Result("origin", "msg", diffs={testfile_path: diff}), file_dict) self.assertEqual(input_generator.last_input, 3) self.file_diff_dict.clear() with open(testfile_path) as f: self.assertEqual(f.readlines(), ["1\n", "3_changed\n"]) os.remove(testfile_path + ".orig") name, section = get_action_info(curr_section, TestAction().get_metadata()) self.assertEqual(input_generator.last_input, 4) self.assertEqual(str(section), " {param : '3'}") self.assertEqual(name, "TestAction") # Check if the user is asked for the parameter only the first time. # Use OpenEditorAction that needs this parameter (editor command). with simulate_console_inputs(1, "test_editor", 0, 1, 0) as generator: OpenEditorAction.is_applicable = staticmethod(lambda *args: True) patch_result = Result("origin", "msg", diffs={testfile_path: diff}) patch_result.file = "f_b" print_result(self.console_printer, self.log_printer, curr_section, self.file_diff_dict, patch_result, file_dict) # choose action, choose editor, choose no action (-1 -> 2) self.assertEqual(generator.last_input, 2) # It shoudn't ask for parameter again print_result(self.console_printer, self.log_printer, curr_section, self.file_diff_dict, patch_result, file_dict) self.assertEqual(generator.last_input, 4)