Repository: spyder-ide/spyder

Issue:
Error when browsing git repository
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->


I'm currently working on a Git repository, and when I clicked on the "browse repository" option from the right-click drop down menu, this error popped up.

### Traceback
```python-traceback
  File "C:\Users\manma\Anaconda3\lib\site-packages\spyder\widgets\explorer.py", line 395, in <lambda>
    self.vcs_command(fnames, 'browse')
  File "C:\Users\manma\Anaconda3\lib\site-packages\spyder\widgets\explorer.py", line 825, in vcs_command
    for path in sorted(fnames):
TypeError: 'bool' object is not iterable
```

## Versions

* Spyder version: 3.3.1 
* Python version: 3.6.6
* Qt version: 5.9.6
* PyQt5 version: 5.9.2
* Operating System: Windows 10

### Dependencies

```
pyflakes >=0.6.0  :  2.0.0 (OK)
pycodestyle >=2.3 :  2.4.0 (OK)
pygments >=2.0    :  2.2.0 (OK)
sphinx >=0.6.6    :  1.7.6 (OK)
rope >=0.9.4      :  0.11.0 (OK)
jedi >=0.9.0      :  0.12.1 (OK)
nbconvert >=4.0   :  5.3.1 (OK)
pandas >=0.13.1   :  0.23.4 (OK)
numpy >=1.7       :  1.15.0 (OK)
sympy >=0.7.3     :  1.2 (OK)
cython >=0.21     :  0.28.5 (OK)
qtconsole >=4.2.0 :  4.4.0 (OK)
IPython >=4.0     :  6.5.0 (OK)
matplotlib >=2.0.0:  2.2.3 (OK)
pylint >=0.25     :  2.1.1 (OK)
```



Discussion:
Thanks for reporting. We'll fix this error in our next version.

Resolving patch:
diff --git a/spyder/utils/vcs.py b/spyder/utils/vcs.py
--- a/spyder/utils/vcs.py
+++ b/spyder/utils/vcs.py
@@ -8,11 +8,13 @@
 
 from __future__ import print_function
 
-import sys
+import os
 import os.path as osp
 import subprocess
+import sys
 
 # Local imports
+from spyder.config.base import running_under_pytest
 from spyder.utils import programs
 from spyder.utils.misc import abspardir
 from spyder.py3compat import PY3
@@ -31,7 +33,7 @@
     'name': 'Git',
     'rootdir': '.git',
     'actions': dict(
-        commit=( ('git', ['gui']), ),
+        commit=( ('git', ['gui' if os.name == 'nt' else 'cola']), ),
         browse=( ('gitk', []), ))
 }]
 
@@ -81,7 +83,10 @@ def run_vcs_tool(path, action):
     tools = info['actions'][action]
     for tool, args in tools:
         if programs.find_program(tool):
-            programs.run_program(tool, args, cwd=path)
+            if not running_under_pytest():
+                programs.run_program(tool, args, cwd=path)
+            else:
+                return True
             return
     else:
         cmdnames = [name for name, args in tools]
@@ -142,11 +147,3 @@ def get_git_revision(repopath):
         return commit, branch
     except (subprocess.CalledProcessError, AssertionError, AttributeError):
         return None, None
-
-
-if __name__ == '__main__':
-    print(get_vcs_root(osp.dirname(__file__)))  # spyder: test-skip
-    print(get_vcs_root(r'D:\Python\ipython\IPython\kernel'))  # spyder: test-skip
-    #run_vcs_tool(r'D:\Python\userconfig\userconfig', 'commit')
-    print(get_git_revision(osp.dirname(__file__)+"/../.."))  # spyder: test-skip
-    print(get_git_revision('/'))  # spyder: test-skip
diff --git a/spyder/widgets/explorer.py b/spyder/widgets/explorer.py
--- a/spyder/widgets/explorer.py
+++ b/spyder/widgets/explorer.py
@@ -388,11 +388,8 @@ def create_file_manage_actions(self, fnames):
         # related actions only when a single file/folder is selected:
         dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0])
         if len(fnames) == 1 and vcs.is_vcs_repository(dirname):
-            # QAction.triggered works differently for PySide and PyQt
-            commit_slot = lambda fnames=[dirname]:\
-                                self.vcs_command(fnames, 'commit')
-            browse_slot = lambda fnames=[dirname]:\
-                                self.vcs_command(fnames, 'browse')
+            commit_slot = lambda : self.vcs_command([dirname], 'commit')
+            browse_slot = lambda : self.vcs_command([dirname], 'browse')
             vcs_ci = create_action(self, _("Commit"),
                                    icon=ima.icon('vcs_commit'),
                                    triggered=commit_slot)


Test patch:
diff --git a/spyder/utils/tests/test_vcs.py b/spyder/utils/tests/test_vcs.py
new file mode 100644
--- /dev/null
+++ b/spyder/utils/tests/test_vcs.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © Spyder Project Contributors
+# Licensed under the terms of the MIT License
+#
+
+"""
+Tests for vcs.py
+"""
+
+# Standard library imports
+import os
+import os.path as osp
+import sys
+
+# Test library imports
+import pytest
+
+# Local imports
+from spyder.utils.vcs import (ActionToolNotFound, get_git_revision,
+                              get_vcs_root, run_vcs_tool)
+
+
+@pytest.mark.skipif(os.environ.get('CI', None) is None,
+                    reason="Not to be run outside of CIs")
+def test_vcs_tool():
+    if sys.platform.startswith('linux'):
+        with pytest.raises(ActionToolNotFound):
+            run_vcs_tool(osp.dirname(__file__), 'browse')
+    else:
+        assert run_vcs_tool(osp.dirname(__file__), 'browse')
+        assert run_vcs_tool(osp.dirname(__file__), 'commit')
+
+
+def test_vcs_root(tmpdir):
+    directory = tmpdir.mkdir('foo')
+    assert get_vcs_root(str(directory)) == None
+    assert get_vcs_root(osp.dirname(__file__)) != None
+
+
+def test_git_revision():
+    root = get_vcs_root(osp.dirname(__file__))
+    assert get_git_revision(osp.dirname(__file__)) == (None, None)
+    assert all([isinstance(x, str) for x in get_git_revision(root)])
+
+
+if __name__ == "__main__":
+    pytest.main()