Completed
Pull Request — master (#980)
by
unknown
43s
created

test_imports_in_hooks()   A

Complexity

Conditions 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
1
# -*- coding: utf-8 -*-
2
3
"""
4
test_generate_hooks
5
-------------------
6
7
Tests formerly known from a unittest residing in test_generate.py named
8
TestHooks.test_ignore_hooks_dirs
9
TestHooks.test_run_python_hooks
10
TestHooks.test_run_python_hooks_cwd
11
TestHooks.test_run_shell_hooks
12
"""
13
14
from __future__ import unicode_literals
15
import errno
16
import os
17
import sys
18
import stat
19
import pytest
20
21
from cookiecutter import generate
22
from cookiecutter import utils
23
from cookiecutter.exceptions import FailedHookException
24
25
WINDOWS = sys.platform.startswith('win')
26
27
28
@pytest.fixture(scope='function')
29
def remove_additional_folders(request):
30
    """
31
    Remove some special folders which are created by the tests.
32
    """
33
    def fin_remove_additional_folders():
34
        directories_to_delete = [
35
            'tests/test-pyhooks/inputpyhooks',
36
            'inputpyhooks',
37
            'inputhooks',
38
            'tests/test-shellhooks',
39
            'tests/test-hooks',
40
            'tests/test-hook-imports/inputpyhooks'
41
        ]
42
        for directory in directories_to_delete:
43
            if os.path.exists(directory):
44
                utils.rmtree(directory)
45
    request.addfinalizer(fin_remove_additional_folders)
46
47
48
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
49
def test_ignore_hooks_dirs():
50
    generate.generate_files(
51
        context={
52
            'cookiecutter': {'pyhooks': 'pyhooks'}
53
        },
54
        repo_dir='tests/test-pyhooks/',
55
        output_dir='tests/test-pyhooks/'
56
    )
57
    assert not os.path.exists('tests/test-pyhooks/inputpyhooks/hooks')
58
59
60
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
61
def test_imports_in_hooks():
62
    try:
63
        generate.generate_files(
64
            context={
65
                'cookiecutter': {'pyhooks': 'pyhooks'}
66
            },
67
            repo_dir='tests/test-hook-imports/',
68
            output_dir='tests/test-hook-imports/')
69
    except FailedHookException:
70
        pytest.fail("Unexpected FailedHookException...")
71
72
73
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
74
def test_run_python_hooks():
75
    generate.generate_files(
76
        context={
77
            'cookiecutter': {'pyhooks': 'pyhooks'}
78
        },
79
        repo_dir='tests/test-pyhooks/'.replace("/", os.sep),
80
        output_dir='tests/test-pyhooks/'.replace("/", os.sep)
81
    )
82
    assert os.path.exists('tests/test-pyhooks/inputpyhooks/python_pre.txt')
83
    assert os.path.exists('tests/test-pyhooks/inputpyhooks/python_post.txt')
84
85
86
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
87
def test_run_python_hooks_cwd():
88
    generate.generate_files(
89
        context={
90
            'cookiecutter': {'pyhooks': 'pyhooks'}
91
        },
92
        repo_dir='tests/test-pyhooks/'
93
    )
94
    assert os.path.exists('inputpyhooks/python_pre.txt')
95
    assert os.path.exists('inputpyhooks/python_post.txt')
96
97
98
@pytest.mark.skipif(WINDOWS, reason='OSError.errno=8 is not thrown on Windows')
99
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
100
def test_empty_hooks():
101
    # OSError.errno=8 is not thrown on Windows when the script is empty
102
    # because it always runs through shell instead of needing a shebang.
103
    with pytest.raises(FailedHookException) as excinfo:
104
        generate.generate_files(
105
            context={
106
                'cookiecutter': {'shellhooks': 'shellhooks'}
107
            },
108
            repo_dir='tests/test-shellhooks-empty/',
109
            overwrite_if_exists=True
110
        )
111
    assert 'shebang' in str(excinfo.value)
112
113
114
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
115
def test_oserror_hooks(mocker):
116
117
    message = 'Out of memory'
118
119
    err = OSError(message)
120
    err.errno = errno.ENOMEM
121
122 View Code Duplication
    prompt = mocker.patch('subprocess.Popen')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
123
    prompt.side_effect = err
124
125
    with pytest.raises(FailedHookException) as excinfo:
126
        generate.generate_files(
127
            context={
128
                'cookiecutter': {'shellhooks': 'shellhooks'}
129
            },
130
            repo_dir='tests/test-shellhooks-empty/',
131
            overwrite_if_exists=True
132
        )
133
    assert message in str(excinfo.value)
134
135
136
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
137
def test_popen_includes_hooks_in_python_path(mocker):
138
    err = OSError()
139
    prompt = mocker.patch('subprocess.Popen')
140
    prompt.side_effect = err
141
142
    original_env = os.environ.copy()
143
    original_python_path = original_env.get("PYTHONPATH", "")
144
145
    with pytest.raises(FailedHookException):
146
        generate.generate_files(
147
            context={
148
                'cookiecutter': {'pyhooks': 'pyhooks'}
149
            },
150
            repo_dir='tests/test-hook-imports/',
151
            output_dir='tests/test-hook-imports/')
152 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
153
    hooks_env = prompt.call_args[1]["env"]
154
    hooks_env_python_path = hooks_env["PYTHONPATH"]
155
156
    assert original_python_path in hooks_env_python_path
157
    assert os.path.join("tests",
158
                        "test-hook-imports",
159
                        "hooks") in hooks_env_python_path
160
161
162
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
163
def test_run_failing_hook_removes_output_directory():
164
    repo_path = os.path.abspath('tests/test-hooks/')
165
    hooks_path = os.path.abspath('tests/test-hooks/hooks')
166
167
    hook_dir = os.path.join(repo_path, 'hooks')
168
    template = os.path.join(repo_path, 'input{{cookiecutter.hooks}}')
169
    os.mkdir(repo_path)
170
    os.mkdir(hook_dir)
171
    os.mkdir(template)
172
173
    hook_path = os.path.join(hooks_path, 'pre_gen_project.py')
174
175
    with open(hook_path, 'w') as f:
176
        f.write("#!/usr/bin/env python\n")
177
        f.write("import sys; sys.exit(1)\n")
178
179
    with pytest.raises(FailedHookException) as excinfo:
180
        generate.generate_files(
181
            context={
182
                'cookiecutter': {'hooks': 'hooks'}
183
            },
184
            repo_dir='tests/test-hooks/',
185
            overwrite_if_exists=True
186
        )
187
188
    assert 'Hook script failed' in str(excinfo.value)
189
    assert not os.path.exists('inputhooks')
190
191
192
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
193
def test_run_failing_hook_preserves_existing_output_directory():
194
    repo_path = os.path.abspath('tests/test-hooks/')
195
    hooks_path = os.path.abspath('tests/test-hooks/hooks')
196
197
    hook_dir = os.path.join(repo_path, 'hooks')
198
    template = os.path.join(repo_path, 'input{{cookiecutter.hooks}}')
199
    os.mkdir(repo_path)
200
    os.mkdir(hook_dir)
201
    os.mkdir(template)
202
203
    hook_path = os.path.join(hooks_path, 'pre_gen_project.py')
204
205
    with open(hook_path, 'w') as f:
206
        f.write("#!/usr/bin/env python\n")
207
        f.write("import sys; sys.exit(1)\n")
208
209
    os.mkdir('inputhooks')
210
    with pytest.raises(FailedHookException) as excinfo:
211
        generate.generate_files(
212
            context={
213
                'cookiecutter': {'hooks': 'hooks'}
214
            },
215
            repo_dir='tests/test-hooks/',
216
            overwrite_if_exists=True
217
        )
218
219
    assert 'Hook script failed' in str(excinfo.value)
220
    assert os.path.exists('inputhooks')
221
222
223
def make_test_repo(name):
224
    hooks = os.path.join(name, 'hooks')
225
    template = os.path.join(name, 'input{{cookiecutter.shellhooks}}')
226
    os.mkdir(name)
227
    os.mkdir(hooks)
228
    os.mkdir(template)
229
230
    with open(os.path.join(template, 'README.rst'), 'w') as f:
231
        f.write("foo\n===\n\nbar\n")
232
233
    if sys.platform.startswith('win'):
234
        filename = os.path.join(hooks, 'pre_gen_project.bat')
235
        with open(filename, 'w') as f:
236
            f.write("@echo off\n")
237
            f.write("\n")
238
            f.write("echo pre generation hook\n")
239
            f.write("echo. >shell_pre.txt\n")
240
241
        filename = os.path.join(hooks, 'post_gen_project.bat')
242
        with open(filename, 'w') as f:
243
            f.write("@echo off\n")
244
            f.write("\n")
245
            f.write("echo post generation hook\n")
246
            f.write("echo. >shell_post.txt\n")
247
    else:
248
        filename = os.path.join(hooks, 'pre_gen_project.sh')
249
        with open(filename, 'w') as f:
250
            f.write("#!/bin/bash\n")
251
            f.write("\n")
252
            f.write("echo 'pre generation hook';\n")
253
            f.write("touch 'shell_pre.txt'\n")
254
        # Set the execute bit
255
        os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR)
256
257
        filename = os.path.join(hooks, 'post_gen_project.sh')
258
        with open(filename, 'w') as f:
259
            f.write("#!/bin/bash\n")
260
            f.write("\n")
261
            f.write("echo 'post generation hook';\n")
262
            f.write("touch 'shell_post.txt'\n")
263
        # Set the execute bit
264
        os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR)
265
266
267
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
268
def test_run_shell_hooks():
269
    make_test_repo('tests/test-shellhooks')
270
    generate.generate_files(
271
        context={
272
            'cookiecutter': {'shellhooks': 'shellhooks'}
273
        },
274
        repo_dir='tests/test-shellhooks/',
275
        output_dir='tests/test-shellhooks/'
276
    )
277
    shell_pre_file = 'tests/test-shellhooks/inputshellhooks/shell_pre.txt'
278
    shell_post_file = 'tests/test-shellhooks/inputshellhooks/shell_post.txt'
279
    assert os.path.exists(shell_pre_file)
280
    assert os.path.exists(shell_post_file)
281