Completed
Pull Request — master (#694)
by Eric
01:23
created

TestRealHooks.teardown_method()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
c 1
b 1
f 0
dl 0
loc 2
rs 10
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
test_real_hooks_with_context_serialized
6
---------------------------------------
7
8
Additional tests for `cookiecutter.hooks` module.
9
"""
10
11
import os
12
import errno
13
import mock
14
import json
15
import sys
16
17
from cookiecutter import hooks, utils
18
from testfixtures import LogCapture, ShouldRaise
19
20
21
class TestRealHooks(object):
22
    repo_path = os.path.abspath(
23
        'tests/test-real-hooks-with-serialized-context')
24
    hooks_path = repo_path + '/hooks'
25
26
    def teardown_method(self, method):
27
        LogCapture.uninstall_all()
28
29
    def test_run_script_with_context_get_updated_context(self):
30
        """
31
        Execute a hook script, passing a serialized context object and
32
        getting the context updated
33
        """
34
        context = {
35
            "my_key": "my_val"
36
        }
37
        expected = {
38
            "my_key": "my_val_updated"
39
        }
40
        actual = hooks.run_script_with_context(
41
            os.path.join(
42
                self.repo_path,
43
                'update_context',
44
                'hooks',
45
                'pre_gen_project.py'
46
            ),
47
            'tests',
48
            context
49
        )
50
51
        assert actual == expected
52
53
    def test_run_script_with_context_returns_context(self):
54
        """
55
        Execute a hook script, passing a serialized context object
56
        """
57
        context = {
58
            "my_key": "my_val"
59
        }
60
        actual = hooks.run_script_with_context(
61
            os.path.join(
62
                self.repo_path, 'simple', 'hooks', 'pre_gen_project.py'),
63
            'tests',
64
            context
65
        )
66
67
        assert actual == context
68
69
    def test_run_hook_returns_context(self):
70
        """
71
        Execute a hook script, passing a serialized context object
72
        """
73
        context = {
74
            "my_key": "my_val"
75
        }
76
77
        with utils.work_in(os.path.join(self.repo_path, 'simple')):
78
            actual = hooks.run_hook(
79
                'pre_gen_project',
80
                os.path.join(
81
                    self.repo_path,
82
                    'simple',
83
                    'input{{simple_hooks}}'
84
                ),
85
                context
86
            )
87
            assert actual == context
88
89
        with utils.work_in(os.path.join(self.repo_path, 'update_context')):
90
            expected = {
91
                "my_key": "my_val_updated"
92
            }
93
            actual = hooks.run_hook(
94
                'pre_gen_project',
95
                os.path.join(
96
                    self.repo_path,
97
                    'update_context',
98
                    'input{{update_context_hooks}}'
99
                ),
100
                context
101
            )
102
            assert actual == expected
103
104
        with utils.work_in(os.path.join(self.repo_path, 'simple')):
105
            actual = hooks.run_hook(
106
                'not_handled_hook',
107
                os.path.join(
108
                    self.repo_path,
109
                    'simple',
110
                    'input{{simple_hooks}}'
111
                ),
112
                context
113
            )
114
            assert actual == context
115
116
    def test_run_script_with_context_runs_hook_in_place(self):
117
        """
118
        Execute a hook script in place, passing a serialized context object
119
        """
120
        hook = os.path.join(
121
            self.repo_path,
122
            'inplace',
123
            'hooks',
124
            'pre_gen_project.py'
125
        )
126
        context = {
127
            "_run_hook_in_place": True
128
        }
129
        expected = {
130
            "_run_hook_in_place": True,
131
            "inplace": hook
132
        }
133
        actual = hooks.run_script_with_context(hook, 'tests', context)
134
135
        assert actual == expected
136
137
    def test_getting_bad_json_returns_original_context(self):
138
        """
139
        Execute a hook script that returns bad json
140
        """
141
        context = {
142
            "my_key": "my_val",
143
        }
144
145
        actual = hooks.run_script_with_context(
146
            os.path.join(
147
                self.repo_path,
148
                'bad_json',
149
                'hooks',
150
                'pre_gen_project.py'
151
            ),
152
            'tests',
153
            context
154
        )
155
156
        assert actual == context
157
158
    @mock.patch('subprocess.Popen', autospec=True)
159
    def test_handle_lost_stdin_during_communication_on_windows_os(
160
        self, mock_popen
161
    ):
162
        """
163
        Ensure that an OSError raised from Popen._stdin_write is correctly
164
        caught and logged, while not blocking the process on windows OS
165
        """
166
        context = {
167
            "my_key": "my_val"
168
        }
169
170
        log = LogCapture()
171
172
        proc = mock_popen.return_value
173
        proc.communicate.side_effect = OSError(
174
            errno.EINVAL, 'Invalid Argument'
175
        )
176
        proc.communicate.return_value = json.dumps(context).encode()
177
        proc.wait.return_value = 0
178
179
        sys.platform = mock.MagicMock()
180
        args = {'startswith.return_value': True}
181
        sys.platform.configure_mock(**args)
182
183
        actual = hooks.run_script_with_context(
184
            os.path.join(
185
                self.repo_path, 'simple', 'hooks', 'pre_gen_project.py'),
186
            'tests',
187
            context
188
        )
189
190
        log.check(
191
            ('root', 'WARNING', 'Popen.communicate failed certainly ' +
192
                'because of the issue #19612')
193
        )
194
        assert actual == context
195
196
    @mock.patch('subprocess.Popen', autospec=True)
197
    def test_handle_oserror_during_communication_on_non_windows_os(
198
        self, mock_popen
199
    ):
200
        """
201
        Ensure that an OSError raised on a non windows os is bubbled up
202
        """
203
        proc = mock_popen.return_value
204
        proc.communicate.side_effect = OSError(
205
            errno.EINVAL, 'Invalid Argument'
206
        )
207
208
        sys.platform = mock.MagicMock()
209
        args = {'startswith.return_value': False}
210
        sys.platform.configure_mock(**args)
211
212
        with ShouldRaise() as s:
213
            hooks.run_script_with_context(
214
                os.path.join(
215
                    self.repo_path, 'simple', 'hooks', 'pre_gen_project.py'),
216
                'tests',
217
                {}
218
            )
219
            assert s.raised.code == errno.EINVAL
220