def test_file_match_no_dups(self): tree = self.make_branch_and_tree('tree') rn = RenameMap(tree) rn.add_edge_hashes(self.a_lines, 'aid') self.build_tree_contents([('tree/a', b''.join(self.a_lines))]) self.build_tree_contents([('tree/b', b''.join(self.b_lines))]) self.build_tree_contents([('tree/c', b''.join(self.b_lines))]) self.assertEqual({'a': 'aid'}, rn.file_match(['a', 'b', 'c']))
def test_add_file_edge_hashes(self): tree = self.make_branch_and_tree('tree') self.build_tree_contents([('tree/a', b''.join(self.a_lines))]) tree.add('a', b'a') rn = RenameMap(tree) rn.add_file_edge_hashes(tree, [b'a']) self.assertEqual({b'a'}, rn.edge_hashes[myhash(('a\n', 'b\n'))]) self.assertEqual({b'a'}, rn.edge_hashes[myhash(('b\n', 'c\n'))]) self.assertIs(None, rn.edge_hashes.get(myhash(('c\n', 'd\n'))))
def test_guess_renames_dry_run(self): tree = self.make_branch_and_tree('tree') tree.lock_write() self.addCleanup(tree.unlock) self.build_tree(['tree/file']) tree.add('file', b'file-id') tree.commit('Added file') os.rename('tree/file', 'tree/file2') RenameMap.guess_renames(tree.basis_tree(), tree, dry_run=True) self.assertEqual('file', tree.id2path(b'file-id'))
def test_guess_renames_handles_directories(self): tree = self.make_branch_and_tree('tree') tree.lock_write() self.addCleanup(tree.unlock) self.build_tree(['tree/dir/', 'tree/dir/file']) tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id']) tree.commit('Added file') os.rename('tree/dir', 'tree/dir2') RenameMap.guess_renames(tree.basis_tree(), tree) self.assertEqual('dir2/file', tree.id2path(b'file-id')) self.assertEqual('dir2', tree.id2path(b'dir-id'))
def test_match_directories(self): tree = self.make_branch_and_tree('tree') rn = RenameMap(tree) required_parents = rn.get_required_parents({ 'path1': 'a', 'path2/tr': 'b', 'path3/path4/path5': 'c', }) self.assertEqual({ 'path2': {'b'}, 'path3/path4': {'c'}, 'path3': set() }, required_parents)
def test_guess_renames_preserves_children(self): """When a directory has been moved, its children are preserved.""" tree = self.make_branch_and_tree('tree') tree.lock_write() self.addCleanup(tree.unlock) self.build_tree_contents([('tree/foo/', b''), ('tree/foo/bar', b'bar'), ('tree/foo/empty', b'')]) tree.add(['foo', 'foo/bar', 'foo/empty'], [b'foo-id', b'bar-id', b'empty-id']) tree.commit('rev1') os.rename('tree/foo', 'tree/baz') RenameMap.guess_renames(tree.basis_tree(), tree) self.assertEqual('baz/empty', tree.id2path(b'empty-id'))
def test_hitcounts(self): rn = RenameMap(None) rn.add_edge_hashes(self.a_lines, 'a') rn.add_edge_hashes(self.b_lines, 'b') self.assertEqual({'a': 2.5, 'b': 0.5}, rn.hitcounts(self.a_lines)) self.assertEqual({'a': 1}, rn.hitcounts(self.a_lines[:-1])) self.assertEqual({'b': 2.5, 'a': 0.5}, rn.hitcounts(self.b_lines))
def test_find_directory_renames(self): tree = self.make_branch_and_tree('tree') rn = RenameMap(tree) matches = { 'path1': 'a', 'path3/path4/path5': 'c', } required_parents = { 'path2': {'b'}, 'path3/path4': {'c'}, 'path3': set([]) } missing_parents = { 'path2-id': {'b'}, 'path4-id': {'c'}, 'path3-id': {'path4-id'} } matches = rn.match_parents(required_parents, missing_parents) self.assertEqual({ 'path3/path4': 'path4-id', 'path2': 'path2-id' }, matches)
def test_add_edge_hashes(self): rn = RenameMap(None) rn.add_edge_hashes(self.a_lines, 'a') self.assertEqual({'a'}, rn.edge_hashes[myhash(('a\n', 'b\n'))]) self.assertEqual({'a'}, rn.edge_hashes[myhash(('b\n', 'c\n'))]) self.assertIs(None, rn.edge_hashes.get(myhash(('c\n', 'd\n'))))
def run_lintian_fixer( # noqa: C901 local_tree: WorkingTree, fixer: Fixer, committer: Optional[str] = None, update_changelog: Union[bool, Callable[[], bool]] = True, compat_release: Optional[str] = None, minimum_certainty: Optional[str] = None, trust_package: bool = False, allow_reformatting: bool = False, dirty_tracker=None, subpath: str = "", net_access: bool = True, opinionated: Optional[bool] = None, diligence: int = 0, timestamp: Optional[datetime] = None, basis_tree: Optional[Tree] = None, changes_by: str = "lintian-brush", ): """Run a lintian fixer on a tree. Args: local_tree: WorkingTree object basis_tree: Tree fixer: Fixer object to apply committer: Optional committer (name and email) update_changelog: Whether to add a new entry to the changelog compat_release: Minimum release that the package should be usable on (e.g. 'stable' or 'unstable') minimum_certainty: How certain the fixer should be about its changes. trust_package: Whether to run code from the package if necessary allow_reformatting: Whether to allow reformatting of changed files dirty_tracker: Optional object that can be used to tell if the tree has been changed. subpath: Path in tree to operate on net_access: Whether to allow accessing external services opinionated: Whether to be opinionated diligence: Level of diligence Returns: tuple with set of FixerResult, summary of the changes """ if basis_tree is None: basis_tree = local_tree.basis_tree() changelog_path = os.path.join(subpath, "debian/changelog") try: with local_tree.get_file(changelog_path) as f: cl = Changelog(f, max_blocks=1) except NoSuchFile: raise NotDebianPackage(local_tree, subpath) package = cl.package if cl.distributions == "UNRELEASED": current_version = cl.version else: current_version = cl.version increment_version(current_version) if compat_release is None: compat_release = "sid" if minimum_certainty is None: minimum_certainty = DEFAULT_MINIMUM_CERTAINTY logger.debug('Running fixer %r', fixer) try: result = fixer.run( local_tree.abspath(subpath), package=package, current_version=current_version, compat_release=compat_release, minimum_certainty=minimum_certainty, trust_package=trust_package, allow_reformatting=allow_reformatting, net_access=net_access, opinionated=opinionated, diligence=diligence, ) except BaseException: reset_tree(local_tree, basis_tree, subpath, dirty_tracker=dirty_tracker) raise if not certainty_sufficient(result.certainty, minimum_certainty): reset_tree(local_tree, basis_tree, subpath, dirty_tracker=dirty_tracker) raise NotCertainEnough( fixer, result.certainty, minimum_certainty, overridden_lintian_issues=result.overridden_lintian_issues) specific_files: Optional[List[str]] if dirty_tracker: relpaths = dirty_tracker.relpaths() # Sort paths so that directories get added before the files they # contain (on VCSes where it matters) local_tree.add( [ p for p in sorted(relpaths) if local_tree.has_filename(p) and not local_tree.is_ignored(p) ] ) specific_files = [p for p in relpaths if local_tree.is_versioned(p)] if not specific_files: raise NoChanges( fixer, "Script didn't make any changes", result.overridden_lintian_issues) else: local_tree.smart_add([local_tree.abspath(subpath)]) specific_files = [subpath] if subpath else None if local_tree.supports_setting_file_ids(): RenameMap.guess_renames(basis_tree, local_tree, dry_run=False) changes = list( local_tree.iter_changes( basis_tree, specific_files=specific_files, want_unversioned=False, require_versioned=True, ) ) if len(local_tree.get_parent_ids()) <= 1 and not changes: raise NoChanges( fixer, "Script didn't make any changes", result.overridden_lintian_issues) if not result.description: raise DescriptionMissing(fixer) lines = result.description.splitlines() summary = lines[0] details = list(itertools.takewhile(lambda line: line, lines[1:])) # If there are upstream changes in a non-native package, perhaps # export them to debian/patches if has_non_debian_changes(changes, subpath) and current_version.debian_revision: try: patch_name, specific_files = _upstream_changes_to_patch( local_tree, basis_tree, dirty_tracker, subpath, result.patch_name or fixer.name, result.description, timestamp=timestamp, ) except BaseException: reset_tree(local_tree, basis_tree, subpath, dirty_tracker=dirty_tracker) raise summary = "Add patch %s: %s" % (patch_name, summary) if only_changes_last_changelog_block( local_tree, basis_tree, changelog_path, changes ): # If the script only changed the last entry in the changelog, # don't update the changelog update_changelog = False if callable(update_changelog): update_changelog = update_changelog() if update_changelog: from .changelog import add_changelog_entry add_changelog_entry(local_tree, changelog_path, [summary] + details) if specific_files: specific_files.append(changelog_path) description = result.description + "\n" description += "\n" description += "Changes-By: %s\n" % changes_by for tag in result.fixed_lintian_tags: description += "Fixes: lintian: %s\n" % tag description += "See-also: https://lintian.debian.org/tags/%s.html\n" % tag if committer is None: committer = get_committer(local_tree) revid = local_tree.commit( description, allow_pointless=False, reporter=NullCommitReporter(), committer=committer, specific_files=specific_files, ) result.revision_id = revid # TODO(jelmer): Support running sbuild & verify lintian warning is gone? return result, summary