Completed
Pull Request — master (#980)
by
unknown
34s
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
    repo_path = os.path.abspath('tests/test-hook-imports/')
139
    hook_dir = os.path.join(repo_path, 'hooks')
140
141
    err = OSError()
142
    prompt = mocker.patch('subprocess.Popen')
143
    prompt.side_effect = err
144
145
    original_env = os.environ.copy()
146
    original_python_path = original_env.get('PYTHONPATH', '')
147
148
    with pytest.raises(FailedHookException):
149
        generate.generate_files(
150
            context={
151
                'cookiecutter': {'pyhooks': 'pyhooks'}
152 View Code Duplication
            },
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
153
            repo_dir='tests/test-hook-imports/',
154
            output_dir='tests/test-hook-imports/')
155
156
    hooks_env = prompt.call_args[1]['env']
157
    hooks_env_python_path = hooks_env['PYTHONPATH']
158
159
    assert original_python_path in hooks_env_python_path
160
    assert hook_dir in hooks_env_python_path
161
162
163
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
164
def test_run_failing_hook_removes_output_directory():
165
    repo_path = os.path.abspath('tests/test-hooks/')
166
    hooks_path = os.path.abspath('tests/test-hooks/hooks')
167
168
    hook_dir = os.path.join(repo_path, 'hooks')
169
    template = os.path.join(repo_path, 'input{{cookiecutter.hooks}}')
170
    os.mkdir(repo_path)
171
    os.mkdir(hook_dir)
172
    os.mkdir(template)
173
174
    hook_path = os.path.join(hooks_path, 'pre_gen_project.py')
175
176
    with open(hook_path, 'w') as f:
177
        f.write("#!/usr/bin/env python\n")
178
        f.write("import sys; sys.exit(1)\n")
179
180
    with pytest.raises(FailedHookException) as excinfo:
181
        generate.generate_files(
182
            context={
183
                'cookiecutter': {'hooks': 'hooks'}
184
            },
185
            repo_dir='tests/test-hooks/',
186
            overwrite_if_exists=True
187
        )
188
189
    assert 'Hook script failed' in str(excinfo.value)
190
    assert not os.path.exists('inputhooks')
191
192
193
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
194
def test_run_failing_hook_preserves_existing_output_directory():
195
    repo_path = os.path.abspath('tests/test-hooks/')
196
    hooks_path = os.path.abspath('tests/test-hooks/hooks')
197
198
    hook_dir = os.path.join(repo_path, 'hooks')
199
    template = os.path.join(repo_path, 'input{{cookiecutter.hooks}}')
200
    os.mkdir(repo_path)
201
    os.mkdir(hook_dir)
202
    os.mkdir(template)
203
204
    hook_path = os.path.join(hooks_path, 'pre_gen_project.py')
205
206
    with open(hook_path, 'w') as f:
207
        f.write("#!/usr/bin/env python\n")
208
        f.write("import sys; sys.exit(1)\n")
209
210
    os.mkdir('inputhooks')
211
    with pytest.raises(FailedHookException) as excinfo:
212
        generate.generate_files(
213
            context={
214
                'cookiecutter': {'hooks': 'hooks'}
215
            },
216
            repo_dir='tests/test-hooks/',
217
            overwrite_if_exists=True
218
        )
219
220
    assert 'Hook script failed' in str(excinfo.value)
221
    assert os.path.exists('inputhooks')
222
223
224
def make_test_repo(name):
225
    hooks = os.path.join(name, 'hooks')
226
    template = os.path.join(name, 'input{{cookiecutter.shellhooks}}')
227
    os.mkdir(name)
228
    os.mkdir(hooks)
229
    os.mkdir(template)
230
231
    with open(os.path.join(template, 'README.rst'), 'w') as f:
232
        f.write("foo\n===\n\nbar\n")
233
234
    if sys.platform.startswith('win'):
235
        filename = os.path.join(hooks, 'pre_gen_project.bat')
236
        with open(filename, 'w') as f:
237
            f.write("@echo off\n")
238
            f.write("\n")
239
            f.write("echo pre generation hook\n")
240
            f.write("echo. >shell_pre.txt\n")
241
242
        filename = os.path.join(hooks, 'post_gen_project.bat')
243
        with open(filename, 'w') as f:
244
            f.write("@echo off\n")
245
            f.write("\n")
246
            f.write("echo post generation hook\n")
247
            f.write("echo. >shell_post.txt\n")
248
    else:
249
        filename = os.path.join(hooks, 'pre_gen_project.sh')
250
        with open(filename, 'w') as f:
251
            f.write("#!/bin/bash\n")
252
            f.write("\n")
253
            f.write("echo 'pre generation hook';\n")
254
            f.write("touch 'shell_pre.txt'\n")
255
        # Set the execute bit
256
        os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR)
257
258
        filename = os.path.join(hooks, 'post_gen_project.sh')
259
        with open(filename, 'w') as f:
260
            f.write("#!/bin/bash\n")
261
            f.write("\n")
262
            f.write("echo 'post generation hook';\n")
263
            f.write("touch 'shell_post.txt'\n")
264
        # Set the execute bit
265
        os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR)
266
267
268
@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
269
def test_run_shell_hooks():
270
    make_test_repo('tests/test-shellhooks')
271
    generate.generate_files(
272
        context={
273
            'cookiecutter': {'shellhooks': 'shellhooks'}
274
        },
275
        repo_dir='tests/test-shellhooks/',
276
        output_dir='tests/test-shellhooks/'
277
    )
278
    shell_pre_file = 'tests/test-shellhooks/inputshellhooks/shell_pre.txt'
279
    shell_post_file = 'tests/test-shellhooks/inputshellhooks/shell_post.txt'
280
    assert os.path.exists(shell_pre_file)
281
    assert os.path.exists(shell_post_file)
282