<pre style='margin:0'>
Renee Otten (reneeotten) pushed a commit to branch master
in repository upt-macports.

</pre>
<p><a href="https://github.com/macports/upt-macports/commit/19d7b79391e09cc91d7b374eaa363a65cda926da">https://github.com/macports/upt-macports/commit/19d7b79391e09cc91d7b374eaa363a65cda926da</a></p>
<pre style="white-space: pre; background: #F8F8F8">The following commit(s) were added to refs/heads/master by this push:
<span style='display:block; white-space:pre;color:#404040;'>     new 19d7b79  Implement the "update" feature.
</span>19d7b79 is described below

<span style='display:block; white-space:pre;color:#808000;'>commit 19d7b79391e09cc91d7b374eaa363a65cda926da
</span>Author: Cyril Roelandt <tipecaml@gmail.com>
AuthorDate: Sun Jan 26 22:28:40 2020 +0100

<span style='display:block; white-space:pre;color:#404040;'>    Implement the "update" feature.
</span>---
 upt_macports/portfile_updater.py            | 275 ++++++++++++++++++
 upt_macports/tests/test_macports_backend.py |  15 +
 upt_macports/tests/test_portfile_updater.py | 433 ++++++++++++++++++++++++++++
 upt_macports/upt_macports.py                |  33 +++
 4 files changed, 756 insertions(+)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/upt_macports/portfile_updater.py b/upt_macports/portfile_updater.py
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..ac99438
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/upt_macports/portfile_updater.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,275 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright 2021      Cyril Roelandt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Licensed under the 3-clause BSD license. See the LICENSE file.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import logging
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import re
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import upt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class PortfileUpdater:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def __init__(self, portfile_fp, pdiff, pkg_class):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.portfile_fp = portfile_fp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.pdiff = pdiff
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.logger = logging.getLogger('upt')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.macports_pkg = pkg_class()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def update(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_portfile_content = self._update_portfile_content()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.portfile_fp.seek(0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.portfile_fp.write(new_portfile_content)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.portfile_fp.truncate()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_portfile_content(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        content = self.portfile_fp.read()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        content = self._update_version(content,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                       self.pdiff.old_version,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                       self.pdiff.new_version)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        content = self._update_revision(content)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            archive_format = self.macports_pkg.archive_format
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            new_archive = self.pdiff.new.get_archive(archive_format)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            content = self._update_checksums(content, new_archive)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        except upt.ArchiveUnavailable:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            self.logger.info('We could not get archives for this package. '
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                             'The checksums/size will be wrong.')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        content = self._update_dependencies(content, self.pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                            self.macports_pkg.jinja2_reqformat)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return content
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_checksums(content, new_archive):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Update the checksums block.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Return CONTENT after replacing the checksums (and size) with updated
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        checksums (and size) for NEW_ARCHIVE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        In the process, completely removes md5 checksum, which are no longer
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        required in MacPorts.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        m = re.search(r"[^\n]*checksums.*?[^\\]\n", content, re.DOTALL)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if not m:  # This Portfile had no checksums, let's not change anything
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return content
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_archive_block = m.group(0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        m = re.match(r'(\s*)checksums(\s+)[^\s]+',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                     old_archive_block.split('\n')[0])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        space_before = m.group(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        space_after = m.group(2)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        indent = ' ' * len(space_before + 'checksums' + space_after)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_archive_block = f'{space_before}checksums{space_after}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_archive_block += f'rmd160  {new_archive.rmd160} \\\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_archive_block += f'{indent}sha256  {new_archive.sha256} \\\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_archive_block += f'{indent}size    {new_archive.size}\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return content.replace(old_archive_block, new_archive_block)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_version(content, old_version, new_version):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Update the version of the package being updated.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Return CONTENT after replacing the OLD_VERSION with the NEW_VERSION.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        This works for the "version" keyword, and a variety of "*.setup"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        keywords.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        keywords = '|'.join([
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'version', 'github.setup', 'bitbucket.setup',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'ruby.setup', 'perl5.setup',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return re.sub(fr'({keywords})(.*){old_version}',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                      fr'\g<1>\g<2>{new_version}',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                      content, count=1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_revision(content):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Update the first revision entry in the Portfile.'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return re.sub(r'revision(\s+)\d+', r'revision\g<1>0', content,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                      count=1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_dependencies(self, content, pdiff, reqformat_fn):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for phase in ['build', 'lib', 'test']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            content = self._update_dependency_phase(content, pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                    reqformat_fn, phase)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return content
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _get_current_dependencies(content, phase):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Return a list of the current dependencies for a given phase.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Extract all dependencies for PHASE from CONTENT. PHASE must be one of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        'build', 'lib', 'test'. The dependencies are returned just like they
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        are written in the Portfile, for instance:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ['port:py${python.version}-six', 'port:py${python.version}-xlrd']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mmm = re.search(fr"[^\n]*depends_{phase}-append.*?[^\\]\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        content, re.DOTALL)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deps = []
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if mmm:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            old_depends_block = mmm.group(0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            for line in old_depends_block.split('\n'):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                m = re.match(fr'(\s*)depends_{phase}-append(\s+)(.*)', line)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if m:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    line = m.group(3)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if line.endswith('\\'):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    line = line[:-1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                line = line.strip()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                deps.extend(line.split())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            old_depends_block = ''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return old_depends_block, deps
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _remove_deleted_dependencies(current_deps, deleted_dependencies):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Remove DELETED_DEPENDENCIES from CURRENT_DEPS.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        CURRENT_DEPS and DELETED_DEPENDENCIES must be lists of dependencies as
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        specified in a Portfile.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Example:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            current_deps = ['port:py${python.version}-six',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            'port:py${python.version}-xlrd']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            deleted_dependencies = ['port:py${python.version}-xlrd']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            This method will return ['port:py${python.version}-six']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # We could have used operations on sets here, but since sets are
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # unordered, the dependencies would have been "shuffled", which would
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # have caused the diff between the old and the new Portfiles to be
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # bigger and harder to read that they needed to be.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for deleted_dependency in deleted_dependencies:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                current_deps.remove(deleted_dependency)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            except ValueError:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # This particular requirement is no longer marked as needed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # upstream. Maybe it was never included in the Makefile, which
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # means that trying to remove it may raise this exception.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                pass
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return current_deps
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _add_new_dependencies(current_deps, new_dependencies):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Add NEW_DEPENDENCIES to CURRENT_DEPS.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        CURRENT_DEPS and NEW_DEPENDENCIES must be lists of dependencies as
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        specified in a Portfile.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Example:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            current_deps = ['port:py${python.version}-six']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            new_dependencies = ['port:py${python.version}-xlrd']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            This method will return  = ['port:py${python.version}-six',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                        'port:py${python.version}-xlrd']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # We could have used operations on sets here, but since sets are
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # unordered, the dependencies would have been "shuffled", which would
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # have caused the diff between the old and the new Portfiles to be
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # bigger and harder to read that they needed to be.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Some of the new requirements may already be in the Portfile. This
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # happens when upstream failed to properly specify metadata in the old
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # version and fixed everything in the new one:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Old upstream metadata: "required: []" (even though 'foo' is needed)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # New upstream metadata: "required: ['foo']"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # In this case, upt will consider that 'foo' is a new requirement.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Since it was already required in the old version (even though that
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # was not specified in the metadata), the dependency on 'foo' will
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # already be specified in the Portfile. We need to make sure that we do
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # not duplicate this dependency, hence the if condition in the loop.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for new_dependency in new_dependencies:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if new_dependency not in current_deps:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                current_deps.append(new_dependency)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return current_deps
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _update_dependency_phase(self, content, pdiff, reqformat_fn, phase):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        phases = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'build': 'build',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'lib': 'run',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'test': 'test',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Let's extract the dependencies currently specified in the Portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # for this phase.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_depends_block, deps = self._get_current_dependencies(content,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                                 phase)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Start by removing the deleted dependencies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deleted_dependencies = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            f'port:{reqformat_fn(deleted_dependency)}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            for deleted_dependency in pdiff.deleted_requirements(phases[phase])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deps = self._remove_deleted_dependencies(deps, deleted_dependencies)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Next, add the new dependencies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_dependencies = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            f'port:{reqformat_fn(new_dependency)}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            for new_dependency in pdiff.new_requirements(phases[phase])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deps = self._add_new_dependencies(deps, new_dependencies)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Finally, format the new depends block properly.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_depends_block = self._format_like(deps, old_depends_block, phase)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if old_depends_block:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            content = content.replace(old_depends_block, new_depends_block)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # This phase had no dependencies, let's add it at the bottom of the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # Portfile and let the maintainer move it wherever they want.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if new_depends_block:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                content += '# TODO: Move this\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                content += new_depends_block
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return content
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @staticmethod
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def _format_like(deps, old_depends_block, phase):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Format a block of dependencies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Return a string representing a dependency block for PHASE, containing
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        dependencies specified in DEPS, so that it uses the same
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        indentation/spacing as OLD_DEPENDS_BLOCK.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if not deps:  # No dependencies -> No block in the Portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return ''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Read
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_depends_block_lines = old_depends_block.split('\n')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block_name = f'depends_{phase}-append'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        m = re.match(fr'(\s*)depends_{phase}-append(\s+)(.*)',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                     old_depends_block_lines[0])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if m:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            first_line_indent = m.group(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            space = m.group(2)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if m.group(3) == '\\':
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # When the first line does not contain a dependency, like this:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                #   depends_lib-append \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # we use this "hack": this allows us to not handle a "special"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # case" when crafting the new depends block.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                deps.insert(0, '')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # This phase was not included in the original Portfile, we will
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # build it "from scratch".
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            first_line_indent = ''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            space = ' '
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        next_lines_indent = ''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if len(old_depends_block_lines) > 1:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            m = re.match(r'(\s+)', old_depends_block_lines[1])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if m:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                next_lines_indent = m.group(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if next_lines_indent == '':
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            next_lines_indent = first_line_indent
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            next_lines_indent += ' ' * len(block_name)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            next_lines_indent += space
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Craft the new block
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_depends = f'{first_line_indent}{block_name}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if not deps[0]:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            new_depends += ' ' * (len(space) - 1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            new_depends += space
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_depends += ' \\\n'.join([
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            dep if i == 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            else f'{next_lines_indent}{dep}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            for i, dep in enumerate(deps)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_depends += '\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return new_depends
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/upt_macports/tests/test_macports_backend.py b/upt_macports/tests/test_macports_backend.py
</span><span style='display:block; white-space:pre;color:#808080;'>index da7ac14..527fdff 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/upt_macports/tests/test_macports_backend.py
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/upt_macports/tests/test_macports_backend.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -7,6 +7,21 @@ from upt_macports.upt_macports import MacPortsBackend
</span> class TestMacPortsBackend(unittest.TestCase):
     def setUp(self):
         self.macports_backend = MacPortsBackend()
<span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.macports_backend.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @mock.patch('upt_macports.upt_macports.MacPortsBackend.package_versions',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return_value=['1.2'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_current_version(self, m_package_versions):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        version = self.macports_backend.current_version(mock.Mock(), 'foo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(version, '1.2')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @mock.patch('upt_macports.upt_macports.MacPortsBackend.package_versions',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return_value=[])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    @mock.patch('upt.Backend.current_version', return_value='1.2')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_current_version_fallback(self, m_current_version,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                      m_package_versions):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        version = self.macports_backend.current_version(mock.Mock(), 'foo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(version, '1.2')
</span> 
     def test_unhandled_frontend(self):
         upt_pkg = upt.Package('foo', '42')
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/upt_macports/tests/test_portfile_updater.py b/upt_macports/tests/test_portfile_updater.py
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..c730c18
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/upt_macports/tests/test_portfile_updater.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,433 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright 2021      Cyril Roelandt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Licensed under the 3-clause BSD license. See the LICENSE file.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import io
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import unittest
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from unittest import mock
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import upt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from upt_macports.portfile_updater import PortfileUpdater
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from upt_macports.upt_macports import MacPortsPythonPackage
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class TestPortfileUpdater(unittest.TestCase):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def setUp(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        handle = io.StringIO('')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.updater = PortfileUpdater(handle, mock.Mock(),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                       MacPortsPythonPackage)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_version(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        test_cases = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'version 1.2.3': 'version 4.5.6',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            '   version    1.2.3': '   version    4.5.6',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'github.setup auth pkg 1.2.3': 'github.setup auth pkg 4.5.6',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'bitbucket.setup auth pkg 1.2.3': 'bitbucket.setup auth pkg 4.5.6',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'perl5.setup foo 1.2.3': 'perl5.setup foo 4.5.6',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'ruby.setup foo 1.2.3 gem': 'ruby.setup foo 4.5.6 gem',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'platforms darwin': 'platforms darwin',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for before, after in test_cases.items():
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            out = self.updater._update_version(before, '1.2.3', '4.5.6')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            self.assertEqual(out, after)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_revision(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        test_cases = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'revision      1\n': 'revision      0\n',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'revision      0\n': 'revision      0\n',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'revision 1\nrevision 2\n': 'revision 0\nrevision 2\n',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'foo\n': 'foo\n',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for before, after in test_cases.items():
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            self.assertEqual(self.updater._update_revision(before), after)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_checksums(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # No checksum in the Portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        portfile = 'version 1.2\nrevision 0\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._update_checksums(portfile, mock.Mock())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, portfile)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Build upt.Archive objects
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldrmd = '91bf89c0493ad2caa8ed29372972e2e887f84bb8'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldsha = '2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a'  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newrmd = '770c41f726e57b64e2c27266e6b0cf8b7bf895ab'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newsha = '2f52bbb095baa858b3273d851de5cc25a4470351bdfe675b2d5b997e3145c2c4'  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldsize = 42
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newsize = 1337
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new = upt.Archive('url', size=newsize, rmd160=newrmd, sha256=newsha)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # A single hash in the Portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        portfile = f'checksums     rmd160  {oldrmd} \n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = f'checksums     rmd160  {newrmd} \\\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected += f'              sha256  {newsha} \\\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected += f'              size    {newsize}\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._update_checksums(portfile, new)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # A more common case
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        portfile = f'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    checksums   md5 abcdef \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                rmd160 {oldrmd} \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                sha256 {oldsha} \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                size   {oldsize}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = f'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    checksums   rmd160  {newrmd} \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                sha256  {newsha} \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                size    {newsize}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._update_checksums(portfile, new)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_get_current_dependencies(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        portfile = '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_lib-append libdep1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_test-append testdep1 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        testdep2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # lib phase
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block, deps = self.updater._get_current_dependencies(portfile, 'lib')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(block, 'depends_lib-append libdep1\n')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertListEqual(deps, ['libdep1'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # test phase
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block, deps = self.updater._get_current_dependencies(portfile, 'test')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = '''    depends_test-append testdep1 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        testdep2\n'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(block, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertListEqual(deps, ['testdep1', 'testdep2'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # build phase
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block, deps = self.updater._get_current_dependencies(portfile, 'build')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(block, '')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(deps, [])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_get_current_dependencies_corner_cases(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        portfile = '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_build-append port:foo port:bar
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_lib-append \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    port:baz
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # build phase
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block, deps = self.updater._get_current_dependencies(portfile, 'build')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(block, 'depends_build-append port:foo port:bar\n')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(deps, ['port:foo', 'port:bar'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # lib phase
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        block, deps = self.updater._get_current_dependencies(portfile, 'lib')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(block, 'depends_lib-append \\\n    port:baz\n')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertListEqual(deps, ['port:baz'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_remove_deleted_dependencies(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        current = ['foo', 'bar']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deleted = ['bar', 'baz']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = ['foo']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._remove_deleted_dependencies(current, deleted)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertListEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_add_new_dependencies(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        current = ['foo', 'bar']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        added = ['bar', 'baz']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = ['foo', 'bar', 'baz']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._add_new_dependencies(current, added)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertListEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_format_like(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # No deps
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(self.updater._format_like([], mock.Mock(), 'lib'), '')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # The block did not exist in the old Portfile and must be added
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = 'depends_lib-append port:foo\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._format_like(['port:foo'], '', 'lib')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # No dependency on the first line
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old = 'depends_lib-append \\\nport:foo\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = '''depends_lib-append \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   port:bar\n'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._format_like(['port:bar'], old, 'lib')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # There are spaces before the block
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        deps = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'port:foo', 'port:bar', 'port:baz'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_depends_block = '''    depends_lib-append   port:foo  \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        port:bar
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = '''    depends_lib-append   port:foo \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        port:bar \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        port:baz
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._format_like(deps, old_depends_block, 'lib')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # The original depends block is a single line
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_depends_block = 'depends_test-append port:foo\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = '''depends_test-append port:foo \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:bar \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:baz
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._format_like(deps, old_depends_block, 'test')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_dependencies(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = '''version 1.2.3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_build-append  \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    port:build-foo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append port:lib-foo \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                       port:lib-bar
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+platforms darwin
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected = '''version 1.2.3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_build-append  \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    port:build-foo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append port:lib-foo \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                       port:lib-baz
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+platforms darwin
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# TODO: Move this
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_test-append port:test-foo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        def reqformat(req):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return f'port:{req}'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg = upt.Package('somepkg', '1.2.3')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'build': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('build-foo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('lib-foo'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('lib-bar'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg = upt.Package('somepkg', '4.5.6')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'build': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('build-foo')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('lib-foo'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('lib-baz'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'test': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('test-foo'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        pdiff = upt.PackageDiff(oldpkg, newpkg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = self.updater._update_dependencies(old_portfile, pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                                lambda x: x)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_portfile_content_beautifulsoup(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version 4.6.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checksums           rmd160  6452de577ef676636fb0be79eba9224cafd5622d \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    size    160846
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append  port:py${python.version}-setuptools
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version 4.9.1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+checksums           rmd160  b72ed53263f07c843ce34513a9d62128051e2fc3 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    sha256  73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    size    374759
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append  port:py${python.version}-setuptools \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        port:py${python.version}-soupsieve
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg = upt.Package('beautifulsoup4', '4.6.0')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.requirements = {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.archives = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            upt.Archive('url',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        rmd160='6452de577ef676636fb0be79eba9224cafd5622d',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        sha256='808b6ac932dccb0a4126558f7dfdcf41710dd44a4ef497a0bb59a77f9f078e89',  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        size=160846)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg = upt.Package('beautifulsoup4', '4.9.1')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('soupsieve'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.archives = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            upt.Archive('url',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        rmd160='b72ed53263f07c843ce34513a9d62128051e2fc3',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        sha256='73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7',  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        size=374759)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        pdiff = upt.PackageDiff(oldpkg, newpkg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater = PortfileUpdater(io.StringIO(old_portfile),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  MacPortsPythonPackage)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = updater._update_portfile_content()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected_portfile)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_portfile_content_sunpy(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Upgrading sunpy from 0.3.1 to 1.1.3 has us:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # 1) Reset the revision (from 1 to 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # 2) Parse a big block of runtime dependencies
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version     0.3.1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+revision    1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_build-append  port:py${python.version}-numpy
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append    port:py${python.version}-scipy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-matplotlib \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-astropy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-pyqt4 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-suds \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-pandas \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-beautifulsoup4 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-configobj \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-setuptools \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-py
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version     1.1.3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+revision    0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_build-append  port:py${python.version}-numpy
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append    port:py${python.version}-scipy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-matplotlib \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-astropy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-pyqt4 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-suds \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-pandas \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-beautifulsoup4 \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-configobj \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-setuptools \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-py \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-numpy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          port:py${python.version}-parfive
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# TODO: Move this
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_test-append port:py${python.version}-hypothesis \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-doctestplus \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-astropy \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-cov \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-mock \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-tox \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-tox-conda
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg = upt.Package('sunpy', '0.3.1')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg = upt.Package('beautifulsoup4', '1.1.3')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('numpy'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('parfive'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'test': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('hypothesis'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-doctestplus'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-astropy'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-cov'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-mock'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('tox'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('tox-conda'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        pdiff = upt.PackageDiff(oldpkg, newpkg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater = PortfileUpdater(io.StringIO(old_portfile),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  MacPortsPythonPackage)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = updater._update_portfile_content()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected_portfile)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_portfile_content_gwosc(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # What is interesting about updating gwosc from 0.3.3 to 0.5.3 is:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # 1) depends_lib-append must be removed completely
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # 2) depends_test-append must be added
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version 0.3.3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_build-append port:py${python.version}-setuptools
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append   port:py${python.version}-six
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    livecheck.type      none
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        expected_portfile = '''\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+version 0.5.3
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if {${name} ne ${subport}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_build-append port:py${python.version}-setuptools
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    livecheck.type      none
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# TODO: Move this
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+depends_test-append port:py${python.version}-pytest \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-cov \\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:py${python.version}-pytest-socket
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+'''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg = upt.Package('gwosc', '0.3.3')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        oldpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('six'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg = upt.Package('gwosc', '0.5.3')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.frontend = 'pypi'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        newpkg.requirements = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'run': [],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            'test': [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-cov'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                upt.PackageRequirement('pytest-socket'),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        pdiff = upt.PackageDiff(oldpkg, newpkg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater = PortfileUpdater(io.StringIO(old_portfile),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  pdiff,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                  MacPortsPythonPackage)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        out = updater._update_portfile_content()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(out, expected_portfile)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = 'line1\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_portfile = 'line2\nline3\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater = PortfileUpdater(io.StringIO(old_portfile), None, mock.Mock())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater._update_portfile_content = lambda: new_portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater.update()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater.portfile_fp.seek(0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(updater.portfile_fp.read(), new_portfile)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def test_update_shorter_portfile(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        old_portfile = 'line1\nline2\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        new_portfile = 'line3\n'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater = PortfileUpdater(io.StringIO(old_portfile), None, mock.Mock())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater._update_portfile_content = lambda: new_portfile
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater.update()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        updater.portfile_fp.seek(0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.assertEqual(updater.portfile_fp.read(), new_portfile)
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/upt_macports/upt_macports.py b/upt_macports/upt_macports.py
</span><span style='display:block; white-space:pre;color:#808080;'>index 8b62caf..bb09465 100755
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/upt_macports/upt_macports.py
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/upt_macports/upt_macports.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -9,6 +9,8 @@ import subprocess
</span> import sys
 from packaging.specifiers import SpecifierSet
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+from upt_macports.portfile_updater import PortfileUpdater
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> 
 class MacPortsPackage(object):
     def __init__(self):
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -328,3 +330,34 @@ class MacPortsBackend(upt.Backend):
</span>                  self.standardize_CPAN_version(dep.version) for dep in s])
 
         return super().needs_requirement(req, phase)
<span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def current_version(self, frontend, pkgname, output=None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''Return the current version of PKGNAME, packaged using FRONTEND.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        PKGNAME is the name of the package in the upstream package archive, not
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+the port name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        '''
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        self.frontend = frontend.name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return self.package_versions(pkgname)[0]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        except:  # noqa
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            self.logger.error(f'Could not get current version for {pkgname}')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return super().current_version(frontend, pkgname, output=output)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    def update_package(self, pdiff, output=None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        pkg_class = self.pkg_classes[self.frontend]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        macports_pkg = pkg_class()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # TODO: This is basically the same code as the one found in
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # MacPortsPackage._create_output_directories(). It would be nice not to
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # repeat ourselves.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if output is None:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            pkgname = pdiff.new.name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            folder_name = macports_pkg._normalized_macports_folder(pkgname)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            output_dir = os.path.join(macports_pkg.category, folder_name)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            portfile_path = f'{output_dir}/Portfile'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            portfile_path = output
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        with open(portfile_path, 'r+') as f:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            PortfileUpdater(f, pdiff, pkg_class).update()
</span></pre><pre style='margin:0'>

</pre>