def test_apply(self): 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\n"], "f_b": ["1\n", "3_changed\n"], "f_c": ["1\n", "2\n", "3\n"] } diff_dict = {"f_b": Diff()} diff_dict["f_b"].change_line(3, "3\n", "3_changed\n") section = Section("") section.append(Setting("editor", "")) uut = OpenEditorAction() os.system = self.fake_edit diff_dict = uut.apply_from_section(Result("origin", "msg", "f_a"), file_dict, diff_dict, section) diff_dict = uut.apply_from_section(Result("origin", "msg", "f_b"), file_dict, diff_dict, section) for filename in diff_dict: file_dict[filename] = diff_dict[filename].apply( file_dict[filename]) self.assertEqual(file_dict, expected_file_dict)
def test_treat_seperated_imports_independently(self): test_file = (""" import re import requests from urllib.parse import urlparse from coalib.results.Diff import Diff from coalib.bears.LocalBear import LocalBear """.splitlines(True)) expected = (""" import re from urllib.parse import urlparse import requests from coalib.bears.LocalBear import LocalBear from coalib.results.Diff import Diff """.splitlines(True)) diff = Diff(expected) settings = {'treat_seperated_imports_independently': True} self.assertEqual( list(self.uut.run('', test_file, **settings))[0].diffs[''].modified, diff.modified) self.assertEqual( list( self.uut.run( '', ['import curses\n', 'import io\n', 'coala = "coala"\n'], **settings)), [])
def test_print_result(self): self.uut.print = lambda x: x self.assertEqual("| | | [{normal}] {bear}:".format(normal=RESULT_SEVERITY.__str__(RESULT_SEVERITY.NORMAL), bear="origin") + "\n| | | message", self.uut._print_result(Result("origin", "message"))) builtins.__dict__["input"] = lambda x: 0 self.uut.print_result(PatchResult("origin", "msg", {}), {}) (testfile, testfile_path) = tempfile.mkstemp() os.close(testfile) file_dict = { testfile_path: ["1\n", "2\n", "3\n"], "f_b": ["1", "2", "3"] } diff = Diff() diff.delete_line(2) diff.change_line(3, "3\n", "3_changed\n") builtins.__dict__["input"] = self.generate_input # To assure user can rechose if he didn't chose wisely self.uut.print_result(PatchResult("origin", "msg", {testfile_path: diff}), file_dict) self.assertEqual(self.curr, 1) self.uut.finalize(file_dict) with open(testfile_path) as f: self.assertEqual(f.readlines(), ["1\n", "3_changed\n"]) os.remove(testfile_path) name, section = self.uut._get_action_info(TestAction().get_metadata()) self.assertEqual(str(section), " {param : 3}") self.assertEqual(name, "TestAction") builtins.__dict__["input"] = lambda x: x
def generate_diff(file, doc_comment, new_comment): """ Generates diff between the original doc_comment and its fix new_comment which are instances of DocumentationComment. :param doc_comment: Original instance of DocumentationComment. :param new_comment: Fixed instance of DocumentationComment. :return: Diff instance. """ diff = Diff(file) # We need to update old comment positions, as `assemble()` # prepends indentation for first line. old_range = TextRange.from_values(doc_comment.range.start.line, 1, doc_comment.range.end.line, doc_comment.range.end.column) # Clearing cached assemble() so a fresh one is fetched. new_comment.assemble.cache_clear() diff.replace(old_range, new_comment.assemble()) return diff
def run(self, filename, file, file_naming_convention: str = "snake"): """ Checks whether the filename follows a certain naming-convention. :param file_naming_convention: The naming-convention. Supported values are: - ``camel`` (``thisIsCamelCase``) - ``pascal`` (``ThisIsPascalCase``) - ``snake`` (``this_is_snake_case``) """ head, tail = os.path.split(filename) filename_without_extension, extension = os.path.splitext(tail) try: new_name = self._naming_convention[file_naming_convention]( filename_without_extension) except KeyError: self.err("Invalid file-naming-convention provided: " + file_naming_convention) return if new_name != filename_without_extension: diff = Diff(file, rename=os.path.join(head, new_name + extension)) yield Result( self, "Filename does not follow {} naming-convention.".format( file_naming_convention), diff.affected_code(filename), diffs={filename: diff})
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, self.console_printer) self.assertEqual(generator.last_input, -1) self.assertEqual(stdout.getvalue(), """ Project wide: **** origin [Section: someSection] **** ! ! [Severity: NORMAL] ! ! {}\n""".format(highlight_text(self.no_color, 'message', style=BackgroundMessageStyle)))
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 apply(self, result, original_file_dict, file_diff_dict, editor: str): """ Open a temporary clone of the file in an editor. :param editor: The editor to open the file with. """ filename = result.file original_file = original_file_dict[filename] diff = file_diff_dict.get(filename, Diff()) current_file = diff.apply(original_file) # Prefix is nice for the user so he has an indication that its the # right file he's editing temphandle, tempname = tempfile.mkstemp(os.path.basename(filename)) os.close(temphandle) with open(tempname, "w") as temphandle: temphandle.writelines(current_file) editor_arg = EDITOR_ARGS.get(editor.strip(), None) if editor_arg: editor = editor + " " + editor_arg # Dear user, you wanted an editor, so you get it. But do you really # think you can do better than we? os.system(editor + " " + tempname) with open(tempname) as temphandle: new_file = temphandle.readlines() os.remove(tempname) intermediate_diff = Diff.from_string_arrays(current_file, new_file) file_diff_dict[filename] = diff + intermediate_diff return file_diff_dict
def generate_label_diff(file, filename, line, line_number, match_object, expected_label): """ Generates a diff for a missing or wrong control loop end label. Missing labels will be added, wrong ones replaced, content after the label will be left untouched. :param match_object: A Match object containing the groups ``close`` containing the closing delimiters of a control end tag and the optional group ``label`` containing the end tag label. :param expected_label: The expected label for that control block. """ diff = Diff(file) content_before = line[:match_object.end('close')] suffix_start = match_object.end('close') # if a label is there, we cut it out, by starting the kept suffix after it if match_object.group('label') is not None: suffix_start = match_object.end('label') content_after = line[suffix_start:] replacement = '{before}{label}{after}'.format(before=content_before, after=content_after, label=expected_label) diff.change_line(line_number, line, replacement) return {filename: diff}
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 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 run(self, filename, file, file_naming_convention: str = None, ignore_uppercase_filenames: bool = True): """ Checks whether the filename follows a certain naming-convention. :param file_naming_convention: The naming-convention. Supported values are: - ``auto`` to guess the correct convention. Defaults to ``snake`` if the correct convention cannot be guessed. - ``camel`` (``thisIsCamelCase``) - ``kebab`` (``this-is-kebab-case``) - ``pascal`` (``ThisIsPascalCase``) - ``snake`` (``this_is_snake_case``) - ``space`` (``This Is Space Case``) :param ignore_uppercase_filenames: Whether or not to ignore fully uppercase filenames completely, e.g. COPYING, LICENSE etc. """ head, tail = os.path.split(filename) filename_without_extension, extension = os.path.splitext(tail) if file_naming_convention is None: self.warn('Please specify a file naming convention explicitly' ' or use "auto".') file_naming_convention = 'auto' else: file_naming_convention = file_naming_convention.lower() if file_naming_convention == 'auto': if extension in self._language_naming_convention: file_naming_convention = self._language_naming_convention[ extension] else: self.warn('The file naming convention could not be guessed. ' 'Using the default "snake" naming convention.') file_naming_convention = 'snake' try: new_name = self._naming_convention[file_naming_convention]( filename_without_extension) except KeyError: self.err('Invalid file-naming-convention provided: ' + file_naming_convention) return if ignore_uppercase_filenames and filename_without_extension.isupper(): return if new_name != filename_without_extension: diff = Diff(file, rename=os.path.join(head, new_name + extension)) yield Result( self, 'Filename does not follow {} naming-convention.'.format( file_naming_convention), diff.affected_code(filename), diffs={filename: diff})
def _get_diff(self): if self.treat_seperated_imports_independently: import_stmts = PyImportSortBear._seperate_imports(self.file) sorted_imps = [] for units in import_stmts: sort_imports = SortImports(file_contents=''.join( [x[1] for x in units]), **self.isort_settings) sort_imports = sort_imports.output.splitlines(True) sorted_imps.append((units, sort_imports)) diff = Diff(self.file) for old, new in sorted_imps: start = old[0][0] end = start + len(old) - 1 diff.delete_lines(start, end) assert isinstance(new, list) diff.add_lines(start, list(new)) if diff.modified != diff._file: return diff else: sort_imports = SortImports(file_contents=''.join(self.file), **self.isort_settings) new_file = tuple(sort_imports.output.splitlines(True)) if new_file != tuple(self.file): diff = Diff.from_string_arrays(self.file, new_file) return diff return None
def test_function(self): test_file_content = load_testfile(test_file).splitlines(True) arguments = {'language': 'python', 'docstyle': 'default'} if optional_setting: arguments.update(optional_setting) section = Section('test-section') for key, value in arguments.items(): section[key] = value with execute_bear( DocumentationStyleBear(section, Queue()), test_file, test_file_content, **arguments) as results: diff = Diff(test_file_content) for result in results: # Only the given test file should contain a patch. self.assertEqual(len(result.diffs), 1) diff += result.diffs[test_file] correct_file_content = load_testfile(expected_file).splitlines(True) self.assertEqual(correct_file_content, diff.modified)
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 test_is_applicable_empty_patch(self): diff = Diff([], rename='new_name') result = Result('', '', diffs={'f': diff}) # Two renames donot result in any change self.assertEqual( ApplyPatchAction.is_applicable(result, {}, {'f': diff}), 'The given patches do not change anything anymore.')
def test_print_result_interactive_small_patch(self, apply_from_section, _): file_dict = {'a': ['a\n', 'b\n', 'c\n'], 'b': ['old_first\n']} diff_dict = {'a': Diff(file_dict['a']), 'b': Diff(file_dict['b'])} diff_dict['a'].add_lines(1, ['test\n']) diff_dict['a'].delete_line(3) result = Result('origin', 'msg', diffs=diff_dict) section = Section('test') print_result(self.console_printer, section, self.file_diff_dict, result, file_dict, True) apply_from_section.assert_called_once_with( result, file_dict, self.file_diff_dict, section)
def test_is_applicable_conflict(self): diff = Diff(["1\n", "2\n", "3\n"]) diff.add_lines(2, ['a line']) conflict_result = Result("", "", diffs={'f': diff}) # Applying the same diff twice will result in a conflict self.assertFalse( ApplyPatchAction.is_applicable(conflict_result, {}, {'f': diff}))
def test_print_result_interactive_big_patch(self, diffs_info, _): file_dict = {'a': ['a\n', 'b\n', 'c\n'], 'b': ['old_first\n']} diff_dict = {'a': Diff(file_dict['a']), 'b': Diff(file_dict['b'])} diff_dict['a'].add_lines(1, ['test\n', 'test1\n', 'test2\n']) diff_dict['a'].delete_line(3) diff_dict['a'].add_lines(3, ['3test\n']) result = Result('origin', 'msg', diffs=diff_dict) section = Section('test') print_result(self.console_printer, section, self.file_diff_dict, result, file_dict, True) diffs_info.assert_called_once_with(diff_dict, self.console_printer)
def test_apply_empty(self): with retrieve_stdout() as stdout: test_result = Result('origin', 'message', diffs={'a': Diff([])}) file_dict = {'a': []} self.assertEqual( self.uut.apply_from_section(test_result, file_dict, {}, self.section), {}) self.assertEqual(stdout.getvalue(), '')
def test_is_applicable_conflict(self): diff = Diff(['1\n', '2\n', '3\n']) diff.add_lines(2, ['a line']) conflict_result = Result('', '', diffs={'f': diff}) # Applying the same diff twice will result in a conflict self.assertIn( 'Two or more patches conflict with each other: ', ApplyPatchAction.is_applicable(conflict_result, {}, {'f': diff}))
def test_print_result_interactive_big_patch(self, diffs_info, _): file_dict = {"a": ["a\n", "b\n", "c\n"], "b": ["old_first\n"]} diff_dict = {"a": Diff(file_dict['a']), "b": Diff(file_dict['b'])} diff_dict["a"].add_lines(1, ["test\n", "test1\n", "test2\n"]) diff_dict["a"].delete_line(3) diff_dict["a"].add_lines(3, ["3test\n"]) result = Result("origin", "msg", diffs=diff_dict) section = Section("test") print_result(self.console_printer, self.log_printer, section, self.file_diff_dict, result, file_dict, True) diffs_info.assert_called_once_with(diff_dict, self.console_printer)
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_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 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 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 correct_single_line_str( self, filename, file, sourcerange, preferred_quotation, force_preferred_quotation: bool = False, ): """ Corrects a given single line string assuming it does not use the preferred quotation. If the preferred quotation mark is used inside the string, no correction will be made. This function will yield one or no Result objects. :param filename: The filename of the file to correct the line in. :param file: The file contents as list of lines. :param sourcerange: The sourcerange indicating where to find the string. :param preferred_quotation: ``'`` or ``"`` respectively. :param force_preferred_quotation: Decide whether the preferred quotes are compulsory to adhere or not. """ str_contents = file[ sourcerange.start.line - 1][sourcerange.start.column:sourcerange.end.column - 1] if (preferred_quotation in str_contents and not force_preferred_quotation): return # Escape preferred quotes if present. str_contents = str_contents.replace(preferred_quotation, '\\' + preferred_quotation) before = file[sourcerange.start.line - 1][:sourcerange.start.column - 1] after = file[sourcerange.end.line - 1][sourcerange.end.column:] replacement = (before + preferred_quotation + str_contents + preferred_quotation + after) diff = Diff(file) diff.change_line(sourcerange.start.line, file[sourcerange.start.line - 1], replacement) yield Result(self, ('You do not use the preferred quotation marks.' ' Preferred mark: {}'.format(preferred_quotation)), diff.affected_code(filename), diffs={filename: diff})
def test_apply_renaming_only(self): with retrieve_stdout() as stdout: test_result = Result('origin', 'message', diffs={'a': Diff([], rename='b')}) file_dict = {'a': []} self.assertEqual( self.uut.apply_from_section(test_result, file_dict, {}, self.section), {}) self.assertEqual( stdout.getvalue(), '[----] ' + join('a', 'a') + '\n' '[++++] ' + join('b', 'b') + '\n')
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): 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)