Completed
Push — improve-find-hooks ( cef439...f45121 )
by Michael
01:07
created

TestFindHooks   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 39
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 39
rs 10
c 1
b 0
f 0
wmc 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
B TestExternalHooks.teardown_method() 0 15 7
A TestExternalHooks.test_run_script() 0 4 2
A TestExternalHooks.setup_method() 0 2 1
A TestExternalHooks.test_run_script_cwd() 0 8 3
1
# -*- coding: utf-8 -*-
2
3
"""
4
test_hooks
5
------------
6
7
Tests for `cookiecutter.hooks` module.
8
"""
9
10
import os
11
import pytest
12
import stat
13
import sys
14
import textwrap
15
16
from cookiecutter import hooks, utils, exceptions
17
18
19
def make_test_repo(name):
20
    """Helper function which is called in the test setup methods."""
21
    hook_dir = os.path.join(name, 'hooks')
22
    template = os.path.join(name, 'input{{hooks}}')
23
    os.mkdir(name)
24
    os.mkdir(hook_dir)
25
    os.mkdir(template)
26
27
    with open(os.path.join(template, 'README.rst'), 'w') as f:
28
        f.write("foo\n===\n\nbar\n")
29
30
    with open(os.path.join(hook_dir, 'pre_gen_project.py'), 'w') as f:
31
        f.write("#!/usr/bin/env python\n")
32
        f.write("# -*- coding: utf-8 -*-\n")
33
        f.write("from __future__ import print_function\n")
34
        f.write("\n")
35
        f.write("print('pre generation hook')\n")
36
        f.write("f = open('python_pre.txt', 'w')\n")
37
        f.write("f.close()\n")
38
39 View Code Duplication
    if sys.platform.startswith('win'):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
40
        post = 'post_gen_project.bat'
41
        with open(os.path.join(hook_dir, post), 'w') as f:
42
            f.write("@echo off\n")
43
            f.write("\n")
44
            f.write("echo post generation hook\n")
45
            f.write("echo. >shell_post.txt\n")
46
    else:
47
        post = 'post_gen_project.sh'
48
        filename = os.path.join(hook_dir, post)
49
        with open(filename, 'w') as f:
50
            f.write("#!/bin/bash\n")
51
            f.write("\n")
52
            f.write("echo 'post generation hook';\n")
53
            f.write("touch 'shell_post.txt'\n")
54
        # Set the execute bit
55
        os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR)
56
57
    return post
58
59
60
class TestExternalHooks(object):
61
62
    repo_path = os.path.abspath('tests/test-hooks/')
63
    hooks_path = os.path.abspath('tests/test-hooks/hooks')
64
65
    def setup_method(self, method):
66
        self.post_hook = make_test_repo(self.repo_path)
67
68
    def teardown_method(self, method):
69
        utils.rmtree(self.repo_path)
70
71
        if os.path.exists('python_pre.txt'):
72
            os.remove('python_pre.txt')
73
        if os.path.exists('shell_post.txt'):
74
            os.remove('shell_post.txt')
75
        if os.path.exists('tests/shell_post.txt'):
76
            os.remove('tests/shell_post.txt')
77
        if os.path.exists('tests/test-hooks/input{{hooks}}/python_pre.txt'):
78
            os.remove('tests/test-hooks/input{{hooks}}/python_pre.txt')
79
        if os.path.exists('tests/test-hooks/input{{hooks}}/shell_post.txt'):
80
            os.remove('tests/test-hooks/input{{hooks}}/shell_post.txt')
81
        if os.path.exists('tests/context_post.txt'):
82
            os.remove('tests/context_post.txt')
83
84
    def test_run_script(self):
85
        """Execute a hook script, independently of project generation"""
86
        hooks.run_script(os.path.join(self.hooks_path, self.post_hook))
87
        assert os.path.isfile('shell_post.txt')
88
89
    def test_run_script_cwd(self):
90
        """Change directory before running hook"""
91
        hooks.run_script(
92
            os.path.join(self.hooks_path, self.post_hook),
93
            'tests'
94
        )
95
        assert os.path.isfile('tests/shell_post.txt')
96
        assert 'tests' not in os.getcwd()
97
98
    def test_run_script_with_context(self):
99
        """Execute a hook script, passing a context"""
100
101
        hook_path = os.path.join(self.hooks_path, 'post_gen_project.sh')
102
103
        if sys.platform.startswith('win'):
104
            post = 'post_gen_project.bat'
105
            with open(os.path.join(self.hooks_path, post), 'w') as f:
106
                f.write("@echo off\n")
107
                f.write("\n")
108
                f.write("echo post generation hook\n")
109
                f.write("echo. >{{cookiecutter.file}}\n")
110
        else:
111
            with open(hook_path, 'w') as fh:
112
                fh.write("#!/bin/bash\n")
113
                fh.write("\n")
114
                fh.write("echo 'post generation hook';\n")
115
                fh.write("touch 'shell_post.txt'\n")
116
                fh.write("touch '{{cookiecutter.file}}'\n")
117
                os.chmod(hook_path, os.stat(hook_path).st_mode | stat.S_IXUSR)
118
119
        hooks.run_script_with_context(
120
            os.path.join(self.hooks_path, self.post_hook),
121
            'tests',
122
            {
123
                'cookiecutter': {
124
                    'file': 'context_post.txt'
125
                }
126
            })
127
        assert os.path.isfile('tests/context_post.txt')
128
        assert 'tests' not in os.getcwd()
129
130
    def test_run_hook(self):
131
        """Execute hook from specified template in specified output
132 View Code Duplication
        directory.
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
133
        """
134
        tests_dir = os.path.join(self.repo_path, 'input{{hooks}}')
135
        with utils.work_in(self.repo_path):
136
            hooks.run_hook('pre_gen_project', tests_dir, {})
137
            assert os.path.isfile(os.path.join(tests_dir, 'python_pre.txt'))
138
139
            hooks.run_hook('post_gen_project', tests_dir, {})
140
            assert os.path.isfile(os.path.join(tests_dir, 'shell_post.txt'))
141
142
    def test_run_failing_hook(self):
143
        hook_path = os.path.join(self.hooks_path, 'pre_gen_project.py')
144
        tests_dir = os.path.join(self.repo_path, 'input{{hooks}}')
145
146
        with open(hook_path, 'w') as f:
147
            f.write("#!/usr/bin/env python\n")
148
            f.write("import sys; sys.exit(1)\n")
149
150
        with utils.work_in(self.repo_path):
151
            with pytest.raises(exceptions.FailedHookException) as excinfo:
152
                hooks.run_hook('pre_gen_project', tests_dir, {})
153
            assert 'Hook script failed' in str(excinfo.value)
154
155
156
@pytest.yield_fixture
157
def dir_with_hooks(tmpdir):
158
    """Yield a directory that contains hook backup files."""
159
160
    hooks_dir = tmpdir.mkdir('hooks')
161
162
    pre_hook_content = textwrap.dedent(
163
        u"""
164
        #!/usr/bin/env python
165
        # -*- coding: utf-8 -*-
166
        print('pre_gen_project.py~')
167
        """
168
    )
169
    pre_gen_hook_file = hooks_dir / 'pre_gen_project.py~'
170
    pre_gen_hook_file.write_text(pre_hook_content, encoding='utf8')
171
172
    post_hook_content = textwrap.dedent(
173
        u"""
174
        #!/usr/bin/env python
175
        # -*- coding: utf-8 -*-
176
        print('post_gen_project.py~')
177
        """
178
    )
179
180
    post_gen_hook_file = hooks_dir / 'post_gen_project.py~'
181
    post_gen_hook_file.write_text(post_hook_content, encoding='utf8')
182
183
    # Make sure to yield the parent directory as `find_hooks()`
184
    # looks into `hooks/` in the current working directory
185
    yield str(tmpdir)
186
187
    pre_gen_hook_file.remove()
188
    post_gen_hook_file.remove()
189
190
191
def test_ignore_hook_backup_files(monkeypatch, dir_with_hooks):
192
    # Change the current working directory that contains `hooks/`
193
    monkeypatch.chdir(dir_with_hooks)
194
    assert hooks.find_hook('pre_gen_project') is None
195
    assert hooks.find_hook('post_gen_project') is None
196