Repository: cookiecutter/cookiecutter

Issue:
Enforce the minimal coverage to 100%



Discussion:

Resolving patch:
diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py
--- a/cookiecutter/utils.py
+++ b/cookiecutter/utils.py
@@ -16,7 +16,7 @@ def force_delete(func, path, exc_info):
     """Error handler for `shutil.rmtree()` equivalent to `rm -rf`.
 
     Usage: `shutil.rmtree(path, onerror=force_delete)`
-    From stackoverflow.com/questions/1889597
+    From https://docs.python.org/3/library/shutil.html#rmtree-example
     """
     os.chmod(path, stat.S_IWRITE)
     func(path)


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -2,7 +2,6 @@
 
 import json
 import os
-import sys
 
 import pytest
 from click.testing import CliRunner
@@ -365,10 +364,6 @@ def test_default_user_config(mocker, cli_runner):
     )
 
 
-@pytest.mark.skipif(
-    sys.version_info[0] == 3 and sys.version_info[1] == 6 and sys.version_info[2] == 1,
-    reason="Outdated pypy3 version on Travis CI/CD with wrong OrderedDict syntax.",
-)
 def test_echo_undefined_variable_error(tmpdir, cli_runner):
     """Cli invocation return error if variable undefined in template."""
     output_dir = str(tmpdir.mkdir('output'))
diff --git a/tests/test_hooks.py b/tests/test_hooks.py
--- a/tests/test_hooks.py
+++ b/tests/test_hooks.py
@@ -1,5 +1,6 @@
 """Tests for `cookiecutter.hooks` module."""
 import os
+import errno
 import stat
 import sys
 import textwrap
@@ -144,6 +145,31 @@ def test_run_script(self):
         hooks.run_script(os.path.join(self.hooks_path, self.post_hook))
         assert os.path.isfile('shell_post.txt')
 
+    def test_run_failing_script(self, mocker):
+        """Test correct exception raise if run_script fails."""
+
+        err = OSError()
+
+        prompt = mocker.patch('subprocess.Popen')
+        prompt.side_effect = err
+
+        with pytest.raises(exceptions.FailedHookException) as excinfo:
+            hooks.run_script(os.path.join(self.hooks_path, self.post_hook))
+        assert 'Hook script failed (error: {})'.format(err) in str(excinfo.value)
+
+    def test_run_failing_script_enoexec(self, mocker):
+        """Test correct exception raise if run_script fails."""
+
+        err = OSError()
+        err.errno = errno.ENOEXEC
+
+        prompt = mocker.patch('subprocess.Popen')
+        prompt.side_effect = err
+
+        with pytest.raises(exceptions.FailedHookException) as excinfo:
+            hooks.run_script(os.path.join(self.hooks_path, self.post_hook))
+        assert 'Hook script failed, might be an empty file or missing a shebang' in str(excinfo.value)
+
     def test_run_script_cwd(self):
         """Change directory before running hook."""
         hooks.run_script(os.path.join(self.hooks_path, self.post_hook), 'tests')
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -14,10 +14,23 @@ def make_readonly(path):
     Path.chmod(path, mode & ~stat.S_IWRITE)
 
 
-@pytest.mark.skipif(
-    sys.version_info[0] == 3 and sys.version_info[1] == 6 and sys.version_info[2] == 1,
-    reason="Outdated pypy3 version on Travis CI/CD",
-)
+def test_force_delete(mocker, tmp_path):
+    """Verify `utils.force_delete` makes files writable."""
+    ro_file = Path(tmp_path, 'bar')
+
+    with open(ro_file, "w") as f:
+        f.write("Test data")
+    make_readonly(ro_file)
+
+    rmtree = mocker.Mock()
+    utils.force_delete(rmtree, ro_file, sys.exc_info())
+
+    assert (ro_file.stat().st_mode & stat.S_IWRITE) == stat.S_IWRITE
+    rmtree.assert_called_once_with(ro_file)
+
+    utils.rmtree(tmp_path)
+
+
 def test_rmtree(tmp_path):
     """Verify `utils.rmtree` remove files marked as read-only."""
     with open(Path(tmp_path, 'bar'), "w") as f:
@@ -29,10 +42,6 @@ def test_rmtree(tmp_path):
     assert not Path(tmp_path).exists()
 
 
-@pytest.mark.skipif(
-    sys.version_info[0] == 3 and sys.version_info[1] == 6 and sys.version_info[2] == 1,
-    reason="Outdated pypy3 version on Travis CI/CD",
-)
 def test_make_sure_path_exists(tmp_path):
     """Verify correct True/False response from `utils.make_sure_path_exists`.
 
@@ -68,10 +77,6 @@ def raiser(*args, **kwargs):
     assert not utils.make_sure_path_exists(uncreatable_directory)
 
 
-@pytest.mark.skipif(
-    sys.version_info[0] == 3 and sys.version_info[1] == 6 and sys.version_info[2] == 1,
-    reason="Outdated pypy3 version on Travis CI/CD",
-)
 def test_work_in(tmp_path):
     """Verify returning to original folder after `utils.work_in` use."""
     cwd = Path.cwd()