Passed
Push — master ( cfd2a0...77debc )
by
unknown
01:59 queued 11s
created

tests.conftest.isolated_filesystem()   A

Complexity

Conditions 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 12
rs 9.9
c 0
b 0
f 0
cc 1
nop 2
1
"""pytest fixtures which are globally available throughout the suite."""
2
import os
3
import shutil
4
5
import pytest
6
7
from cookiecutter import utils
8
from cookiecutter.config import DEFAULT_CONFIG
9
10
11
USER_CONFIG = """
12
cookiecutters_dir: '{cookiecutters_dir}'
13
replay_dir: '{replay_dir}'
14
"""
15
# In YAML, double quotes mean to use escape sequences.
16
# Single quotes mean we will have unescaped backslahes.
17
# http://blogs.perl.org/users/tinita/2018/03/
18
# strings-in-yaml---to-quote-or-not-to-quote.html
19
20
21
@pytest.fixture(autouse=True)
22
def isolated_filesystem(monkeypatch, tmp_path):
23
    """Ensure filesystem isolation, set the user home to a tmp_path."""
24
    root_path = tmp_path.joinpath("home")
25
    root_path.mkdir()
26
    cookiecutters_dir = root_path.joinpath(".cookiecutters/")
27
    replay_dir = root_path.joinpath(".cookiecutter_replay/")
28
    monkeypatch.setitem(DEFAULT_CONFIG, 'cookiecutters_dir', str(cookiecutters_dir))
29
    monkeypatch.setitem(DEFAULT_CONFIG, 'replay_dir', str(replay_dir))
30
31
    monkeypatch.setenv("HOME", str(root_path))
32
    monkeypatch.setenv("USERPROFILE", str(root_path))
33
34
35
def backup_dir(original_dir, backup_dir):
36
    """Generate backup directory based on original directory."""
37
    # If the default original_dir is pre-existing, move it to a temp location
38
    if not os.path.isdir(original_dir):
39
        return False
40
41
    # Remove existing backups before backing up. If they exist, they're stale.
42
    if os.path.isdir(backup_dir):
43
        utils.rmtree(backup_dir)
44
45
    shutil.copytree(original_dir, backup_dir)
46
    return True
47
48
49
def restore_backup_dir(original_dir, backup_dir, original_dir_found):
50
    """Restore default contents."""
51
    # Carefully delete the created original_dir only in certain
52
    # conditions.
53
    original_dir_is_dir = os.path.isdir(original_dir)
54
    if original_dir_found:
55
        # Delete the created original_dir as long as a backup
56
        # exists
57
        if original_dir_is_dir and os.path.isdir(backup_dir):
58
            utils.rmtree(original_dir)
59
    else:
60
        # Delete the created original_dir.
61
        # There's no backup because it never existed
62
        if original_dir_is_dir:
63
            utils.rmtree(original_dir)
64
65
    # Restore the user's default original_dir contents
66
    if os.path.isdir(backup_dir):
67
        shutil.copytree(backup_dir, original_dir)
68
    if os.path.isdir(original_dir):
69
        utils.rmtree(backup_dir)
70
71
72
@pytest.fixture(scope='function')
73
def clean_system(request):
74
    """Fixture. Simulates a clean system with no configured or cloned cookiecutters.
75
76
    It runs code which can be regarded as setup code as known from a unittest
77
    TestCase. Additionally it defines a local function referring to values
78
    which have been stored to local variables in the setup such as the location
79
    of the cookiecutters on disk. This function is registered as a teardown
80
    hook with `request.addfinalizer` at the very end of the fixture. Pytest
81
    runs the named hook as soon as the fixture is out of scope, when the test
82
    finished to put it another way.
83
84
    During setup:
85
86
    * Back up the `~/.cookiecutterrc` config file to `~/.cookiecutterrc.backup`
87
    * Back up the `~/.cookiecutters/` dir to `~/.cookiecutters.backup/`
88
    * Back up the `~/.cookiecutter_replay/` dir to
89
      `~/.cookiecutter_replay.backup/`
90
    * Starts off a test case with no pre-existing `~/.cookiecutterrc` or
91
      `~/.cookiecutters/` or `~/.cookiecutter_replay/`
92
93
    During teardown:
94
95
    * Delete `~/.cookiecutters/` only if a backup is present at
96
      `~/.cookiecutters.backup/`
97
    * Delete `~/.cookiecutter_replay/` only if a backup is present at
98
      `~/.cookiecutter_replay.backup/`
99
    * Restore the `~/.cookiecutterrc` config file from
100
      `~/.cookiecutterrc.backup`
101
    * Restore the `~/.cookiecutters/` dir from `~/.cookiecutters.backup/`
102
    * Restore the `~/.cookiecutter_replay/` dir from
103
      `~/.cookiecutter_replay.backup/`
104
105
    """
106
    # If ~/.cookiecutterrc is pre-existing, move it to a temp location
107
    user_config_path = os.path.expanduser('~/.cookiecutterrc')
108
    user_config_path_backup = os.path.expanduser('~/.cookiecutterrc.backup')
109
    if os.path.exists(user_config_path):
110
        user_config_found = True
111
        shutil.copy(user_config_path, user_config_path_backup)
112
        os.remove(user_config_path)
113
    else:
114
        user_config_found = False
115
116
    # If the default cookiecutters_dir is pre-existing, move it to a
117
    # temp location
118
    cookiecutters_dir = os.path.expanduser('~/.cookiecutters')
119
    cookiecutters_dir_backup = os.path.expanduser('~/.cookiecutters.backup')
120
    cookiecutters_dir_found = backup_dir(cookiecutters_dir, cookiecutters_dir_backup)
121
122
    # If the default cookiecutter_replay_dir is pre-existing, move it to a
123
    # temp location
124
    cookiecutter_replay_dir = os.path.expanduser('~/.cookiecutter_replay')
125
    cookiecutter_replay_dir_backup = os.path.expanduser('~/.cookiecutter_replay.backup')
126
    cookiecutter_replay_dir_found = backup_dir(
127
        cookiecutter_replay_dir, cookiecutter_replay_dir_backup
128
    )
129
130
    def restore_backup():
131
        # If it existed, restore ~/.cookiecutterrc
132
        # We never write to ~/.cookiecutterrc, so this logic is simpler.
133
        if user_config_found and os.path.exists(user_config_path_backup):
134
            shutil.copy(user_config_path_backup, user_config_path)
135
            os.remove(user_config_path_backup)
136
137
        # Carefully delete the created ~/.cookiecutters dir only in certain
138
        # conditions.
139
        restore_backup_dir(
140
            cookiecutters_dir, cookiecutters_dir_backup, cookiecutters_dir_found
141
        )
142
143
        # Carefully delete the created ~/.cookiecutter_replay dir only in
144
        # certain conditions.
145
        restore_backup_dir(
146
            cookiecutter_replay_dir,
147
            cookiecutter_replay_dir_backup,
148
            cookiecutter_replay_dir_found,
149
        )
150
151
    request.addfinalizer(restore_backup)
152
153
154
@pytest.fixture(scope='session')
155
def user_dir(tmp_path_factory):
156
    """Fixture that simulates the user's home directory."""
157
    return tmp_path_factory.mktemp('user_dir')
158
159
160
@pytest.fixture(scope='session')
161
def user_config_data(user_dir):
162
    """Fixture that creates 2 Cookiecutter user config dirs.
163
164
     It will create it in the user's home directory.
165
166
    * `cookiecutters_dir`
167
    * `cookiecutter_replay`
168
169
    :returns: Dict with name of both user config dirs
170
    """
171
    cookiecutters_dir = user_dir.joinpath('cookiecutters')
172
    cookiecutters_dir.mkdir()
173
    replay_dir = user_dir.joinpath('cookiecutter_replay')
174
    replay_dir.mkdir()
175
    return {
176
        'cookiecutters_dir': str(cookiecutters_dir),
177
        'replay_dir': str(replay_dir),
178
    }
179
180
181
@pytest.fixture(scope='session')
182
def user_config_file(user_dir, user_config_data):
183
    """Fixture that creates a config file called `config`.
184
185
     It will create it in the user's home directory, with YAML from
186
     `user_config_data`.
187
188
    :param user_dir: Simulated user's home directory
189
    :param user_config_data: Dict of config values
190
    :returns: String of path to config file
191
    """
192
    config_file = user_dir.joinpath('config')
193
194
    config_text = USER_CONFIG.format(**user_config_data)
195
    config_file.write_text(config_text)
196
    return str(config_file)
197
198
199
@pytest.fixture
200
def output_dir(tmp_path):
201
    """Fixture to prepare test output directory."""
202
    output_path = tmp_path.joinpath("output")
203
    output_path.mkdir()
204
    return str(output_path)
205
206
207
@pytest.fixture
208
def clone_dir(tmp_path):
209
    """Simulate creation of a directory called `clone_dir` inside of `tmp_path`. \
210
    Returns a str to said directory."""
211
    clone_dir = tmp_path.joinpath("clone_dir")
212
    clone_dir.mkdir()
213
    return clone_dir
214