Completed
Pull Request — master (#406)
by
unknown
01:47
created

test_identify_raise_on_unknown_repo()   B

Complexity

Conditions 6

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 6
dl 0
loc 25
rs 7.5384

2 Methods

Rating   Name   Duplication   Size   Complexity  
A test_hg_clone_does_not_change_line_endings_on_win() 0 10 2
A test_git_clone_does_not_change_line_endings_on_win() 0 10 2
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
test_vcs
6
------------
7
8
Tests for `cookiecutter.vcs` module.
9
"""
10
11
import os
12
import pytest
13
14
from cookiecutter import exceptions, vcs
15
16
17
@pytest.mark.parametrize('repo_url, exp_repo_type, exp_repo_url', [
18
    (
19
        "git+https://github.com/pytest-dev/cookiecutter-pytest-plugin.git",
20
        "git",
21
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git"
22
    ), (
23
        "hg+https://bitbucket.org/foo/bar.hg",
24
        "hg",
25
        "https://bitbucket.org/foo/bar.hg"
26
    ), (
27
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git",
28
        "git",
29
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git"
30
    ), (
31
        "https://bitbucket.org/foo/bar.hg",
32
        "hg",
33
        "https://bitbucket.org/foo/bar.hg"
34
    )
35
])
36
def test_identify_known_repo(repo_url, exp_repo_type, exp_repo_url):
37
    assert vcs.identify_repo(repo_url) == (exp_repo_type, exp_repo_url)
38
39
40
@pytest.fixture(params=[
41
    "foo+git",  # uses explicit identifier with 'git' in the wrong place
42
    "foo+hg",  # uses explicit identifier with 'hg' in the wrong place
43
    "foo+bar",  # uses explicit identifier with neither 'git' nor 'hg'
44
    "foobar"  # no identifier but neither 'git' nor 'bitbucket' in url
45
])
46
def unknown_repo_type_url(request):
47
    return request.param
48
49
50
def test_identify_raise_on_unknown_repo(unknown_repo_type_url):
51
    with pytest.raises(exceptions.UnknownRepoType):
52
        vcs.identify_repo(unknown_repo_type_url)
53
54
    def test_git_clone_does_not_change_line_endings_on_win(self):
55
        repo_dir = vcs.clone(
56
            'https://github.com/audreyr/cookiecutter-pypackage.git'
57
        )
58
        readme = open(os.path.join(repo_dir, 'README.rst'), 'rb').readlines()
59
        first_line = readme[0]
60
        self.assertTrue(first_line.endswith('\n'))
61
        self.assertFalse(first_line.endswith('\r\n'))
62
        if os.path.isdir('cookiecutter-pypackage'):
63
            utils.rmtree('cookiecutter-pypackage')
64
65
    def test_hg_clone_does_not_change_line_endings_on_win(self):
66
        repo_dir = vcs.clone(
67
            'https://bitbucket.org/pokoli/cookiecutter-trytonmodule'
68
        )
69
        readme = open(os.path.join(repo_dir, 'README.rst'), 'rb').readlines()
70
        first_line = readme[0]
71
        self.assertTrue(first_line.endswith('\n'))
72
        self.assertFalse(first_line.endswith('\r\n'))
73
        if os.path.isdir('cookiecutter-pypackage'):
74
            utils.rmtree('cookiecutter-pypackage')
75
76
77
def test_prompt_should_ask_and_rm_repo_dir(mocker, tmpdir):
78
    """In `prompt_and_delete_repo()`, if the user agrees to delete/reclone the
79
    repo, the repo should be deleted.
80
    """
81
    mock_read_user = mocker.patch(
82
        'cookiecutter.vcs.read_user_yes_no',
83
        return_value=True,
84
        autospec=True
85
    )
86
    repo_dir = tmpdir.mkdir('repo')
87
88
    vcs.prompt_and_delete_repo(str(repo_dir))
89
90
    assert mock_read_user.called
91
    assert not repo_dir.exists()
92
93
94
def test_prompt_should_ask_and_keep_repo_dir(mocker, tmpdir):
95
    """In `prompt_and_delete_repo()`, if the user wants to keep their old
96
    cloned template repo, it should not be deleted.
97
    """
98
    mock_read_user = mocker.patch(
99
        'cookiecutter.vcs.read_user_yes_no',
100
        return_value=False,
101
        autospec=True
102
    )
103
    repo_dir = tmpdir.mkdir('repo')
104
105
    with pytest.raises(SystemExit):
106
        vcs.prompt_and_delete_repo(str(repo_dir))
107
108
    assert mock_read_user.called
109
    assert repo_dir.exists()
110
111
112
def test_prompt_should_not_ask_if_no_input_and_rm_repo_dir(mocker, tmpdir):
113
    """In `prompt_and_delete_repo()`, if `no_input` is True, the call to
114
    `vcs.read_user_yes_no()` should be suppressed.
115
    """
116
    mock_read_user = mocker.patch(
117
        'cookiecutter.vcs.read_user_yes_no',
118
        return_value=True,
119
        autospec=True
120
    )
121
    repo_dir = tmpdir.mkdir('repo')
122
123
    vcs.prompt_and_delete_repo(str(repo_dir), no_input=True)
124
125
    assert not mock_read_user.called
126
    assert not repo_dir.exists()
127
128
129
@pytest.fixture
130
def clone_dir(tmpdir):
131
    """Simulates creation of a directory called `clone_dir` inside of `tmpdir`.
132
    Returns a str to said directory.
133
    """
134
    return str(tmpdir.mkdir('clone_dir'))
135
136
137
def test_clone_should_raise_if_vcs_not_installed(mocker, clone_dir):
138
    """In `clone()`, a `VCSNotInstalled` exception should be raised if no VCS
139
    is installed.
140
    """
141
    mocker.patch(
142
        'cookiecutter.vcs.is_vcs_installed',
143
        autospec=True,
144
        return_value=False
145
    )
146
147
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
148
149
    with pytest.raises(exceptions.VCSNotInstalled):
150
        vcs.clone(repo_url, clone_to_dir=clone_dir)
151
152
153
@pytest.mark.parametrize('which_return, result', [
154
    ('', False),
155
    (None, False),
156
    (False, False),
157
    ('/usr/local/bin/git', True),
158
])
159
def test_is_vcs_installed(mocker, which_return, result):
160
    mocker.patch(
161
        'cookiecutter.vcs.which',
162
        autospec=True,
163
        return_value=which_return
164
    )
165
    assert vcs.is_vcs_installed('git') == result
166
167
168
@pytest.mark.parametrize('repo_type, repo_url, repo_name', [
169
    ('git', 'https://github.com/hello/world.git', 'world'),
170
    ('hg', 'https://bitbucket.org/foo/bar', 'bar'),
171
])
172
def test_clone_should_invoke_git(
173
        mocker, clone_dir, repo_type, repo_url, repo_name):
174
    """When `clone()` is called with a git/hg repo, the corresponding VCS
175
    command should be run via `subprocess.check_call()`.
176
177
    This should take place:
178
    * In the correct dir
179
    * With the correct args.
180
    """
181
    mocker.patch(
182
        'cookiecutter.vcs.is_vcs_installed',
183
        autospec=True,
184
        return_value=True
185
    )
186
187
    mock_subprocess = mocker.patch(
188
        'cookiecutter.vcs.subprocess.check_call',
189
        autospec=True,
190
    )
191
    expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name))
192
193
    branch = 'foobar'
194
195
    repo_dir = vcs.clone(
196
        repo_url,
197
        checkout=branch,
198
        clone_to_dir=clone_dir,
199
        no_input=True
200
    )
201
202
    assert repo_dir == expected_repo_dir
203
204
    mock_subprocess.assert_any_call(
205
        [repo_type, 'clone', repo_url], cwd=clone_dir
206
    )
207
    mock_subprocess.assert_any_call(
208
        [repo_type, 'checkout', branch], cwd=expected_repo_dir
209
    )
210
211
212
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
213
    """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit
214
    without cloning anything.
215
    """
216
    mocker.patch(
217
        'cookiecutter.vcs.is_vcs_installed',
218
        autospec=True,
219
        return_value=True
220
    )
221
    mocker.patch(
222
        'cookiecutter.vcs.prompt_and_delete_repo',
223
        side_effect=SystemExit,
224
        autospec=True
225
    )
226
    mock_subprocess = mocker.patch(
227
        'cookiecutter.vcs.subprocess.check_call',
228
        autospec=True,
229
    )
230
231
    clone_to_dir = tmpdir.mkdir('clone')
232
233
    # Create repo_dir to trigger prompt_and_delete_repo
234
    clone_to_dir.mkdir('cookiecutter-pytest-plugin')
235
236
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
237
238
    with pytest.raises(SystemExit):
239
        vcs.clone(repo_url, clone_to_dir=str(clone_to_dir))
240
    assert not mock_subprocess.called
241
242
243
def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir):
244
    """In `clone()`, repo URL's trailing slash should be stripped if one is
245
    present.
246
    """
247
    mocker.patch(
248
        'cookiecutter.vcs.is_vcs_installed',
249
        autospec=True,
250
        return_value=True
251
    )
252
253
    mock_subprocess = mocker.patch(
254
        'cookiecutter.vcs.subprocess.check_call',
255
        autospec=True,
256
    )
257
258
    vcs.clone(
259
        'https://github.com/foo/bar/',
260
        clone_to_dir=clone_dir,
261
        no_input=True
262
    )
263
264
    mock_subprocess.assert_called_once_with(
265
        ['git', 'clone', 'https://github.com/foo/bar'], cwd=clone_dir
266
    )
267