Repository: tox-dev/tox
Pull request #1155 (2019-02-01 20:22:56)

-eALL should also include environment from envlist
According to the `--help` output, `-eALL` should work against specified
environments (ALL selects all). However, the environments from the
envlist key were ignored if they were not associated with a section.

For instance, with:
```
[tox]
envlist = py{a,b}
```

No environment was selected, and the default `python` was used instead.
With this patch, `tox` with test both `a` and `b`.


Patch:
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 5d7eae23..b53bb1bf 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -24,6 +24,7 @@ Eugene Yunak
 Fernando L. Pereira
 Florian Preinstorfer
 Florian Schulze
+Gonéri Le Bouder
 Hazal Ozturk
 Henk-Jaap Wagenaar
 Ian Stapleton Cordasco
diff --git a/docs/changelog/1155.bugix.rst b/docs/changelog/1155.bugix.rst
new file mode 100644
index 00000000..c9f208d7
--- /dev/null
+++ b/docs/changelog/1155.bugix.rst
@@ -0,0 +1,1 @@
+The ``-eALL`` command line argument now expands the ``envlist`` key and includes all its environment.
diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py
index bf4252e3..69f47cdb 100644
--- a/src/tox/config/__init__.py
+++ b/src/tox/config/__init__.py
@@ -1155,39 +1155,54 @@ class ParseIni(object):
                 reader.addsubstitutions(**{env_attr.name: res})
         return tc
 
-    def _getenvdata(self, reader, config):
-        candidates = (
-            os.environ.get(PARALLEL_ENV_VAR_KEY),
-            self.config.option.env,
-            os.environ.get("TOXENV"),
-            reader.getstring("envlist", replace=False),
-        )
-        env_str = next((i for i in candidates if i), [])
+    def _getallenvs(self, reader, extra_env_list=None):
+        extra_env_list = extra_env_list or []
+        env_str = reader.getstring("envlist", replace=False)
         env_list = _split_env(env_str)
+        for env in extra_env_list:
+            if env not in env_list:
+                env_list.append(env)
 
-        # collect section envs
         all_envs = OrderedDict((i, None) for i in env_list)
-        if "ALL" in all_envs:
-            all_envs.pop("ALL")
         for section in self._cfg:
             if section.name.startswith(testenvprefix):
                 all_envs[section.name[len(testenvprefix) :]] = None
         if not all_envs:
             all_envs["python"] = None
+        return list(all_envs.keys())
+
+    def _getenvdata(self, reader, config):
+        from_option = self.config.option.env
+        from_environ = os.environ.get("TOXENV")
+        from_config = reader.getstring("envlist", replace=False)
+
+        env_list = []
+        if (from_option and "ALL" in from_option) or (
+            not from_option and from_environ and "ALL" in from_environ.split(",")
+        ):
+            all_envs = self._getallenvs(reader)
+        else:
+            candidates = (
+                os.environ.get(PARALLEL_ENV_VAR_KEY),
+                from_option,
+                from_environ,
+                from_config,
+            )
+            env_str = next((i for i in candidates if i), [])
+            env_list = _split_env(env_str)
+            all_envs = self._getallenvs(reader, env_list)
+
+        if not env_list:
+            env_list = all_envs
 
         package_env = config.isolated_build_env
         if config.isolated_build is True and package_env in all_envs:
-            all_envs.pop(package_env)
-
-        if not env_list or "ALL" in env_list:
-            env_list = list(all_envs.keys())
+            all_envs.remove(package_env)
 
         if config.isolated_build is True and package_env in env_list:
             msg = "isolated_build_env {} cannot be part of envlist".format(package_env)
             raise tox.exception.ConfigError(msg)
-
-        all_env_list = list(all_envs.keys())
-        return env_list, all_env_list
+        return env_list, all_envs
 
 
 def _split_env(env):


Test patch:
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index 00524bc4..20913a99 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -2065,7 +2065,7 @@ class TestGlobalOptions:
         config = newconfig([], "")
         assert not config.sdistsrc
 
-    def test_env_selection(self, newconfig, monkeypatch):
+    def test_env_selection_with_section_name(self, newconfig, monkeypatch):
         inisource = """
             [tox]
             envlist = py36
@@ -2091,6 +2091,18 @@ class TestGlobalOptions:
         config = newconfig(["-espam"], inisource)
         assert config.envlist == ["spam"]
 
+    def test_env_selection_expanded_envlist(self, newconfig, monkeypatch):
+        inisource = """
+            [tox]
+            envlist = py{36,35,27}
+            [testenv:py36]
+            basepython=python3.6
+        """
+        config = newconfig([], inisource)
+        assert config.envlist == ["py36", "py35", "py27"]
+        config = newconfig(["-eALL"], inisource)
+        assert config.envlist == ["py36", "py35", "py27"]
+
     def test_py_venv(self, newconfig):
         config = newconfig(["-epy"], "")
         env = config.envconfigs["py"]