Repository: conda/conda

Issue:
conda install --rev is broken in 4.3
per https://github.com/ContinuumIO/anaconda-issues/issues/7855#issuecomment-357036226
Write downgrade integration tests
In an integration test:

1. Create an environment with the current conda under test, but include an earlier version of conda in that environment.  Probably something like

        conda create -n _test-env python=2.7 itsdangers conda=X.X

2. Subprocess to that conda, and make sure that conda can operate on that environment.

                CONDA_ROOT/envs/_test-env/bin/conda install -p CONDA_ROOT/envs/_test-env/bin/conda flask


Discussion:

Resolving patch:
diff --git a/conda/cli/install.py b/conda/cli/install.py
--- a/conda/cli/install.py
+++ b/conda/cli/install.py
@@ -287,7 +287,7 @@ def install(args, parser, command='install'):
             spec_regex = r'^(%s)$' % '|'.join(re.escape(s.split()[0]) for s in ospecs)
             print('\n# All requested packages already installed.')
             for action in action_set:
-                print_packages(action["PREFIX"], spec_regex)
+                print_packages(action.get("PREFIX", prefix), spec_regex)
         else:
             common.stdout_json_success(
                 message='All requested packages already installed.')
diff --git a/conda/plan.py b/conda/plan.py
--- a/conda/plan.py
+++ b/conda/plan.py
@@ -806,7 +806,7 @@ def revert_actions(prefix, revision=-1, index=None):
     if state == curr:
         return {}
 
-    dists = (Dist(s) for s in state)
+    dists = tuple(Dist(s) for s in state)
     actions = ensure_linked_actions(dists, prefix)
     for dist in curr - state:
         add_unlink(actions, Dist(dist))
@@ -819,7 +819,6 @@ def revert_actions(prefix, revision=-1, index=None):
         if dist not in index:
             msg = "Cannot revert to {}, since {} is not in repodata".format(revision, dist)
             raise CondaRevisionError(msg)
-
     return actions
 
 


Test patch:
diff --git a/tests/test_create.py b/tests/test_create.py
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -28,7 +28,7 @@
 import pytest
 import requests
 
-from conda import CondaError, CondaMultiError, plan
+from conda import CondaError, CondaMultiError, plan, __version__ as CONDA_VERSION
 from conda._vendor.auxlib.entity import EntityEncoder
 from conda.base.context import context, reset_context
 from conda.cli.common import get_index_trap
@@ -58,6 +58,7 @@
 from conda.gateways.disk.delete import rm_rf
 from conda.gateways.disk.update import touch
 from conda.gateways.logging import TRACE
+from conda.gateways.subprocess import subprocess_call
 from conda.models.index_record import IndexRecord
 from conda.utils import on_win
 
@@ -157,7 +158,8 @@ def run_command(command, prefix, *arguments, **kwargs):
 
 @contextmanager
 def make_temp_env(*packages, **kwargs):
-    prefix = kwargs.pop('prefix', None) or make_temp_prefix()
+    name = kwargs.pop('name', None)
+    prefix = kwargs.pop('prefix', None) or make_temp_prefix(name)
     assert isdir(prefix), prefix
     with disable_logger('fetch'), disable_logger('dotupdate'):
         try:
@@ -1202,6 +1204,53 @@ def test_transactional_rollback_upgrade_downgrade(self):
                     run_command(Commands.INSTALL, prefix, 'flask=0.11.1')
                 assert_package_is_installed(prefix, 'flask-0.10.1')
 
+    def test_conda_downgrade(self):
+        # Create an environment with the current conda under test, but include an earlier
+        # version of conda and other packages in that environment.
+        # Make sure we can flip back and forth.
+        conda_exe = join('Scripts', 'conda.exe') if on_win else join('bin', 'conda')
+        with env_var("CONDA_AUTO_UPDATE_CONDA", "false", reset_context):
+            with make_temp_env("conda=4.3.27 python=%s" % sys.version_info[0],
+                               name='_' + str(uuid4())[:8]) as prefix:  # rev 0
+                assert package_is_installed(prefix, "conda")
+
+                run_command(Commands.INSTALL, prefix, "mccabe")  # rev 1
+                assert package_is_installed(prefix, "mccabe")
+
+                subprocess_call("%s install -p %s -y itsdangerous" % (join(prefix, conda_exe), prefix))  # rev 2
+                linked_data_.clear()
+                assert package_is_installed(prefix, "itsdangerous")
+
+                run_command(Commands.INSTALL, prefix, "lockfile")  # rev 3
+                assert package_is_installed(prefix, "lockfile")
+
+                subprocess_call("%s install -p %s -y conda=4.3" % (join(prefix, conda_exe), prefix))  # rev 4
+                linked_data_.clear()
+                assert not package_is_installed(prefix, "conda-4.3.27")
+
+                subprocess_call("%s install -p %s -y colorama" % (join(prefix, conda_exe), prefix))  # rev 5
+                linked_data_.clear()
+                assert package_is_installed(prefix, "colorama")
+
+                stdout, stderr = run_command(Commands.LIST, prefix, "--revisions")
+                print(stdout)
+
+                run_command(Commands.INSTALL, prefix, "--rev 3")
+                linked_data_.clear()
+                assert package_is_installed(prefix, "conda-4.3.27")
+                assert not package_is_installed(prefix, "colorama")
+
+                subprocess_call("%s install -y -p %s --rev 1" % (join(prefix, conda_exe), prefix))
+                linked_data_.clear()
+                assert not package_is_installed(prefix, "itsdangerous")
+                linked_data_.clear()
+                assert package_is_installed(prefix, "conda-4.3.27")
+                assert package_is_installed(prefix, "python-%s" % sys.version_info[0])
+
+                result = subprocess_call("%s info --json" % join(prefix, conda_exe))
+                conda_info = json.loads(result.stdout)
+                assert conda_info["conda_version"] == "4.3.27"
+
     @pytest.mark.skipif(on_win, reason="openssl only has a postlink script on unix")
     def test_run_script_called(self):
         import conda.core.link