# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
import copy
import os
import re
import sys
from importlib.metadata import version
from pprint import pprint
from unittest.mock import Mock, patch

import pytest

from conda.auxlib.ish import dals
from conda.base.context import conda_tests_ctxt_mgmt_def_pol, context
from conda.common.compat import on_linux, on_mac, on_win
from conda.common.io import env_var, env_vars
from conda.core.solve import DepsModifier, UpdateModifier
from conda.exceptions import SpecsConfigurationConflictError, UnsatisfiableError
from conda.models.channel import Channel
from conda.models.enums import PackageType
from conda.models.records import PrefixRecord
from conda.models.version import VersionOrder
from conda.resolve import MatchSpec
from conda.testing.helpers import (
    CHANNEL_DIR_V1,
    add_subdir,
    add_subdir_to_iter,
    convert_to_dist_str,
    get_solver,
    get_solver_2,
    get_solver_4,
    get_solver_aggregate_1,
    get_solver_aggregate_2,
    get_solver_cuda,
    get_solver_must_unfreeze,
)

pytestmark = pytest.mark.usefixtures("parametrized_solver_fixture")


@pytest.mark.benchmark
def test_solve_1(tmpdir):
    specs = (MatchSpec("numpy"),)

    with get_solver(tmpdir, specs) as solver:
        final_state = solver.solve_final_state()
        # print(convert_to_dist_str(final_state))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-3.3.2-0",
                "channel-1::numpy-1.7.1-py33_0",
            )
        )
        assert convert_to_dist_str(final_state) == order

    specs_to_add = (MatchSpec("python=2"),)
    with get_solver(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state,
        history_specs=specs,
    ) as solver:
        final_state = solver.solve_final_state()
        # print(convert_to_dist_str(final_state))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state) == order


def test_solve_2(tmpdir):
    if context.solver == "libmamba":
        pytest.skip(
            "conda-libmamba-solver does not use Solver.ssc (SolverStateContainer)"
        )

    specs = (MatchSpec("numpy"),)

    with get_solver_aggregate_1(tmpdir, specs) as solver:
        final_state = solver.solve_final_state()
        # print(convert_to_dist_str(final_state))
        order = add_subdir_to_iter(
            (
                "channel-2::mkl-2017.0.3-0",
                "channel-2::openssl-1.0.2l-0",
                "channel-2::readline-6.2-2",
                "channel-2::sqlite-3.13.0-0",
                "channel-2::tk-8.5.18-0",
                "channel-2::xz-5.2.3-0",
                "channel-2::zlib-1.2.11-0",
                "channel-2::python-3.6.2-0",
                "channel-2::numpy-1.13.1-py36_0",
            )
        )
        assert convert_to_dist_str(final_state) == order

    specs_to_add = (MatchSpec("channel-4::numpy"),)
    with get_solver_aggregate_1(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state,
        history_specs=specs,
    ) as solver:
        solver.solve_final_state()
        extra_prec = PrefixRecord(
            _hash=5842798532132402024,
            name="mkl",
            version="2017.0.3",
            build="0",
            build_number=0,
            channel=Channel("channel-2/osx-64"),
            subdir="osx-64",
            fn="mkl-2017.0.3-0.tar.bz2",
            md5="76cfa5d21e73db338ffccdbe0af8a727",
            url="https://conda.anaconda.org/channel-2/osx-64/mkl-2017.0.3-0.tar.bz2",
            arch="x86_64",
            platform="darwin",
            depends=(),
            constrains=(),
            track_features=(),
            features=(),
            license="proprietary - Intel",
            license_family="Proprietary",
            timestamp=0,
            date="2017-06-26",
            size=135839394,
        )

        solver_ssc = copy.copy(solver.ssc)
        ssc = solver.ssc
        ssc.add_back_map = [MatchSpec("mkl")]
        # Do a transformation from set -> list for precs (done in _run_sat)
        sol_precs = [_ for _ in ssc.solution_precs]
        ssc.solution_precs = copy.copy(sol_precs)
        # Add extra prec to the ssc being used
        sol_precs.append(extra_prec)
        solver_ssc.solution_precs = sol_precs

        # Last modification to ssc before finding orphaned packages is done by run_sat
        solver._run_sat = Mock(return_value=ssc)
        # Give solver the modified ssc
        solver.ssc = solver_ssc
        final_state = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_ALL
        )
        prec_names = [_.name for _ in final_state]
        assert len(prec_names) == len(set(prec_names))


def test_virtual_package_solver(tmpdir, clear_cuda_version):
    if context.solver == "libmamba":
        pytest.skip(
            "conda-libmamba-solver does not use Solver.ssc (SolverStateContainer)"
        )

    specs = (MatchSpec("cudatoolkit"),)

    with env_var("CONDA_OVERRIDE_CUDA", "10.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            _ = solver.solve_final_state()
            ssc = solver.ssc
            # Check the cuda virtual package is included in the solver
            assert "__cuda" in ssc.specs_map.keys()

            # Check that the environment is consistent after installing a
            # package which *depends* on a virtual package
            for pkgs in ssc.solution_precs:
                if pkgs.name == "cudatoolkit":
                    # make sure this package depends on the __cuda virtual
                    # package as a dependency since this is requirement of the
                    # test the test
                    assert "__cuda" in pkgs.depends[0]
            assert ssc.r.bad_installed(ssc.solution_precs, ())[1] is None


def test_solve_msgs_exclude_vp(tmpdir, clear_cuda_version):
    # Sovler hints should exclude virtual packages that are not dependencies
    specs = (
        MatchSpec("python =2.7.5"),
        MatchSpec("readline =5.0"),
    )

    with env_var("CONDA_OVERRIDE_CUDA", "10.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError) as exc:
                solver.solve_final_state()

    assert "__cuda==10.0" not in str(exc.value).strip()


def test_cuda_1(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cudatoolkit"),)

    with env_var("CONDA_OVERRIDE_CUDA", "9.2"):
        with get_solver_cuda(tmpdir, specs) as solver:
            final_state = solver.solve_final_state()
            # print(convert_to_dist_str(final_state))
            order = add_subdir_to_iter(("channel-1::cudatoolkit-9.0-0",))
            assert convert_to_dist_str(final_state) == order


def test_cuda_2(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cudatoolkit"),)

    with env_var("CONDA_OVERRIDE_CUDA", "10.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            final_state = solver.solve_final_state()
            # print(convert_to_dist_str(final_state))
            order = add_subdir_to_iter(("channel-1::cudatoolkit-10.0-0",))
            assert convert_to_dist_str(final_state) == order


def test_cuda_fail_1(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cudatoolkit"),)

    # No cudatoolkit in index for CUDA 8.0
    with env_var("CONDA_OVERRIDE_CUDA", "8.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError) as exc:
                solver.solve_final_state()

    if context.solver == "libmamba":
        # LIBMAMBA ADJUSTMENT
        # We have a different (yet equivalent) error message
        possible_messages = [
            dals(
                """Encountered problems while solving:
  - nothing provides __cuda >=9.0 needed by cudatoolkit-9.0-0"""
            ),
            dals(
                """Encountered problems while solving:
  - nothing provides __cuda >=10.0 needed by cudatoolkit-10.0-0"""
            ),
        ]
        exc_msg = str(exc.value).strip()
        assert any(msg in exc_msg for msg in possible_messages)
    else:
        assert str(exc.value).strip() == dals(
            f"""The following specifications were found to be incompatible with your system:

  - feature:/{context._native_subdir()}::__cuda==8.0=0
  - cudatoolkit -> __cuda[version='>=10.0|>=9.0']

Your installed version is: 8.0"""
        )


def test_cuda_fail_2(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cudatoolkit"),)

    # No CUDA on system
    with env_var("CONDA_OVERRIDE_CUDA", ""):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError) as exc:
                solver.solve_final_state()
    if context.solver == "libmamba":
        # LIBMAMBA ADJUSTMENT
        # We have a different (yet equivalent) error message
        possible_messages = [
            dals(
                """Encountered problems while solving:
  - nothing provides __cuda >=9.0 needed by cudatoolkit-9.0-0"""
            ),
            dals(
                """Encountered problems while solving:
  - nothing provides __cuda >=10.0 needed by cudatoolkit-10.0-0"""
            ),
        ]
        exc_msg = str(exc.value).strip()
        assert any(msg in exc_msg for msg in possible_messages)
    else:
        assert str(exc.value).strip() == dals(
            """The following specifications were found to be incompatible with your system:

  - cudatoolkit -> __cuda[version='>=10.0|>=9.0']

Your installed version is: not available"""
        )


def test_cuda_constrain_absent(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-constrain"),)

    with env_var("CONDA_OVERRIDE_CUDA", ""):
        with get_solver_cuda(tmpdir, specs) as solver:
            final_state = solver.solve_final_state()
            # print(convert_to_dist_str(final_state))
            order = add_subdir_to_iter(("channel-1::cuda-constrain-11.0-0",))
            assert convert_to_dist_str(final_state) == order


@pytest.mark.skip(reason="known broken; fix to be implemented")
def test_cuda_constrain_sat(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-constrain"),)

    with env_var("CONDA_OVERRIDE_CUDA", "10.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            final_state = solver.solve_final_state()
            # print(convert_to_dist_str(final_state))
            order = add_subdir_to_iter(("channel-1::cuda-constrain-10.0-0",))
            assert convert_to_dist_str(final_state) == order


@pytest.mark.skip(reason="known broken; fix to be implemented")
def test_cuda_constrain_unsat(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-constrain"),)

    # No cudatoolkit in index for CUDA 8.0
    with env_var("CONDA_OVERRIDE_CUDA", "8.0"):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError) as exc:
                solver.solve_final_state()

    assert str(exc.value).strip() == dals(
        f"""The following specifications were found to be incompatible with your system:

  - feature:|@/{context.subdir}::__cuda==8.0=0
  - __cuda[version='>=10.0'] -> feature:/linux-64::__cuda==8.0=0

Your installed version is: 8.0"""
    )


@pytest.mark.skipif(not on_linux, reason="linux-only test")
def test_cuda_glibc_sat(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-glibc"),)

    with (
        env_var("CONDA_OVERRIDE_CUDA", "10.0"),
        env_var("CONDA_OVERRIDE_GLIBC", "2.23"),
    ):
        with get_solver_cuda(tmpdir, specs) as solver:
            final_state = solver.solve_final_state()
            # print(convert_to_dist_str(final_state))
            order = add_subdir_to_iter(("channel-1::cuda-glibc-10.0-0",))
            assert convert_to_dist_str(final_state) == order


@pytest.mark.skip(reason="known broken; fix to be implemented")
@pytest.mark.skipif(not on_linux, reason="linux-only test")
def test_cuda_glibc_unsat_depend(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-glibc"),)

    with env_var("CONDA_OVERRIDE_CUDA", "8.0"), env_var("CONDA_OVERRIDE_GLIBC", "2.23"):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError) as exc:
                solver.solve_final_state()

    assert str(exc.value).strip() == dals(
        f"""The following specifications were found to be incompatible with your system:

  - feature:|@/{context.subdir}::__cuda==8.0=0
  - __cuda[version='>=10.0'] -> feature:/linux-64::__cuda==8.0=0

Your installed version is: 8.0"""
    )


@pytest.mark.skip(reason="known broken; fix to be implemented")
@pytest.mark.skipif(not on_linux, reason="linux-only test")
def test_cuda_glibc_unsat_constrain(tmpdir, clear_cuda_version):
    specs = (MatchSpec("cuda-glibc"),)

    with (
        env_var("CONDA_OVERRIDE_CUDA", "10.0"),
        env_var("CONDA_OVERRIDE_GLIBC", "2.12"),
    ):
        with get_solver_cuda(tmpdir, specs) as solver:
            with pytest.raises(UnsatisfiableError):
                solver.solve_final_state()


@pytest.mark.skipif(
    not (on_win or on_mac or on_linux),
    reason="archspec is only supported on win, mac or linux",
)
def test_archspec_call(tmpdir):
    specs = (MatchSpec("numpy"),)

    with env_vars(), patch("archspec.cpu.host") as archspec:
        if "CONDA_OVERRIDE_ARCHSPEC" in os.environ:
            del os.environ["CONDA_OVERRIDE_ARCHSPEC"]

        with get_solver_cuda(tmpdir, specs) as solver:
            solver.solve_final_state()
            archspec.assert_called()


def test_prune_1(tmpdir, request):
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba",
            reason="Features not supported in libmamba",
            strict=True,
        )
    )

    specs = (
        MatchSpec("numpy=1.6"),
        MatchSpec("python=2.7.3"),
        MatchSpec("accelerate"),
    )

    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::libnvvm-1.0-p0",
                "channel-1::mkl-rt-11.0-p0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.3-7",
                "channel-1::bitarray-0.8.1-py27_0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::mkl-service-1.0.0-py27_p0",
                "channel-1::numpy-1.6.2-py27_p4",
                "channel-1::numba-0.8.1-np16py27_0",
                "channel-1::numexpr-2.1-np16py27_p0",
                "channel-1::scipy-0.12.0-np16py27_p0",
                "channel-1::numbapro-0.11.0-np16py27_p0",
                "channel-1::scikit-learn-0.13.1-np16py27_p0",
                "channel-1::mkl-11.0-np16py27_p0",
                "channel-1::accelerate-1.1.0-np16py27_p0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_remove = (MatchSpec("numbapro"),)
    with get_solver(
        tmpdir,
        specs_to_remove=specs_to_remove,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(
            (
                "channel-1::accelerate-1.1.0-np16py27_p0",
                "channel-1::mkl-11.0-np16py27_p0",
                "channel-1::scikit-learn-0.13.1-np16py27_p0",
                "channel-1::numbapro-0.11.0-np16py27_p0",
                "channel-1::scipy-0.12.0-np16py27_p0",
                "channel-1::numexpr-2.1-np16py27_p0",
                "channel-1::numba-0.8.1-np16py27_0",
                "channel-1::numpy-1.6.2-py27_p4",
                "channel-1::mkl-service-1.0.0-py27_p0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::bitarray-0.8.1-py27_0",
                "channel-1::llvm-3.2-0",
                "channel-1::mkl-rt-11.0-p0",
                "channel-1::libnvvm-1.0-p0",
            )
        )
        link_order = add_subdir_to_iter(("channel-1::numpy-1.6.2-py27_4",))
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order


def test_update_prune_1(tmpdir):
    """Regression test: Ensure that update with prune is not taking the history
    into account, since that stops it from removing packages that were removed
    from the environment specs.
    """
    specs = (
        MatchSpec("numpy"),
        MatchSpec("python=2.7.3"),
    )

    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # Here numpy is removed from the specs (but the old specs are kept as
    # history).
    new_environment_specs = (MatchSpec("python=2.7.3"),)

    with get_solver(
        tmpdir,
        new_environment_specs,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_1 = solver.solve_final_state(prune=True)
        pprint(convert_to_dist_str(final_state_1))

        # Numpy should now be absent from the solved packages.
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
            )
        )
        assert convert_to_dist_str(final_state_1) == order


def test_update_prune_2(tmpdir, request):
    """Regression test: Ensure that update with prune is pruning dependencies
    of packages that are removed from environment as well.
    """
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba",
            reason="Features not supported in libmamba",
            strict=True,
        )
    )

    specs = (
        MatchSpec("python=2.7.3"),
        MatchSpec("accelerate"),
    )

    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::libnvvm-1.0-p0",
                "channel-1::mkl-rt-11.0-p0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.3-7",
                "channel-1::bitarray-0.8.1-py27_0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::mkl-service-1.0.0-py27_p0",
                "channel-1::numpy-1.7.1-py27_p0",
                "channel-1::numba-0.8.1-np17py27_0",
                "channel-1::numexpr-2.1-np17py27_p0",
                "channel-1::scipy-0.12.0-np17py27_p0",
                "channel-1::numbapro-0.11.0-np17py27_p0",
                "channel-1::scikit-learn-0.13.1-np17py27_p0",
                "channel-1::mkl-11.0-np17py27_p0",
                "channel-1::accelerate-1.1.0-np17py27_p0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # Here accelerate is removed from the specs (but the old specs are kept as
    # history). This should cause every dependency of accelerate to disappear
    # from the pruned package list.
    new_environment_specs = (MatchSpec("python=2.7.3"),)

    with get_solver(
        tmpdir,
        new_environment_specs,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_1 = solver.solve_final_state(prune=True)
        pprint(convert_to_dist_str(final_state_1))

        # Every dependecy of accelerate should now be absent from the solved
        # packages.
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
            )
        )
        assert convert_to_dist_str(final_state_1) == order


def test_update_prune_3(tmpdir, request):
    """Ensure that update with prune is not removing packages that are still
    needed by remaining specs.
    """
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba",
            reason="Features not supported in libmamba",
            strict=True,
        )
    )

    specs = (
        MatchSpec("numpy"),
        MatchSpec("python=2.7.3"),
        MatchSpec("accelerate"),
    )

    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::libnvvm-1.0-p0",
                "channel-1::mkl-rt-11.0-p0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.3-7",
                "channel-1::bitarray-0.8.1-py27_0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::mkl-service-1.0.0-py27_p0",
                "channel-1::numpy-1.7.1-py27_p0",
                "channel-1::numba-0.8.1-np17py27_0",
                "channel-1::numexpr-2.1-np17py27_p0",
                "channel-1::scipy-0.12.0-np17py27_p0",
                "channel-1::numbapro-0.11.0-np17py27_p0",
                "channel-1::scikit-learn-0.13.1-np17py27_p0",
                "channel-1::mkl-11.0-np17py27_p0",
                "channel-1::accelerate-1.1.0-np17py27_p0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # Here accelerate is removed from the specs (but the old specs are kept as
    # history). This should cause every dependency of accelerate to disappear
    # from the pruned package list, but numpy should be kept, because it is still
    # in the specs.
    new_environment_specs = (
        MatchSpec("numpy"),
        MatchSpec("python=2.7.3"),
    )

    with get_solver(
        tmpdir,
        new_environment_specs,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_1 = solver.solve_final_state(prune=True)
        pprint(convert_to_dist_str(final_state_1))

        # Every dependecy of accelerate should now be absent from the solved packages,
        # but numpy should remain.
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order


def test_update_prune_4(tmpdir):
    """Regression test: Ensure that update with prune is not taking the history
    into account, since that stops it from removing packages that were removed
    from the environment specs.
    """
    specs = (
        MatchSpec("numpy"),
        MatchSpec("python=2.7.3"),
    )

    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # Here numpy is removed from the specs (but the old specs are kept as
    # history).
    new_environment_specs = (MatchSpec("python=2.7.3"),)

    with get_solver(
        tmpdir,
        new_environment_specs,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_1 = solver.solve_final_state(
            prune=True,
            update_modifier=UpdateModifier.FREEZE_INSTALLED,
        )
        pprint(convert_to_dist_str(final_state_1))

        # Numpy should now be absent from the solved packages.
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
            )
        )
        assert convert_to_dist_str(final_state_1) == order


@pytest.mark.parametrize("prune", [True, False])
def test_update_prune_5(tmpdir, prune, capsys, request):
    """Regression test: Check that prefix data is not taken into account when solving on prune."""
    # "Create" a conda env with specs that "pin" dependencies.
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba" and not prune,
            reason="Features not supported in libmamba",
            strict=True,
        )
    )

    specs = (
        MatchSpec("python=2.7"),
        MatchSpec("numexpr==2.0.1=np17py27_p3"),
    )
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()

    out, _ = capsys.readouterr()
    assert "Updating numexpr is constricted by" not in out

    # If prefix data is evaluated it will conflict with "pinned" dependencies of new specs.
    new_environment_specs = (
        MatchSpec("python=2.7"),
        MatchSpec("numexpr==2.0.1=np17py27_p2"),
    )
    with get_solver(
        tmpdir,
        new_environment_specs,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        solver.solve_final_state(prune=prune)

    out, _ = capsys.readouterr()
    solve_using_prefix_data = not prune
    assert ("Updating numexpr is constricted by" in out) is solve_using_prefix_data


def test_force_remove_1(tmpdir, request):
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba",
            reason="Known limitation. VERIFY task needed to make this pass, but breaks other tests."
            " See https://github.com/conda/conda-libmamba-solver/pull/302",
            strict=True,
        )
    )

    specs = (MatchSpec("numpy[version=*,build=*py27*]"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # without force_remove, taking out python takes out everything that depends on it, too,
    #    so numpy goes away.  All of pythons' deps are also pruned.
    specs_to_remove = (MatchSpec("python"),)
    with get_solver(
        tmpdir,
        specs_to_remove=specs_to_remove,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        # openssl remains because it is in the aggressive_update_packages set,
        #    but everything else gets removed
        order = add_subdir_to_iter(("channel-1::openssl-1.0.1c-0",))
        assert convert_to_dist_str(final_state_2) == order

    # with force remove, we remove only the explicit specs that we provide
    #    this leaves an inconsistent env
    specs_to_remove = (MatchSpec("python"),)
    with get_solver(
        tmpdir,
        specs_to_remove=specs_to_remove,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state_2 = solver.solve_final_state(force_remove=True)
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::numpy-1.7.1-py27_0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    # re-solving restores order
    with get_solver(tmpdir, prefix_records=final_state_2) as solver:
        final_state_3 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_3))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_3) == order


def test_no_deps_1(tmpdir):
    specs = (MatchSpec("python=2"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = (MatchSpec("numba"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.5-0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::numpy-1.7.1-py27_0",
                "channel-1::numba-0.8.1-np17py27_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    specs_to_add = (MatchSpec("numba"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state(deps_modifier="NO_DEPS")
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::numba-0.8.1-np17py27_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order


def test_only_deps_1(tmpdir):
    specs = (MatchSpec("numba[version=*,build=*py27*]"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state(deps_modifier=DepsModifier.ONLY_DEPS)
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.5-0",
                "channel-1::llvmpy-0.11.2-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::numpy-1.7.1-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order


def test_only_deps_2(tmpdir):
    specs = (
        MatchSpec("numpy=1.5"),
        MatchSpec("python=2.7.3"),
    )
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::numpy-1.5.1-py27_4",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = (MatchSpec("numba=0.5"),)
    with get_solver(tmpdir, specs_to_add) as solver:
        final_state_2 = solver.solve_final_state(deps_modifier=DepsModifier.ONLY_DEPS)
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.5-0",
                "channel-1::llvmpy-0.10.0-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.1-py27_0",
                # 'channel-1::numba-0.5.0-np17py27_0',  # not in the order because only_deps
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    # fails because numpy=1.5 is in our history as an explicit spec
    specs_to_add = (MatchSpec("numba=0.5"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        with pytest.raises(UnsatisfiableError):
            final_state_2 = solver.solve_final_state(
                deps_modifier=DepsModifier.ONLY_DEPS
            )

    specs_to_add = MatchSpec("numba=0.5"), MatchSpec("numpy")
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state(deps_modifier=DepsModifier.ONLY_DEPS)
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.7.3-7",
                "channel-1::llvmpy-0.10.0-py27_0",
                "channel-1::meta-0.4.2.dev-py27_0",
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.1-py27_0",
                # 'channel-1::numba-0.5.0-np17py27_0',   # not in the order because only_deps
            )
        )
        assert convert_to_dist_str(final_state_2) == order


def test_update_all_1(tmpdir):
    if context.solver == "libmamba":
        # LIBMAMBA ADJUSTMENT
        # Libmamba requires MatchSpec.conda_build_form() internally, which depends on `version` and
        # `build` fields. `system` below is using only `build_number`, so we have to adapt the syntax
        # accordingly. It should be the same result, but in a conda_build_form-friendly way:
        system_spec = "system[version=*,build=*0]"
    else:
        system_spec = "system[version=*,build_number=0]"
    specs = (
        MatchSpec("numpy=1.5"),
        MatchSpec("python=2.6"),
        MatchSpec(system_spec),
    )
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-0",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.6.8-6",
                "channel-1::numpy-1.5.1-py26_4",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = MatchSpec("numba=0.6"), MatchSpec("numpy")
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-0",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.6.8-6",
                "channel-1::llvmpy-0.10.2-py26_0",
                "channel-1::nose-1.3.0-py26_0",
                "channel-1::numpy-1.7.1-py26_0",
                "channel-1::numba-0.6.0-np17py26_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    specs_to_add = (MatchSpec("numba=0.6"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_ALL
        )
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-2.6.8-6",  # stick with python=2.6 even though UPDATE_ALL
                "channel-1::llvmpy-0.10.2-py26_0",
                "channel-1::nose-1.3.0-py26_0",
                "channel-1::numpy-1.7.1-py26_0",
                "channel-1::numba-0.6.0-np17py26_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order


def test_broken_install(tmpdir):
    if context.solver == "libmamba":
        pytest.skip("conda-libmamba-solver does not use a Solver._r (Resolve) object")

    specs = MatchSpec("pandas=0.11.0=np16py27_1"), MatchSpec("python=2.7")
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order_original = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::numpy-1.6.2-py27_4",
                "channel-1::pytz-2013b-py27_0",
                "channel-1::six-1.3.0-py27_0",
                "channel-1::dateutil-2.1-py27_1",
                "channel-1::scipy-0.12.0-np16py27_0",
                "channel-1::pandas-0.11.0-np16py27_1",
            )
        )
        assert convert_to_dist_str(final_state_1) == order_original
        assert solver._r.environment_is_consistent(final_state_1)

    # Add an incompatible numpy; installation should be untouched
    final_state_1_modified = list(final_state_1)
    numpy_matcher = MatchSpec("channel-1::numpy==1.7.1=py33_p0")
    numpy_prec = next(prec for prec in solver._index if numpy_matcher.match(prec))
    final_state_1_modified[7] = numpy_prec
    assert not solver._r.environment_is_consistent(final_state_1_modified)

    specs_to_add = (MatchSpec("flask"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1_modified, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::numpy-1.7.1-py33_p0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::jinja2-2.6-py27_0",
                "channel-1::pytz-2013b-py27_0",
                "channel-1::scipy-0.12.0-np16py27_0",
                "channel-1::six-1.3.0-py27_0",
                "channel-1::werkzeug-0.8.3-py27_0",
                "channel-1::dateutil-2.1-py27_1",
                "channel-1::flask-0.9-py27_0",
                "channel-1::pandas-0.11.0-np16py27_1",
            )
        )
        assert convert_to_dist_str(final_state_2) == order
        assert not solver._r.environment_is_consistent(final_state_2)

    # adding numpy spec again snaps the packages back to a consistent state
    specs_to_add = (
        MatchSpec("flask"),
        MatchSpec("numpy 1.6.*"),
    )
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1_modified, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::jinja2-2.6-py27_0",
                "channel-1::numpy-1.6.2-py27_4",
                "channel-1::pytz-2013b-py27_0",
                "channel-1::six-1.3.0-py27_0",
                "channel-1::werkzeug-0.8.3-py27_0",
                "channel-1::dateutil-2.1-py27_1",
                "channel-1::flask-0.9-py27_0",
                "channel-1::scipy-0.12.0-np16py27_0",
                "channel-1::pandas-0.11.0-np16py27_1",
            )
        )
        assert convert_to_dist_str(final_state_2) == order
        assert solver._r.environment_is_consistent(final_state_2)

    # Add an incompatible pandas; installation should be untouched, then fixed
    final_state_2_mod = list(final_state_1)
    pandas_matcher = MatchSpec("channel-1::pandas==0.11.0=np17py27_1")
    pandas_prec = next(prec for prec in solver._index if pandas_matcher.match(prec))
    final_state_2_mod[12] = pandas_prec
    assert not solver._r.environment_is_consistent(final_state_2_mod)


def test_conda_downgrade(tmpdir, request):
    if context.solver == "libmamba":
        request.applymarker(
            pytest.mark.xfail(
                context.solver == "libmamba",
                reason="Known flaky:https://github.com/conda/conda-libmamba-solver/issues/317",
            )
        )
    specs = (MatchSpec("conda-build"),)
    with env_var(
        "CONDA_CHANNEL_PRIORITY", "False", stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        with get_solver_aggregate_1(tmpdir, specs) as solver:
            final_state_1 = solver.solve_final_state()
            pprint(convert_to_dist_str(final_state_1))
            order = add_subdir_to_iter(
                (
                    "channel-4::ca-certificates-2018.03.07-0",
                    "channel-2::conda-env-2.6.0-0",
                    "channel-2::libffi-3.2.1-1",
                    "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                    "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                    "channel-2::zlib-1.2.11-0",
                    "channel-4::ncurses-6.1-hf484d3e_0",
                    "channel-4::openssl-1.0.2p-h14c3975_0",
                    "channel-4::patchelf-0.9-hf484d3e_2",
                    "channel-4::tk-8.6.7-hc745277_3",
                    "channel-4::xz-5.2.4-h14c3975_4",
                    "channel-4::yaml-0.1.7-had09818_2",
                    "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                    "channel-4::readline-7.0-ha6073c6_4",
                    "channel-4::sqlite-3.24.0-h84994c4_0",
                    "channel-4::python-3.7.0-hc3d631a_0",
                    "channel-4::asn1crypto-0.24.0-py37_0",
                    "channel-4::beautifulsoup4-4.6.3-py37_0",
                    "channel-4::certifi-2018.8.13-py37_0",
                    "channel-4::chardet-3.0.4-py37_1",
                    "channel-4::cryptography-vectors-2.3-py37_0",
                    "channel-4::filelock-3.0.4-py37_0",
                    "channel-4::glob2-0.6-py37_0",
                    "channel-4::idna-2.7-py37_0",
                    "channel-4::markupsafe-1.0-py37h14c3975_1",
                    "channel-4::pkginfo-1.4.2-py37_1",
                    "channel-4::psutil-5.4.6-py37h14c3975_0",
                    "channel-4::pycosat-0.6.3-py37h14c3975_0",
                    "channel-4::pycparser-2.18-py37_1",
                    "channel-4::pysocks-1.6.8-py37_0",
                    "channel-4::pyyaml-3.13-py37h14c3975_0",
                    "channel-4::ruamel_yaml-0.15.46-py37h14c3975_0",
                    "channel-4::six-1.11.0-py37_1",
                    "channel-4::cffi-1.11.5-py37h9745a5d_0",
                    "channel-4::setuptools-40.0.0-py37_0",
                    "channel-4::cryptography-2.3-py37hb7f436b_0",
                    "channel-4::jinja2-2.10-py37_0",
                    "channel-4::pyopenssl-18.0.0-py37_0",
                    "channel-4::urllib3-1.23-py37_0",
                    "channel-4::requests-2.19.1-py37_0",
                    "channel-4::conda-4.5.10-py37_0",
                    "channel-4::conda-build-3.12.1-py37_0",
                )
            )
            assert convert_to_dist_str(final_state_1) == order

    specs_to_add = (MatchSpec("itsdangerous"),)  # MatchSpec("conda"),
    saved_sys_prefix = sys.prefix
    try:
        sys.prefix = tmpdir.strpath
        with get_solver_aggregate_1(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            unlink_precs, link_precs = solver.solve_for_diff()
            pprint(convert_to_dist_str(unlink_precs))
            pprint(convert_to_dist_str(link_precs))
            unlink_order = (
                # no conda downgrade
            )
            link_order = ("channel-2/noarch::itsdangerous-0.24-py_0",)
            assert convert_to_dist_str(unlink_precs) == unlink_order
            assert convert_to_dist_str(link_precs) == link_order

        specs_to_add = (
            MatchSpec("itsdangerous"),
            MatchSpec("conda"),
        )
        with get_solver_aggregate_1(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            unlink_precs, link_precs = solver.solve_for_diff()
            pprint(convert_to_dist_str(unlink_precs))
            pprint(convert_to_dist_str(link_precs))
            assert convert_to_dist_str(unlink_precs) == unlink_order
            assert convert_to_dist_str(link_precs) == link_order

        specs_to_add = (
            MatchSpec("itsdangerous"),
            MatchSpec("conda<4.4.10"),
            MatchSpec("python"),
        )
        with get_solver_aggregate_1(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            unlink_precs, link_precs = solver.solve_for_diff()
            pprint(convert_to_dist_str(unlink_precs))
            pprint(convert_to_dist_str(link_precs))
            unlink_order = add_subdir_to_iter(
                (
                    # now conda gets downgraded
                    "channel-4::conda-build-3.12.1-py37_0",
                    "channel-4::conda-4.5.10-py37_0",
                    "channel-4::requests-2.19.1-py37_0",
                    "channel-4::urllib3-1.23-py37_0",
                    "channel-4::pyopenssl-18.0.0-py37_0",
                    "channel-4::jinja2-2.10-py37_0",
                    "channel-4::cryptography-2.3-py37hb7f436b_0",
                    "channel-4::setuptools-40.0.0-py37_0",
                    "channel-4::cffi-1.11.5-py37h9745a5d_0",
                    "channel-4::six-1.11.0-py37_1",
                    "channel-4::ruamel_yaml-0.15.46-py37h14c3975_0",
                    "channel-4::pyyaml-3.13-py37h14c3975_0",
                    "channel-4::pysocks-1.6.8-py37_0",
                    "channel-4::pycparser-2.18-py37_1",
                    "channel-4::pycosat-0.6.3-py37h14c3975_0",
                    "channel-4::psutil-5.4.6-py37h14c3975_0",
                    "channel-4::pkginfo-1.4.2-py37_1",
                    "channel-4::markupsafe-1.0-py37h14c3975_1",
                    "channel-4::idna-2.7-py37_0",
                    "channel-4::glob2-0.6-py37_0",
                    "channel-4::filelock-3.0.4-py37_0",
                    "channel-4::cryptography-vectors-2.3-py37_0",
                    "channel-4::chardet-3.0.4-py37_1",
                    "channel-4::certifi-2018.8.13-py37_0",
                    "channel-4::beautifulsoup4-4.6.3-py37_0",
                    "channel-4::asn1crypto-0.24.0-py37_0",
                    "channel-4::python-3.7.0-hc3d631a_0",
                    "channel-4::sqlite-3.24.0-h84994c4_0",
                    "channel-4::readline-7.0-ha6073c6_4",
                    "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                    "channel-4::yaml-0.1.7-had09818_2",
                    "channel-4::xz-5.2.4-h14c3975_4",
                    "channel-4::tk-8.6.7-hc745277_3",
                    "channel-4::openssl-1.0.2p-h14c3975_0",
                    "channel-4::ncurses-6.1-hf484d3e_0",
                )
            )
            link_order = add_subdir_to_iter(
                (
                    "channel-2::openssl-1.0.2l-0",
                    "channel-2::readline-6.2-2",
                    "channel-2::sqlite-3.13.0-0",
                    "channel-2::tk-8.5.18-0",
                    "channel-2::xz-5.2.3-0",
                    "channel-2::yaml-0.1.6-0",
                    "channel-2::python-3.6.2-0",
                    "channel-2::asn1crypto-0.22.0-py36_0",
                    "channel-4::beautifulsoup4-4.6.3-py36_0",
                    "channel-2::certifi-2016.2.28-py36_0",
                    "channel-4::chardet-3.0.4-py36_1",
                    "channel-4::filelock-3.0.4-py36_0",
                    "channel-4::glob2-0.6-py36_0",
                    "channel-2::idna-2.6-py36_0",
                    "channel-2::itsdangerous-0.24-py36_0",
                    "channel-2::markupsafe-1.0-py36_0",
                    "channel-4::pkginfo-1.4.2-py36_1",
                    "channel-2::psutil-5.2.2-py36_0",
                    "channel-2::pycosat-0.6.2-py36_0",
                    "channel-2::pycparser-2.18-py36_0",
                    "channel-2::pyparsing-2.2.0-py36_0",
                    "channel-2::pyyaml-3.12-py36_0",
                    "channel-2::requests-2.14.2-py36_0",
                    "channel-2::ruamel_yaml-0.11.14-py36_1",
                    "channel-2::six-1.10.0-py36_0",
                    "channel-2::cffi-1.10.0-py36_0",
                    "channel-2::packaging-16.8-py36_0",
                    "channel-2::setuptools-36.4.0-py36_1",
                    "channel-2::cryptography-1.8.1-py36_0",
                    "channel-2::jinja2-2.9.6-py36_0",
                    "channel-2::pyopenssl-17.0.0-py36_0",
                    "channel-2::conda-4.3.30-py36h5d9f9f4_0",
                    "channel-4::conda-build-3.12.1-py36_0",
                )
            )
            if context.solver == "libmamba":
                # LIBMAMBA ADJUSTMENT
                # We only check for conda itself and the explicit specs
                # The other packages are slightly different;
                # again libedit and ncurses are involved
                # (they are also involved in test_fast_update_with_update_modifier_not_set)
                for pkg in link_precs:
                    if pkg.name == "conda":
                        assert VersionOrder(pkg.version) < VersionOrder("4.4.10")
                    elif pkg.name == "python":
                        assert pkg.version == "3.6.2"
                    elif pkg.name == "conda-build":
                        assert pkg.version == "3.12.1"
                    elif pkg.name == "itsdangerous":
                        assert pkg.version == "0.24"
            else:
                assert convert_to_dist_str(unlink_precs) == unlink_order
                assert convert_to_dist_str(link_precs) == link_order
    finally:
        sys.prefix = saved_sys_prefix


def test_unfreeze_when_required(tmpdir):
    # The available packages are:
    # libfoo 1.0, 2.0
    # libbar 1.0, 2.0
    # foobar 1.0 : depends on libfoo 1.0, libbar 2.0
    # foobar 2.0 : depends on libfoo 2.0, libbar 2.0
    # qux 1.0: depends on libfoo 1.0, libbar 2.0
    # qux 2.0: depends on libfoo 2.0, libbar 1.0
    #
    # qux 1.0 and foobar 1.0 can be installed at the same time but
    # if foobar is installed first it must be downgraded from 2.0.
    # If foobar is frozen then no solution exists.

    specs = [MatchSpec("foobar"), MatchSpec("qux")]
    with get_solver_must_unfreeze(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-freeze::libbar-2.0-0",
                "channel-freeze::libfoo-1.0-0",
                "channel-freeze::foobar-1.0-0",
                "channel-freeze::qux-1.0-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs = (MatchSpec("foobar"),)
    with get_solver_must_unfreeze(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-freeze::libbar-2.0-0",
                "channel-freeze::libfoo-2.0-0",
                "channel-freeze::foobar-2.0-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    # When frozen there is no solution - but conda tries really hard to not freeze things that conflict
    #    this section of the test broke when we improved the detection of conflicting specs.
    # specs_to_add = MatchSpec("qux"),
    # with get_solver_must_unfreeze(specs_to_add, prefix_records=final_state_1, history_specs=specs) as solver:
    #     with pytest.raises(UnsatisfiableError):
    #         solver.solve_final_state(update_modifier=UpdateModifier.FREEZE_INSTALLED)

    specs_to_add = (MatchSpec("qux"),)
    with get_solver_must_unfreeze(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_SPECS
        )
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-freeze::libbar-2.0-0",
                "channel-freeze::libfoo-1.0-0",
                "channel-freeze::foobar-1.0-0",
                "channel-freeze::qux-1.0-0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order


def test_auto_update_conda(tmpdir):
    specs = (MatchSpec("conda=1.3"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::yaml-0.1.4-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::pyyaml-3.10-py27_0",
                "channel-1::conda-1.3.5-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    with env_vars(
        {"CONDA_AUTO_UPDATE_CONDA": "yes"}, stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        specs_to_add = (MatchSpec("pytz"),)
        with get_solver(
            tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
        ) as solver:
            final_state_2 = solver.solve_final_state()
            # PrefixDag(final_state_2, specs).open_url()
            print(convert_to_dist_str(final_state_2))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-1",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::yaml-0.1.4-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::python-2.7.5-0",
                    "channel-1::pytz-2013b-py27_0",
                    "channel-1::pyyaml-3.10-py27_0",
                    "channel-1::conda-1.3.5-py27_0",
                )
            )
            assert convert_to_dist_str(final_state_2) == order

    saved_sys_prefix = sys.prefix
    try:
        sys.prefix = tmpdir.strpath
        with env_vars(
            {"CONDA_AUTO_UPDATE_CONDA": "yes"},
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            specs_to_add = (MatchSpec("pytz"),)
            with get_solver(
                tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
            ) as solver:
                final_state_2 = solver.solve_final_state()
                # PrefixDag(final_state_2, specs).open_url()
                print(convert_to_dist_str(final_state_2))
                order = add_subdir_to_iter(
                    (
                        "channel-1::openssl-1.0.1c-0",
                        "channel-1::readline-6.2-0",
                        "channel-1::sqlite-3.7.13-0",
                        "channel-1::system-5.8-1",
                        "channel-1::tk-8.5.13-0",
                        "channel-1::yaml-0.1.4-0",
                        "channel-1::zlib-1.2.7-0",
                        "channel-1::python-2.7.5-0",
                        "channel-1::pytz-2013b-py27_0",
                        "channel-1::pyyaml-3.10-py27_0",
                        "channel-1::conda-1.5.2-py27_0",
                    )
                )
                assert convert_to_dist_str(final_state_2) == order

        with env_vars(
            {"CONDA_AUTO_UPDATE_CONDA": "no"},
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            specs_to_add = (MatchSpec("pytz"),)
            with get_solver(
                tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
            ) as solver:
                final_state_2 = solver.solve_final_state()
                # PrefixDag(final_state_2, specs).open_url()
                print(convert_to_dist_str(final_state_2))
                order = add_subdir_to_iter(
                    (
                        "channel-1::openssl-1.0.1c-0",
                        "channel-1::readline-6.2-0",
                        "channel-1::sqlite-3.7.13-0",
                        "channel-1::system-5.8-1",
                        "channel-1::tk-8.5.13-0",
                        "channel-1::yaml-0.1.4-0",
                        "channel-1::zlib-1.2.7-0",
                        "channel-1::python-2.7.5-0",
                        "channel-1::pytz-2013b-py27_0",
                        "channel-1::pyyaml-3.10-py27_0",
                        "channel-1::conda-1.3.5-py27_0",
                    )
                )
                assert convert_to_dist_str(final_state_2) == order
    finally:
        sys.prefix = saved_sys_prefix


def test_explicit_conda_downgrade(tmpdir):
    specs = (MatchSpec("conda=1.5"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::yaml-0.1.4-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::pyyaml-3.10-py27_0",
                "channel-1::conda-1.5.2-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    with env_vars(
        {"CONDA_AUTO_UPDATE_CONDA": "yes"}, stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        specs_to_add = (MatchSpec("conda=1.3"),)
        with get_solver(
            tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
        ) as solver:
            final_state_2 = solver.solve_final_state()
            # PrefixDag(final_state_2, specs).open_url()
            print(convert_to_dist_str(final_state_2))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-1",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::yaml-0.1.4-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::python-2.7.5-0",
                    "channel-1::pyyaml-3.10-py27_0",
                    "channel-1::conda-1.3.5-py27_0",
                )
            )
            assert convert_to_dist_str(final_state_2) == order

    saved_sys_prefix = sys.prefix
    try:
        sys.prefix = tmpdir.strpath
        with env_vars(
            {"CONDA_AUTO_UPDATE_CONDA": "yes"},
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            specs_to_add = (MatchSpec("conda=1.3"),)
            with get_solver(
                tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
            ) as solver:
                final_state_2 = solver.solve_final_state()
                # PrefixDag(final_state_2, specs).open_url()
                print(convert_to_dist_str(final_state_2))
                order = add_subdir_to_iter(
                    (
                        "channel-1::openssl-1.0.1c-0",
                        "channel-1::readline-6.2-0",
                        "channel-1::sqlite-3.7.13-0",
                        "channel-1::system-5.8-1",
                        "channel-1::tk-8.5.13-0",
                        "channel-1::yaml-0.1.4-0",
                        "channel-1::zlib-1.2.7-0",
                        "channel-1::python-2.7.5-0",
                        "channel-1::pyyaml-3.10-py27_0",
                        "channel-1::conda-1.3.5-py27_0",
                    )
                )
                assert convert_to_dist_str(final_state_2) == order

        with env_vars(
            {"CONDA_AUTO_UPDATE_CONDA": "no"},
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            specs_to_add = (MatchSpec("conda=1.3"),)
            with get_solver(
                tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
            ) as solver:
                final_state_2 = solver.solve_final_state()
                # PrefixDag(final_state_2, specs).open_url()
                print(convert_to_dist_str(final_state_2))
                order = add_subdir_to_iter(
                    (
                        "channel-1::openssl-1.0.1c-0",
                        "channel-1::readline-6.2-0",
                        "channel-1::sqlite-3.7.13-0",
                        "channel-1::system-5.8-1",
                        "channel-1::tk-8.5.13-0",
                        "channel-1::yaml-0.1.4-0",
                        "channel-1::zlib-1.2.7-0",
                        "channel-1::python-2.7.5-0",
                        "channel-1::pyyaml-3.10-py27_0",
                        "channel-1::conda-1.3.5-py27_0",
                    )
                )
                assert convert_to_dist_str(final_state_2) == order
    finally:
        sys.prefix = saved_sys_prefix


def test_aggressive_update_packages(tmpdir):
    def solve(prev_state, specs_to_add, order):
        final_state_1, specs = prev_state
        specs_to_add = tuple(MatchSpec(spec_str) for spec_str in specs_to_add)
        with get_solver(
            tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
        ) as solver:
            final_state_2 = solver.solve_final_state()
            print(convert_to_dist_str(final_state_2))
            assert convert_to_dist_str(final_state_2) == order
        concat_specs = specs + specs_to_add
        return final_state_2, concat_specs

    # test with "libpng", "cmake": both have multiple versions and no requirements in "channel-1"

    empty_state = ((), ())
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": ""},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        base_state = solve(
            empty_state,
            ["libpng=1.2"],
            add_subdir_to_iter(("channel-1::libpng-1.2.50-0",)),
        )

    # # ~~has "libpng" restricted to "=1.2" by history_specs~~ NOPE!
    # In conda 4.6 making aggressive_update *more* aggressive, making it override history specs.
    state_1 = base_state
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": "libpng"},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        solve(
            state_1,
            ["cmake=2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.9-0",
                    "channel-1::libpng-1.5.13-1",
                )
            ),
        )
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": ""},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        state_1_2 = solve(
            state_1,
            ["cmake=2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.9-0",
                    "channel-1::libpng-1.2.50-0",
                )
            ),
        )
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": "libpng"},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        solve(
            state_1_2,
            ["cmake>2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.10.2-0",
                    "channel-1::libpng-1.5.13-1",
                )
            ),
        )

    # use new history_specs to remove "libpng" version restriction
    state_2 = (base_state[0], (MatchSpec("libpng"),))
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": "libpng"},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        solve(
            state_2,
            ["cmake=2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.9-0",
                    "channel-1::libpng-1.5.13-1",
                )
            ),
        )
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": ""},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        state_2_2 = solve(
            state_2,
            ["cmake=2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.9-0",
                    "channel-1::libpng-1.2.50-0",
                )
            ),
        )
    with env_vars(
        {"CONDA_AGGRESSIVE_UPDATE_PACKAGES": "libpng"},
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        solve(
            state_2_2,
            ["cmake>2.8.9"],
            add_subdir_to_iter(
                (
                    "channel-1::cmake-2.8.10.2-0",
                    "channel-1::libpng-1.5.13-1",
                )
            ),
        )


def test_python2_update(tmpdir):
    # Here we're actually testing that a user-request will uninstall incompatible packages
    # as necessary.
    specs = MatchSpec("conda"), MatchSpec("python=2")
    with get_solver_4(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order1 = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::conda-env-2.6.0-1",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::yaml-0.1.7-had09818_2",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.24.0-h84994c4_0",
                "channel-4::python-2.7.15-h1571d57_0",
                "channel-4::asn1crypto-0.24.0-py27_0",
                "channel-4::certifi-2018.8.13-py27_0",
                "channel-4::chardet-3.0.4-py27_1",
                "channel-4::cryptography-vectors-2.3-py27_0",
                "channel-4::enum34-1.1.6-py27_1",
                "channel-4::futures-3.2.0-py27_0",
                "channel-4::idna-2.7-py27_0",
                "channel-4::ipaddress-1.0.22-py27_0",
                "channel-4::pycosat-0.6.3-py27h14c3975_0",
                "channel-4::pycparser-2.18-py27_1",
                "channel-4::pysocks-1.6.8-py27_0",
                "channel-4::ruamel_yaml-0.15.46-py27h14c3975_0",
                "channel-4::six-1.11.0-py27_1",
                "channel-4::cffi-1.11.5-py27h9745a5d_0",
                "channel-4::cryptography-2.3-py27hb7f436b_0",
                "channel-4::pyopenssl-18.0.0-py27_0",
                "channel-4::urllib3-1.23-py27_0",
                "channel-4::requests-2.19.1-py27_0",
                "channel-4::conda-4.5.10-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order1

    specs_to_add = (MatchSpec("python=3.7"),)
    with get_solver_4(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::conda-env-2.6.0-1",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::xz-5.2.4-h14c3975_4",
                "channel-4::yaml-0.1.7-had09818_2",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.24.0-h84994c4_0",
                "channel-4::python-3.7.0-hc3d631a_0",
                "channel-4::asn1crypto-0.24.0-py37_0",
                "channel-4::certifi-2018.8.13-py37_0",
                "channel-4::chardet-3.0.4-py37_1",
                "channel-4::idna-2.7-py37_0",
                "channel-4::pycosat-0.6.3-py37h14c3975_0",
                "channel-4::pycparser-2.18-py37_1",
                "channel-4::pysocks-1.6.8-py37_0",
                "channel-4::ruamel_yaml-0.15.46-py37h14c3975_0",
                "channel-4::six-1.11.0-py37_1",
                "channel-4::cffi-1.11.5-py37h9745a5d_0",
                "channel-4::cryptography-2.2.2-py37h14c3975_0",
                "channel-4::pyopenssl-18.0.0-py37_0",
                "channel-4::urllib3-1.23-py37_0",
                "channel-4::requests-2.19.1-py37_0",
                "channel-4::conda-4.5.10-py37_0",
            )
        )
        full_solution = convert_to_dist_str(final_state_2)
        if context.solver == "libmamba":
            # LIBMAMBA ADJUSTMENT
            # libmamba has a different solution here (cryptography 2.3 instead of 2.2.2)
            # and cryptography-vectors (not present in regular conda)
            # they are essentially the same functional solution; the important part here
            # is that the env migrated to Python 3.7, so we only check some packages
            important_parts = add_subdir_to_iter(
                (
                    "channel-4::python-3.7.0-hc3d631a_0",
                    "channel-4::conda-4.5.10-py37_0",
                    "channel-4::pycosat-0.6.3-py37h14c3975_0",
                )
            )

            pprint("Important parts")
            pprint(set(important_parts))
            pprint("Full solution")
            pprint(set(full_solution))
            assert set(important_parts).issubset(set(full_solution))
        else:
            assert full_solution == order


def test_update_deps_1(tmpdir):
    specs = (MatchSpec("python=2"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        # print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs2 = MatchSpec("numpy=1.7.0"), MatchSpec("python=2.7.3")
    with get_solver(
        tmpdir, specs2, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.0-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    specs_to_add = (MatchSpec("iopro"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_2, history_specs=specs2
    ) as solver:
        final_state_3a = solver.solve_final_state()
        print(convert_to_dist_str(final_state_3a))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::unixodbc-2.3.1-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.3-7",
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.0-py27_0",
                "channel-1::iopro-1.5.0-np17py27_p0",
            )
        )
        assert convert_to_dist_str(final_state_3a) == order

    specs_to_add = (MatchSpec("iopro"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_2, history_specs=specs2
    ) as solver:
        final_state_3 = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_DEPS
        )
        pprint(convert_to_dist_str(final_state_3))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::unixodbc-2.3.1-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",  # with update_deps, numpy should switch from 1.7.0 to 1.7.1
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.1-py27_0",  # with update_deps, numpy should switch from 1.7.0 to 1.7.1
                "channel-1::iopro-1.5.0-np17py27_p0",
            )
        )
        assert convert_to_dist_str(final_state_3) == order

    specs_to_add = (MatchSpec("iopro"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_2, history_specs=specs2
    ) as solver:
        final_state_3 = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_DEPS,
            deps_modifier=DepsModifier.ONLY_DEPS,
        )
        pprint(convert_to_dist_str(final_state_3))
        order = add_subdir_to_iter(
            (
                "channel-1::unixodbc-2.3.1-0",
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",  # with update_deps, numpy should switch from 1.7.0 to 1.7.1
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::numpy-1.7.1-py27_0",  # with update_deps, numpy should switch from 1.7.0 to 1.7.1
                # 'channel-1::iopro-1.5.0-np17py27_p0',
            )
        )
        assert convert_to_dist_str(final_state_3) == order


def test_update_deps_2(tmpdir):
    specs = MatchSpec("flask==0.12"), MatchSpec("jinja2==2.8")
    with get_solver_aggregate_2(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order1 = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::xz-5.2.4-h14c3975_4",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.24.0-h84994c4_0",
                "channel-4::python-3.6.6-hc3d631a_0",
                "channel-4::certifi-2018.8.13-py36_0",
                "channel-4::click-6.7-py36_0",
                "channel-4::itsdangerous-0.24-py36_1",
                "channel-4::markupsafe-1.0-py36h14c3975_1",
                "channel-4::werkzeug-0.14.1-py36_0",
                "channel-4::setuptools-40.0.0-py36_0",
                "channel-2::jinja2-2.8-py36_1",
                "channel-2::flask-0.12-py36_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order1

    # The "conda update flask" case is held back by the jinja2==2.8 user-requested spec.
    specs_to_add = (MatchSpec("flask"),)
    with get_solver_aggregate_2(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(("channel-2::flask-0.12-py36_0",))
        link_order = add_subdir_to_iter(("channel-4::flask-0.12.2-py36hb24657c_0",))
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order

    # Now solve with UPDATE_DEPS
    specs_to_add = (MatchSpec("flask"),)
    with get_solver_aggregate_2(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff(
            update_modifier=UpdateModifier.UPDATE_DEPS
        )
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(
            (
                "channel-2::flask-0.12-py36_0",
                "channel-2::jinja2-2.8-py36_1",
            )
        )
        link_order = add_subdir_to_iter(
            (
                "channel-4::jinja2-2.10-py36_0",
                "channel-4::flask-1.0.2-py36_1",
            )
        )
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order


def test_fast_update_with_update_modifier_not_set(tmpdir):
    specs = (
        MatchSpec("python=2"),
        MatchSpec("openssl==1.0.2l"),
        MatchSpec("sqlite=3.21"),
    )
    with get_solver_4(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order1 = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.0-h9df7e31_2",
                "channel-4::openssl-1.0.2l-h077ae2c_5",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1-heed3624_0",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.21.0-h1bed415_2",
                "channel-4::python-2.7.14-h89e7a4a_22",
            )
        )
        assert convert_to_dist_str(final_state_1) == order1

    specs_to_add = (MatchSpec("python"),)
    with get_solver_4(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(
            (
                "channel-4::python-2.7.14-h89e7a4a_22",
                "channel-4::libedit-3.1-heed3624_0",
                "channel-4::openssl-1.0.2l-h077ae2c_5",
                "channel-4::ncurses-6.0-h9df7e31_2",
            )
        )
        link_order = add_subdir_to_iter(
            (
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::xz-5.2.4-h14c3975_4",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::python-3.6.4-hc3d631a_1",  # python is upgraded
            )
        )
        if context.solver == "libmamba":
            # LIBMAMBA ADJUSTMENT
            # We only check python was upgraded as expected, not the full solution
            assert add_subdir(
                "channel-4::python-2.7.14-h89e7a4a_22"
            ) in convert_to_dist_str(unlink_precs)
            assert add_subdir(
                "channel-4::python-3.6.4-hc3d631a_1"
            ) in convert_to_dist_str(link_precs)
        else:
            assert convert_to_dist_str(unlink_precs) == unlink_order
            assert convert_to_dist_str(link_precs) == link_order

    specs_to_add = (MatchSpec("sqlite"),)
    with get_solver_4(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(
            (
                "channel-4::python-2.7.14-h89e7a4a_22",
                "channel-4::sqlite-3.21.0-h1bed415_2",
                "channel-4::libedit-3.1-heed3624_0",
                "channel-4::openssl-1.0.2l-h077ae2c_5",
                "channel-4::ncurses-6.0-h9df7e31_2",
            )
        )
        link_order = add_subdir_to_iter(
            (
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::sqlite-3.24.0-h84994c4_0",  # sqlite is upgraded
                "channel-4::python-2.7.15-h1571d57_0",  # python is not upgraded
            )
        )
        if context.solver == "libmamba":
            # LIBMAMBA ADJUSTMENT
            # We only check sqlite was upgraded as expected and python stays the same
            assert add_subdir(
                "channel-4::sqlite-3.21.0-h1bed415_2"
            ) in convert_to_dist_str(unlink_precs)
            sqlite = next(pkg for pkg in link_precs if pkg.name == "sqlite")
            # mamba chooses a different sqlite version (3.23 instead of 3.24)
            assert VersionOrder(sqlite.version) > VersionOrder("3.21")
            # If Python was changed, it should have stayed at 2.7
            python = next((pkg for pkg in link_precs if pkg.name == "python"), None)
            if python:
                assert python.version.startswith("2.7")
        else:
            assert convert_to_dist_str(unlink_precs) == unlink_order
            assert convert_to_dist_str(link_precs) == link_order

    specs_to_add = (
        MatchSpec("sqlite"),
        MatchSpec("python"),
    )
    with get_solver_4(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state(
            update_modifier=UpdateModifier.SPECS_SATISFIED_SKIP_SOLVE
        )
        pprint(convert_to_dist_str(final_state_2))
        assert convert_to_dist_str(final_state_2) == order1


@pytest.mark.integration
def test_pinned_1(tmpdir):
    specs = (MatchSpec("numpy"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-3.3.2-0",
                "channel-1::numpy-1.7.1-py33_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    with env_var(
        "CONDA_PINNED_PACKAGES",
        "python=2.6&iopro<=1.4.2",
        stack_callback=conda_tests_ctxt_mgmt_def_pol,
    ):
        specs = (MatchSpec("system=5.8=0"),)
        with get_solver(tmpdir, specs) as solver:
            final_state_1 = solver.solve_final_state()
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_1))
            order = add_subdir_to_iter(("channel-1::system-5.8-0",))
            assert convert_to_dist_str(final_state_1) == order

        # ignore_pinned=True
        specs_to_add = (MatchSpec("python"),)
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            final_state_2 = solver.solve_final_state(ignore_pinned=True)
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_2))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-0",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::python-3.3.2-0",
                )
            )
            assert convert_to_dist_str(final_state_2) == order

        # ignore_pinned=False
        specs_to_add = (MatchSpec("python"),)
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            final_state_2 = solver.solve_final_state(ignore_pinned=False)
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_2))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-0",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::python-2.6.8-6",
                )
            )
            assert convert_to_dist_str(final_state_2) == order

        # incompatible CLI and configured specs
        specs_to_add = (MatchSpec("scikit-learn==0.13"),)
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_1,
            history_specs=specs,
        ) as solver:
            if context.solver == "libmamba":
                # LIBMAMBA ADJUSTMENT
                # Original tests checks for SpecsConfigurationConflictError
                # being raised but libmamba will fails with UnsatisfiableError
                # instead. Hence, we check the error string.
                with pytest.raises(UnsatisfiableError) as exc_info:
                    solver.solve_final_state(ignore_pinned=False)
                error = str(exc_info.value)
                assert "package scikit-learn-0.13" in error
                assert "requires python 2.7*" in error
            else:
                with pytest.raises(SpecsConfigurationConflictError) as exc:
                    solver.solve_final_state(ignore_pinned=False)
                kwargs = exc.value._kwargs
                assert kwargs["requested_specs"] == ["scikit-learn==0.13"]
                assert kwargs["pinned_specs"] == ["python=2.6"]

        specs_to_add = (MatchSpec("numba"),)
        history_specs = (
            MatchSpec("python"),
            MatchSpec("system=5.8=0"),
        )
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_2,
            history_specs=history_specs,
        ) as solver:
            final_state_3 = solver.solve_final_state()
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_3))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-0",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::llvm-3.2-0",
                    "channel-1::python-2.6.8-6",
                    "channel-1::argparse-1.2.1-py26_0",
                    "channel-1::llvmpy-0.11.2-py26_0",
                    "channel-1::numpy-1.7.1-py26_0",
                    "channel-1::numba-0.8.1-np17py26_0",
                )
            )
            assert convert_to_dist_str(final_state_3) == order

        specs_to_add = (MatchSpec("python"),)
        history_specs = (
            MatchSpec("python"),
            MatchSpec("system=5.8=0"),
            MatchSpec("numba"),
        )
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_3,
            history_specs=history_specs,
        ) as solver:
            final_state_4 = solver.solve_final_state(
                update_modifier=UpdateModifier.UPDATE_DEPS
            )
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_4))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-1",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::llvm-3.2-0",
                    "channel-1::python-2.6.8-6",
                    "channel-1::argparse-1.2.1-py26_0",
                    "channel-1::llvmpy-0.11.2-py26_0",
                    "channel-1::numpy-1.7.1-py26_0",
                    "channel-1::numba-0.8.1-np17py26_0",
                )
            )
            assert convert_to_dist_str(final_state_4) == order

        specs_to_add = (MatchSpec("python"),)
        history_specs = (
            MatchSpec("python"),
            MatchSpec("system=5.8=0"),
            MatchSpec("numba"),
        )
        with get_solver(
            tmpdir,
            specs_to_add=specs_to_add,
            prefix_records=final_state_4,
            history_specs=history_specs,
        ) as solver:
            final_state_5 = solver.solve_final_state(
                update_modifier=UpdateModifier.UPDATE_ALL
            )
            # PrefixDag(final_state_1, specs).open_url()
            pprint(convert_to_dist_str(final_state_5))
            order = add_subdir_to_iter(
                (
                    "channel-1::openssl-1.0.1c-0",
                    "channel-1::readline-6.2-0",
                    "channel-1::sqlite-3.7.13-0",
                    "channel-1::system-5.8-1",
                    "channel-1::tk-8.5.13-0",
                    "channel-1::zlib-1.2.7-0",
                    "channel-1::llvm-3.2-0",
                    "channel-1::python-2.6.8-6",
                    "channel-1::argparse-1.2.1-py26_0",
                    "channel-1::llvmpy-0.11.2-py26_0",
                    "channel-1::numpy-1.7.1-py26_0",
                    "channel-1::numba-0.8.1-np17py26_0",
                )
            )
            assert convert_to_dist_str(final_state_5) == order

    # now update without pinning
    if context.solver == "libmamba":
        # LIBMAMBA ADJUSTMENT:
        # libmamba decides to stay in python=2.6 unless explicit
        specs_to_add = (MatchSpec("python=3"),)
    else:
        specs_to_add = (MatchSpec("python"),)
    history_specs = (
        MatchSpec("python"),
        MatchSpec("system=5.8=0"),
        MatchSpec("numba"),
    )
    with get_solver(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state_4,
        history_specs=history_specs,
    ) as solver:
        final_state_5 = solver.solve_final_state(
            update_modifier=UpdateModifier.UPDATE_ALL
        )
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_5))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::llvm-3.2-0",
                "channel-1::python-3.3.2-0",
                "channel-1::llvmpy-0.11.2-py33_0",
                "channel-1::numpy-1.7.1-py33_0",
                "channel-1::numba-0.8.1-np17py33_0",
            )
        )
        assert convert_to_dist_str(final_state_5) == order


def test_no_update_deps_1(tmpdir):  # i.e. FREEZE_DEPS
    # NOTE: So far, NOT actually testing the FREEZE_DEPS flag.  I'm unable to contrive a
    # situation where it's actually needed.

    specs = (MatchSpec("python=2"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = (MatchSpec("zope.interface"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
                "channel-1::nose-1.3.0-py27_0",
                "channel-1::zope.interface-4.0.5-py27_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order

    specs_to_add = (MatchSpec("zope.interface>4.1"),)
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        with pytest.raises(UnsatisfiableError):
            final_state_2 = solver.solve_final_state()

    # allow python to float
    specs_to_add = MatchSpec("zope.interface>4.1"), MatchSpec("python")
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        final_state_2 = solver.solve_final_state()
        # PrefixDag(final_state_2, specs).open_url()
        print(convert_to_dist_str(final_state_2))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-3.3.2-0",
                "channel-1::nose-1.3.0-py33_0",
                "channel-1::zope.interface-4.1.1.1-py33_0",
            )
        )
        assert convert_to_dist_str(final_state_2) == order


def test_force_reinstall_1(tmpdir):
    specs = (MatchSpec("python=2"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = specs
    with get_solver(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_dists, link_dists = solver.solve_for_diff()
        assert not unlink_dists
        assert not link_dists

        unlink_dists, link_dists = solver.solve_for_diff(force_reinstall=True)
        assert len(unlink_dists) == len(link_dists) == 1
        assert unlink_dists[0] == link_dists[0]

        unlink_dists, link_dists = solver.solve_for_diff()
        assert not unlink_dists
        assert not link_dists


def test_force_reinstall_2(tmpdir):
    specs = (MatchSpec("python=2"),)
    with get_solver(tmpdir, specs) as solver:
        unlink_dists, link_dists = solver.solve_for_diff(force_reinstall=True)
        assert not unlink_dists
        # PrefixDag(final_state_1, specs).open_url()
        print(convert_to_dist_str(link_dists))
        order = add_subdir_to_iter(
            (
                "channel-1::openssl-1.0.1c-0",
                "channel-1::readline-6.2-0",
                "channel-1::sqlite-3.7.13-0",
                "channel-1::system-5.8-1",
                "channel-1::tk-8.5.13-0",
                "channel-1::zlib-1.2.7-0",
                "channel-1::python-2.7.5-0",
            )
        )
        assert convert_to_dist_str(link_dists) == order


def test_timestamps_1(tmpdir):
    specs = (MatchSpec("python=3.6.2"),)
    with get_solver_4(tmpdir, specs) as solver:
        unlink_dists, link_dists = solver.solve_for_diff(force_reinstall=True)
        assert not unlink_dists
        pprint(convert_to_dist_str(link_dists))
        order = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.0-h9df7e31_2",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::xz-5.2.4-h14c3975_4",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1-heed3624_0",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.23.1-he433501_0",
                "channel-4::python-3.6.2-hca45abc_19",  # this package has a later timestamp but lower hash value
                # than the alternate 'channel-4::python-3.6.2-hda45abc_19'
            )
        )
        assert convert_to_dist_str(link_dists) == order


def test_channel_priority_churn_minimized(tmpdir):
    """
    get_solver_aggregate_2 has [channel-4, channel-2].
    When the channels are reverted in the second operation, we put
    channel-2 first. Under these circumstances, reinstalling
    'itsdangerous' should pick the noarch version from channel-2
    instead of the non-noarch variant in channel-4. Everything
    else should stay the same due to FREEZE_INSTALLED.
    """
    specs = (
        MatchSpec("conda-build"),
        MatchSpec("itsdangerous"),
    )
    with get_solver_aggregate_2(tmpdir, specs) as solver:
        final_state = solver.solve_final_state()

    pprint(convert_to_dist_str(final_state))

    if context.solver == "libmamba":
        # With libmamba v2, we need this extra flag to make this test pass
        # Otherwise, the solver considers the current state as satisfying.
        solver_kwargs = {"force_reinstall": True}
    else:
        solver_kwargs = {}
    with get_solver_aggregate_2(
        tmpdir,
        [MatchSpec("itsdangerous")],
        prefix_records=final_state,
        history_specs=specs,
    ) as solver:
        solver.channels.reverse()
        unlink_dists, link_dists = solver.solve_for_diff(
            update_modifier=UpdateModifier.FREEZE_INSTALLED,
            **solver_kwargs,
        )
        pprint(convert_to_dist_str(unlink_dists))
        pprint(convert_to_dist_str(link_dists))
        assert len(unlink_dists) == 1
        assert len(link_dists) == 1


def test_remove_with_constrained_dependencies(tmpdir):
    # This is a regression test for #6904. Up through conda 4.4.10, removal isn't working
    # correctly with constrained dependencies.
    specs = (
        MatchSpec("conda"),
        MatchSpec("conda-build"),
    )
    with get_solver_4(tmpdir, specs) as solver:
        unlink_dists_1, link_dists_1 = solver.solve_for_diff()
        assert not unlink_dists_1
        pprint(convert_to_dist_str(link_dists_1))
        assert not unlink_dists_1
        order = add_subdir_to_iter(
            (
                "channel-4::ca-certificates-2018.03.07-0",
                "channel-4::conda-env-2.6.0-1",
                "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                "channel-4::libffi-3.2.1-hd88cf55_4",
                "channel-4::ncurses-6.1-hf484d3e_0",
                "channel-4::openssl-1.0.2p-h14c3975_0",
                "channel-4::patchelf-0.9-hf484d3e_2",
                "channel-4::tk-8.6.7-hc745277_3",
                "channel-4::xz-5.2.4-h14c3975_4",
                "channel-4::yaml-0.1.7-had09818_2",
                "channel-4::zlib-1.2.11-ha838bed_2",
                "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                "channel-4::readline-7.0-ha6073c6_4",
                "channel-4::sqlite-3.24.0-h84994c4_0",
                "channel-4::python-3.7.0-hc3d631a_0",
                "channel-4::asn1crypto-0.24.0-py37_0",
                "channel-4::beautifulsoup4-4.6.3-py37_0",
                "channel-4::certifi-2018.8.13-py37_0",
                "channel-4::chardet-3.0.4-py37_1",
                "channel-4::cryptography-vectors-2.3-py37_0",
                "channel-4::filelock-3.0.4-py37_0",
                "channel-4::glob2-0.6-py37_0",
                "channel-4::idna-2.7-py37_0",
                "channel-4::markupsafe-1.0-py37h14c3975_1",
                "channel-4::pkginfo-1.4.2-py37_1",
                "channel-4::psutil-5.4.6-py37h14c3975_0",
                "channel-4::pycosat-0.6.3-py37h14c3975_0",
                "channel-4::pycparser-2.18-py37_1",
                "channel-4::pysocks-1.6.8-py37_0",
                "channel-4::pyyaml-3.13-py37h14c3975_0",
                "channel-4::ruamel_yaml-0.15.46-py37h14c3975_0",
                "channel-4::six-1.11.0-py37_1",
                "channel-4::cffi-1.11.5-py37h9745a5d_0",
                "channel-4::setuptools-40.0.0-py37_0",
                "channel-4::cryptography-2.3-py37hb7f436b_0",
                "channel-4::jinja2-2.10-py37_0",
                "channel-4::pyopenssl-18.0.0-py37_0",
                "channel-4::urllib3-1.23-py37_0",
                "channel-4::requests-2.19.1-py37_0",
                "channel-4::conda-4.5.10-py37_0",
                "channel-4::conda-build-3.12.1-py37_0",
            )
        )
        assert convert_to_dist_str(link_dists_1) == order

    specs_to_remove = (MatchSpec("pycosat"),)
    with get_solver_4(
        tmpdir,
        specs_to_remove=specs_to_remove,
        prefix_records=link_dists_1,
        history_specs=specs,
    ) as solver:
        unlink_dists_2, link_dists_2 = solver.solve_for_diff()
        assert not link_dists_2
        pprint(convert_to_dist_str(unlink_dists_2))
        order = add_subdir_to_iter(
            (
                "channel-4::conda-build-3.12.1-py37_0",
                "channel-4::conda-4.5.10-py37_0",
                "channel-4::pycosat-0.6.3-py37h14c3975_0",
            )
        )
        for spec in order:
            assert spec in convert_to_dist_str(unlink_dists_2)


def test_priority_1(tmpdir, request):
    if context.solver == "libmamba":
        request.applymarker(
            pytest.mark.xfail(
                context.solver == "libmamba",
                reason="libmamba is 'lazier' to change channels if the installed one already "
                "satisfies the request.",
                strict=True,
                run=False,
            )
        )

    with env_var(
        "CONDA_SUBDIR", "linux-64", stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        specs = (
            MatchSpec("pandas"),
            MatchSpec("python=2.7"),
        )
        with env_var(
            "CONDA_CHANNEL_PRIORITY",
            "True",
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            with get_solver_aggregate_1(tmpdir, specs) as solver:
                final_state_1 = solver.solve_final_state()
                pprint(convert_to_dist_str(final_state_1))
                order = add_subdir_to_iter(
                    (
                        "channel-2::mkl-2017.0.3-0",
                        "channel-2::openssl-1.0.2l-0",
                        "channel-2::readline-6.2-2",
                        "channel-2::sqlite-3.13.0-0",
                        "channel-2::tk-8.5.18-0",
                        "channel-2::zlib-1.2.11-0",
                        "channel-2::python-2.7.13-0",
                        "channel-2::numpy-1.13.1-py27_0",
                        "channel-2::pytz-2017.2-py27_0",
                        "channel-2::six-1.10.0-py27_0",
                        "channel-2::python-dateutil-2.6.1-py27_0",
                        "channel-2::pandas-0.20.3-py27_0",
                    )
                )
                assert convert_to_dist_str(final_state_1) == order

        with env_var(
            "CONDA_CHANNEL_PRIORITY",
            "False",
            stack_callback=conda_tests_ctxt_mgmt_def_pol,
        ):
            with get_solver_aggregate_1(
                tmpdir, specs, prefix_records=final_state_1, history_specs=specs
            ) as solver:
                final_state_2 = solver.solve_final_state()
                pprint(convert_to_dist_str(final_state_2))
                # python and pandas will be updated as they are explicit specs.  Other stuff may or may not,
                #     as required to satisfy python and pandas
                order = add_subdir_to_iter(
                    (
                        "channel-4::python-2.7.15-h1571d57_0",
                        "channel-4::pandas-0.23.4-py27h04863e7_0",
                    )
                )
                for spec in order:
                    assert spec in convert_to_dist_str(final_state_2)

        # channel priority taking effect here.  channel-2 should be the channel to draw from.  Downgrades expected.
        # python and pandas will be updated as they are explicit specs.  Other stuff may or may not,
        #     as required to satisfy python and pandas
        with get_solver_aggregate_1(
            tmpdir, specs, prefix_records=final_state_2, history_specs=specs
        ) as solver:
            final_state_3 = solver.solve_final_state()
            pprint(convert_to_dist_str(final_state_3))
            order = add_subdir_to_iter(
                (
                    "channel-2::python-2.7.13-0",
                    "channel-2::pandas-0.20.3-py27_0",
                )
            )
            for spec in order:
                assert spec in convert_to_dist_str(final_state_3)

        specs_to_add = (MatchSpec("six<1.10"),)
        specs_to_remove = (MatchSpec("pytz"),)
        with get_solver_aggregate_1(
            tmpdir,
            specs_to_add=specs_to_add,
            specs_to_remove=specs_to_remove,
            prefix_records=final_state_3,
            history_specs=specs,
        ) as solver:
            final_state_4 = solver.solve_final_state()
            pprint(convert_to_dist_str(final_state_4))
            order = add_subdir_to_iter(
                (
                    "channel-2::python-2.7.13-0",
                    "channel-2::six-1.9.0-py27_0",
                )
            )
            for spec in order:
                assert spec in convert_to_dist_str(final_state_4)
            assert "pandas" not in convert_to_dist_str(final_state_4)


def test_features_solve_1(tmpdir, request):
    request.applymarker(
        pytest.mark.xfail(
            context.solver == "libmamba",
            reason="Features not supported in libmamba",
            strict=True,
        )
    )

    # in this test, channel-2 is a view of pkgs/free/linux-64
    #   and channel-4 is a view of the newer pkgs/main/linux-64
    # The channel list, equivalent to context.channels is ('channel-2', 'channel-4')
    specs = (MatchSpec("python=2.7"), MatchSpec("numpy"), MatchSpec("nomkl"))
    with env_var(
        "CONDA_CHANNEL_PRIORITY", "True", stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        with get_solver_aggregate_1(tmpdir, specs) as solver:
            final_state_1 = solver.solve_final_state()
            pprint(convert_to_dist_str(final_state_1))
            order = add_subdir_to_iter(
                (
                    "channel-2::nomkl-1.0-0",
                    "channel-2::libgfortran-3.0.0-1",
                    "channel-2::openssl-1.0.2l-0",
                    "channel-2::readline-6.2-2",
                    "channel-2::sqlite-3.13.0-0",
                    "channel-2::tk-8.5.18-0",
                    "channel-2::zlib-1.2.11-0",
                    "channel-2::openblas-0.2.19-0",
                    "channel-2::python-2.7.13-0",
                    "channel-2::numpy-1.13.1-py27_nomkl_0",
                )
            )
            assert convert_to_dist_str(final_state_1) == order

    with env_var(
        "CONDA_CHANNEL_PRIORITY", "False", stack_callback=conda_tests_ctxt_mgmt_def_pol
    ):
        with get_solver_aggregate_1(tmpdir, specs) as solver:
            final_state_1 = solver.solve_final_state()
            pprint(convert_to_dist_str(final_state_1))
            order = add_subdir_to_iter(
                (
                    "channel-4::blas-1.0-openblas",
                    "channel-4::ca-certificates-2018.03.07-0",
                    "channel-2::libffi-3.2.1-1",
                    "channel-4::libgcc-ng-8.2.0-hdf63c60_0",
                    "channel-4::libgfortran-ng-7.2.0-hdf63c60_3",
                    "channel-4::libstdcxx-ng-8.2.0-hdf63c60_0",
                    "channel-2::zlib-1.2.11-0",
                    "channel-4::libopenblas-0.2.20-h9ac9557_7",
                    "channel-4::ncurses-6.1-hf484d3e_0",
                    "channel-4::nomkl-3.0-0",
                    "channel-4::openssl-1.0.2p-h14c3975_0",
                    "channel-4::tk-8.6.7-hc745277_3",
                    "channel-4::libedit-3.1.20170329-h6b74fdf_2",
                    "channel-4::readline-7.0-ha6073c6_4",
                    "channel-4::sqlite-3.24.0-h84994c4_0",
                    "channel-4::python-2.7.15-h1571d57_0",
                    "channel-4::numpy-base-1.15.0-py27h7cdd4dd_0",
                    "channel-4::numpy-1.15.0-py27h2aefc1b_0",
                )
            )
            assert convert_to_dist_str(final_state_1) == order


@pytest.mark.integration  # this test is slower, so we'll lump it into integration
def test_freeze_deps_1(tmpdir):
    specs = (MatchSpec("six=1.7"),)
    with get_solver_2(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
        pprint(convert_to_dist_str(final_state_1))
        order = add_subdir_to_iter(
            (
                "channel-2::openssl-1.0.2l-0",
                "channel-2::readline-6.2-2",
                "channel-2::sqlite-3.13.0-0",
                "channel-2::tk-8.5.18-0",
                "channel-2::xz-5.2.3-0",
                "channel-2::zlib-1.2.11-0",
                "channel-2::python-3.4.5-0",
                "channel-2::six-1.7.3-py34_0",
            )
        )
        assert convert_to_dist_str(final_state_1) == order

    specs_to_add = (MatchSpec("bokeh"),)
    with get_solver_2(
        tmpdir, specs_to_add, prefix_records=final_state_1, history_specs=specs
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = ()
        link_order = add_subdir_to_iter(
            (
                "channel-2::mkl-2017.0.3-0",
                "channel-2::yaml-0.1.6-0",
                "channel-2::backports_abc-0.5-py34_0",
                "channel-2::markupsafe-1.0-py34_0",
                "channel-2::numpy-1.13.0-py34_0",
                "channel-2::pyyaml-3.12-py34_0",
                "channel-2::requests-2.14.2-py34_0",
                "channel-2::setuptools-27.2.0-py34_0",
                "channel-2::jinja2-2.9.6-py34_0",
                "channel-2::python-dateutil-2.6.1-py34_0",
                "channel-2::tornado-4.4.2-py34_0",
                "channel-2::bokeh-0.12.4-py34_0",
            )
        )
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order

    # now we can't install the latest bokeh 0.12.5, but instead we get bokeh 0.12.4
    specs_to_add = (MatchSpec("bokeh"),)
    with get_solver_2(
        tmpdir,
        specs_to_add,
        prefix_records=final_state_1,
        history_specs=(MatchSpec("six=1.7"), MatchSpec("python=3.4")),
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = ()
        link_order = add_subdir_to_iter(
            (
                "channel-2::mkl-2017.0.3-0",
                "channel-2::yaml-0.1.6-0",
                "channel-2::backports_abc-0.5-py34_0",
                "channel-2::markupsafe-1.0-py34_0",
                "channel-2::numpy-1.13.0-py34_0",
                "channel-2::pyyaml-3.12-py34_0",
                "channel-2::requests-2.14.2-py34_0",
                "channel-2::setuptools-27.2.0-py34_0",
                "channel-2::jinja2-2.9.6-py34_0",
                "channel-2::python-dateutil-2.6.1-py34_0",
                "channel-2::tornado-4.4.2-py34_0",
                "channel-2::bokeh-0.12.4-py34_0",
            )
        )
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order

    # here, the python=3.4 spec can't be satisfied, so it's dropped, and we go back to py27
    with pytest.raises(UnsatisfiableError):
        specs_to_add = (MatchSpec("bokeh=0.12.5"),)
        with get_solver_2(
            tmpdir,
            specs_to_add,
            prefix_records=final_state_1,
            history_specs=(MatchSpec("six=1.7"), MatchSpec("python=3.4")),
        ) as solver:
            unlink_precs, link_precs = solver.solve_for_diff()

    # adding the explicit python spec allows conda to change the python versions.
    # one possible outcome is that this updates to python 3.6.  That is not desirable because of the
    #    explicit "six=1.7" request in the history.  It should only neuter that spec if there's no way
    #    to solve it with that spec.
    specs_to_add = MatchSpec("bokeh=0.12.5"), MatchSpec("python")
    with get_solver_2(
        tmpdir,
        specs_to_add,
        prefix_records=final_state_1,
        history_specs=(MatchSpec("six=1.7"), MatchSpec("python=3.4")),
    ) as solver:
        unlink_precs, link_precs = solver.solve_for_diff()
        pprint(convert_to_dist_str(unlink_precs))
        pprint(convert_to_dist_str(link_precs))
        unlink_order = add_subdir_to_iter(
            (
                "channel-2::six-1.7.3-py34_0",
                "channel-2::python-3.4.5-0",
                # LIBMAMBA ADJUSTMENT
                # libmamba v1 doesn't remove xz in this solve
                *(
                    ()
                    if (
                        context.solver == "libmamba"
                        and VersionOrder(version("libmambapy")) < VersionOrder("2.0a0")
                    )
                    else ("channel-2::xz-5.2.3-0",)
                ),
            )
        )
        link_order = add_subdir_to_iter(
            (
                "channel-2::mkl-2017.0.3-0",
                "channel-2::yaml-0.1.6-0",
                "channel-2::python-2.7.13-0",
                "channel-2::backports-1.0-py27_0",
                "channel-2::backports_abc-0.5-py27_0",
                "channel-2::certifi-2016.2.28-py27_0",
                "channel-2::futures-3.1.1-py27_0",
                "channel-2::markupsafe-1.0-py27_0",
                "channel-2::numpy-1.13.1-py27_0",
                "channel-2::pyyaml-3.12-py27_0",
                "channel-2::requests-2.14.2-py27_0",
                "channel-2::six-1.7.3-py27_0",
                "channel-2::python-dateutil-2.6.1-py27_0",
                "channel-2::setuptools-36.4.0-py27_1",
                "channel-2::singledispatch-3.4.0.3-py27_0",
                "channel-2::ssl_match_hostname-3.5.0.1-py27_0",
                "channel-2::jinja2-2.9.6-py27_0",
                "channel-2::tornado-4.5.2-py27_0",
                "channel-2::bokeh-0.12.5-py27_1",
            )
        )
        assert convert_to_dist_str(unlink_precs) == unlink_order
        assert convert_to_dist_str(link_precs) == link_order

    # here, the python=3.4 spec can't be satisfied, so it's dropped, and we go back to py27
    specs_to_add = (MatchSpec("bokeh=0.12.5"),)
    with get_solver_2(
        tmpdir,
        specs_to_add,
        prefix_records=final_state_1,
        history_specs=(MatchSpec("six=1.7"), MatchSpec("python=3.4")),
    ) as solver:
        with pytest.raises(UnsatisfiableError):
            solver.solve_final_state(update_modifier=UpdateModifier.FREEZE_INSTALLED)


# class PrivateEnvTests(TestCase):

#     def setUp(self):
#         self.prefix = '/a/test/c/prefix'

#         self.preferred_env = "_spiffy-test-app_"
#         self.preferred_env_prefix = join(self.prefix, 'envs', self.preferred_env)

#         # self.save_path_conflict = os.environ.get('CONDA_PATH_CONFLICT')
#         self.saved_values = {}
#         self.saved_values['CONDA_ROOT_PREFIX'] = os.environ.get('CONDA_ROOT_PREFIX')
#         self.saved_values['CONDA_ENABLE_PRIVATE_ENVS'] = os.environ.get('CONDA_ENABLE_PRIVATE_ENVS')

#         # os.environ['CONDA_PATH_CONFLICT'] = 'prevent'
#         os.environ['CONDA_ROOT_PREFIX'] = self.prefix
#         os.environ['CONDA_ENABLE_PRIVATE_ENVS'] = 'true'

#         reset_context()

#     def tearDown(self):
#         for key, value in self.saved_values.items():
#             if value is not None:
#                 os.environ[key] = value
#             else:
#                 del os.environ[key]

#         reset_context()

# @patch.object(Context, 'prefix_specified')
# def test_simple_install_uninstall(self, prefix_specified):
#     prefix_specified.__get__ = Mock(return_value=False)
#
#     specs = MatchSpec("spiffy-test-app"),
#     with get_solver_3(specs) as solver:
#         final_state_1 = solver.solve_final_state()
#         # PrefixDag(final_state_1, specs).open_url()
#         print(convert_to_dist_str(final_state_1))
#         order = (
#             'channel-1::openssl-1.0.2l-0',
#             'channel-1::readline-6.2-2',
#             'channel-1::sqlite-3.13.0-0',
#             'channel-1::tk-8.5.18-0',
#             'channel-1::zlib-1.2.8-3',
#             'channel-1::python-2.7.13-0',
#             'channel-1::spiffy-test-app-2.0-py27hf99fac9_0',
#         )
#         assert tuple(final_state_1) == tuple(solver._index[Dist(d)] for d in order)
#
#     specs_to_add = MatchSpec("uses-spiffy-test-app"),
#     with get_solver_3(specs_to_add, prefix_records=final_state_1, history_specs=specs) as solver:
#         final_state_2 = solver.solve_final_state()
#         # PrefixDag(final_state_2, specs).open_url()
#         print(convert_to_dist_str(final_state_2))
#         order = (
#
#         )
#         assert tuple(final_state_2) == tuple(solver._index[Dist(d)] for d in order)
#
#     specs = specs + specs_to_add
#     specs_to_remove = MatchSpec("uses-spiffy-test-app"),
#     with get_solver_3(specs_to_remove=specs_to_remove, prefix_records=final_state_2,
#                       history_specs=specs) as solver:
#         final_state_3 = solver.solve_final_state()
#         # PrefixDag(final_state_2, specs).open_url()
#         print(convert_to_dist_str(final_state_3))
#         order = (
#
#         )
#         assert tuple(final_state_3) == tuple(solver._index[Dist(d)] for d in order)


def test_current_repodata_usage(tmpdir):
    config = {
        # force this to False, because otherwise tests
        # fail when run with old conda-build
        "CONDA_USE_ONLY_TAR_BZ2": False,
        # explicitly set env var so libmamba recognizes
        # the explicit setting (ignored otherwise)
        "CONDA_REPODATA_FNS": "current_repodata.json",
    }
    with env_vars(config, stack_callback=conda_tests_ctxt_mgmt_def_pol):
        solver = context.plugin_manager.get_solver_backend()(
            tmpdir.strpath,
            (Channel(CHANNEL_DIR_V1),),
            ("win-64",),
            specs_to_add=[MatchSpec("zlib")],
            repodata_fn="current_repodata.json",
        )
        final_state = solver.solve_final_state()
        # zlib 1.2.11, vc 14.1, vs2015_runtime, virtual package for vc track_feature
        assert final_state
        checked = False
        for prec in final_state:
            if prec.name == "zlib":
                assert prec.version == "1.2.11"
                assert prec.fn.endswith(".conda")
                checked = True
        if not checked:
            raise ValueError("Didn't have expected state in solve (needed zlib record)")


def test_current_repodata_fallback(tmpdir):
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir.strpath,
        (Channel(CHANNEL_DIR_V1),),
        ("win-64",),
        specs_to_add=[MatchSpec("zlib=1.2.8")],
    )
    final_state = solver.solve_final_state()
    # zlib 1.2.11, zlib 1.2.8, vc 14.1, vs2015_runtime, virtual package for vc track_feature
    assert final_state
    checked = False
    for prec in final_state:
        if prec.name == "zlib":
            assert prec.version == "1.2.8"
            assert prec.fn.endswith(".tar.bz2")
            checked = True
    if not checked:
        raise ValueError("Didn't have expected state in solve (needed zlib record)")


def test_downgrade_python_prevented_with_sane_message(tmpdir):
    specs = (MatchSpec("python=2.6"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()
    # PrefixDag(final_state_1, specs).open_url()
    pprint(convert_to_dist_str(final_state_1))
    order = add_subdir_to_iter(
        (
            "channel-1::openssl-1.0.1c-0",
            "channel-1::readline-6.2-0",
            "channel-1::sqlite-3.7.13-0",
            "channel-1::system-5.8-1",
            "channel-1::tk-8.5.13-0",
            "channel-1::zlib-1.2.7-0",
            "channel-1::python-2.6.8-6",
        )
    )
    assert convert_to_dist_str(final_state_1) == order

    # incompatible CLI and configured specs
    specs_to_add = (MatchSpec("scikit-learn==0.13"),)
    with get_solver(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        with pytest.raises(UnsatisfiableError) as exc:
            solver.solve_final_state()

        error_msg = str(exc.value).strip()
        # libmamba has a significantly more detailed (and different) error messages
        if context.solver == "classic":
            error_snippets = [
                "incompatible with the existing python installation in your environment:",
                "- scikit-learn==0.13 -> python=2.7",
                "Your python: python=2.6",
            ]
        elif context.solver == "libmamba":
            error_snippets = [
                "Encountered problems while solving",
                "Pins seem to be involved in the conflict. Currently pinned specs",
                r"python.*2\.6",
                r"scikit-learn.*0\.13",
            ]

        for snippet in error_snippets:
            assert re.search(snippet, error_msg)

    specs_to_add = (MatchSpec("unsatisfiable-with-py26"),)
    with get_solver(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        with pytest.raises(UnsatisfiableError) as exc:
            solver.solve_final_state()
        error_msg = str(exc.value).strip()
        # libmamba has a significantly more detailed (and different) error messages
        if context.solver == "classic":
            error_snippets = [
                "incompatible with the existing python installation in your environment:",
                "- unsatisfiable-with-py26 -> python=2.7",
                "Your python: python=2.6",
            ]
        elif context.solver == "libmamba":
            error_snippets = [
                "Encountered problems while solving",
                "Pins seem to be involved in the conflict. Currently pinned specs",
                r"python.*2\.6",
                "unsatisfiable-with-py26",
            ]

        for snippet in error_snippets:
            assert re.search(snippet, error_msg)


fake_index = [
    PrefixRecord(
        package_type=PackageType.NOARCH_GENERIC,
        name="mypkg",
        version="0.1.1",
        channel="test",
        subdir="conda-test",
        fn="mypkg-0.1.1",
        build="pypi_0",
        build_number=1,
        paths_data=None,
        files=None,
        depends=[],
        constrains=[],
    ),
    PrefixRecord(
        package_type=PackageType.NOARCH_GENERIC,
        name="mypkg",
        version="0.1.0",
        channel="test",
        subdir="conda-test",
        fn="mypkg-0.1.1",
        build="pypi_0",
        build_number=1,
        paths_data=None,
        files=None,
        depends=[],
        constrains=[],
    ),
    PrefixRecord(
        package_type=PackageType.NOARCH_GENERIC,
        name="mypkgnot",
        version="1.1.1",
        channel="test",
        subdir="conda-test",
        fn="mypkgnot-1.1.1",
        build="pypi_0",
        build_number=1,
        paths_data=None,
        files=None,
        depends=["mypkg 0.1.0"],
        constrains=[],
    ),
]


def test_packages_in_solution_change_already_newest(tmpdir):
    specs = MatchSpec("mypkg")
    pre_packages = {"mypkg": [("mypkg", "0.1.1")]}
    post_packages = {"mypkg": [("mypkg", "0.1.1")]}
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[specs]
    )
    constrained = solver.get_constrained_packages(
        pre_packages, post_packages, fake_index
    )
    assert len(constrained) == 0


def test_packages_in_solution_change_needs_update(tmpdir):
    specs = MatchSpec("mypkg")
    pre_packages = {"mypkg": [("mypkg", "0.1.0")]}
    post_packages = {"mypkg": [("mypkg", "0.1.1")]}
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[specs]
    )
    constrained = solver.get_constrained_packages(
        pre_packages, post_packages, fake_index
    )
    assert len(constrained) == 0


def test_packages_in_solution_change_constrained(tmpdir):
    specs = MatchSpec("mypkg")
    pre_packages = {"mypkg": [("mypkg", "0.1.0")]}
    post_packages = {"mypkg": [("mypkg", "0.1.0")]}
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[specs]
    )
    constrained = solver.get_constrained_packages(
        pre_packages, post_packages, fake_index
    )
    assert len(constrained) == 1


def test_determine_constricting_specs_conflicts(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.0",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg 0.1.0"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert any(i for i in constricting if i[0] == "mypkgnot")


def test_determine_constricting_specs_conflicts_upperbound(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg <=0.1.1"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert any(i for i in constricting if i[0] == "mypkgnot")


def test_determine_constricting_specs_multi_conflicts(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg <=0.1.1"],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="notmypkg",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg 0.1.1"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert any(i for i in constricting if i[0] == "mypkgnot")
    assert any(i for i in constricting if i[0] == "notmypkg")


def test_determine_constricting_specs_no_conflicts_upperbound_compound_depends(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg >=0.1.1,<0.2.1"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert constricting is None


def test_determine_constricting_specs_no_conflicts_version_star(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg 0.1.*"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert constricting is None


def test_determine_constricting_specs_no_conflicts_free(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert constricting is None


def test_determine_constricting_specs_no_conflicts_no_upperbound(tmpdir):
    solution_prec = [
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkg",
            version="0.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkg-0.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=[],
            constrains=[],
        ),
        PrefixRecord(
            package_type=PackageType.NOARCH_GENERIC,
            name="mypkgnot",
            version="1.1.1",
            channel="test",
            subdir="conda-test",
            fn="mypkgnot-1.1.1",
            build="pypi_0",
            build_number=1,
            paths_data=None,
            files=None,
            depends=["mypkg >=0.0.5"],
            constrains=[],
        ),
    ]
    spec = MatchSpec("mypkg")
    solver = context.plugin_manager.get_solver_backend()(
        tmpdir, (Channel(CHANNEL_DIR_V1),), ("linux-64",), specs_to_add=[spec]
    )
    constricting = solver.determine_constricting_specs(spec, solution_prec)
    assert constricting is None


@pytest.mark.integration
def test_indirect_dep_optimized_by_version_over_package_count(tmpdir):
    """We need to adjust the Anaconda metapackage - custom version - so that it keeps
    dependencies on all of its components.  That custom package is intended to free up constraints.  However,
    We learned the hard way that the changes in conda 4.7 can end up removing all components, because
    anaconda-custom doesn't actually depend on them.

    So, here we go: a new metapackage that we hotfix anaconda-custom to depend on.  This new metapackage
    is versioned similarly to the real anaconda metapackages, for the sake of accounting for
    different package collections in different anaconda versions.  This test is for the case where a newer
    version of that new metapackage has fewer deps (but newer version).  We want it to prefer the newer
    version.
    """
    specs = (MatchSpec("anaconda=1.4"),)
    with get_solver(tmpdir, specs) as solver:
        final_state_1 = solver.solve_final_state()

    # start out with the oldest anaconda.  Using that state, free up zeromq.  Doing this should result in
    #    two things:
    #    * the anaconda metapackage goes to the "custom" version
    #    * zeromq goes up one build number, from 0 to 1.
    #    * all the other packages from the original anaconda metapackage get removed.
    #      Only the packages from the _dummy_anaconda_impl remain, but they are new.
    # This does NOT work if you omit the anaconda matchspec here.  It is part of the history,
    #     and it must be supplied as an explicit spec to override that history.
    specs_to_add = MatchSpec("zeromq"), MatchSpec("anaconda")
    with get_solver(
        tmpdir,
        specs_to_add=specs_to_add,
        prefix_records=final_state_1,
        history_specs=specs,
    ) as solver:
        final_state = solver.solve_final_state()

        # anaconda, _dummy_anaconda_impl, zeromq.  NOT bzip2
        #    note that bzip2 is extra - it would be one less removal - that's the big test here.
        #    bzip2 is part of the older _dummy_anaconda_impl

        for prec in final_state:
            if prec.name == "anaconda":
                assert prec.version == "1.5.0"
            elif prec.name == "zeromq":
                assert prec.build_number == 1
            elif prec.name == "_dummy_anaconda_impl":
                assert prec.version == "2.0"
