1# -*- coding: utf-8 -*- 2 3"""Tests for `cookiecutter.utils` module.""" 4 5import os 6import stat 7import sys 8 9import pytest 10 11from cookiecutter import utils 12 13 14def make_readonly(path): 15 """Change the access permissions to readonly for a given file.""" 16 mode = os.stat(path).st_mode 17 os.chmod(path, mode & ~stat.S_IWRITE) 18 19 20def test_rmtree(): 21 """Verify `utils.rmtree` remove files marked as read-only.""" 22 os.mkdir('foo') 23 with open('foo/bar', "w") as f: 24 f.write("Test data") 25 make_readonly('foo/bar') 26 utils.rmtree('foo') 27 assert not os.path.exists('foo') 28 29 30def test_make_sure_path_exists(): 31 """Verify correct True/False response from `utils.make_sure_path_exists`. 32 33 Should return True if directory exist or created. 34 Should return False if impossible to create directory (for example protected) 35 """ 36 if sys.platform.startswith('win'): 37 existing_directory = os.path.abspath(os.curdir) 38 uncreatable_directory = 'a*b' 39 else: 40 existing_directory = '/usr/' 41 uncreatable_directory = '/this-doesnt-exist-and-cant-be-created/' 42 43 assert utils.make_sure_path_exists(existing_directory) 44 assert utils.make_sure_path_exists('tests/blah') 45 assert utils.make_sure_path_exists('tests/trailingslash/') 46 assert not utils.make_sure_path_exists(uncreatable_directory) 47 utils.rmtree('tests/blah/') 48 utils.rmtree('tests/trailingslash/') 49 50 51def test_workin(): 52 """Verify returning to original folder after `utils.work_in` use.""" 53 cwd = os.getcwd() 54 ch_to = 'tests/files' 55 56 class TestException(Exception): 57 pass 58 59 def test_work_in(): 60 with utils.work_in(ch_to): 61 test_dir = os.path.join(cwd, ch_to).replace("/", os.sep) 62 assert test_dir == os.getcwd() 63 raise TestException() 64 65 # Make sure we return to the correct folder 66 assert cwd == os.getcwd() 67 68 # Make sure that exceptions are still bubbled up 69 with pytest.raises(TestException): 70 test_work_in() 71 72 73def test_prompt_should_ask_and_rm_repo_dir(mocker, tmpdir): 74 """In `prompt_and_delete()`, if the user agrees to delete/reclone the \ 75 repo, the repo should be deleted.""" 76 mock_read_user = mocker.patch( 77 'cookiecutter.utils.read_user_yes_no', return_value=True, autospec=True 78 ) 79 repo_dir = tmpdir.mkdir('repo') 80 81 deleted = utils.prompt_and_delete(str(repo_dir)) 82 83 assert mock_read_user.called 84 assert not repo_dir.exists() 85 assert deleted 86 87 88def test_prompt_should_ask_and_exit_on_user_no_answer(mocker, tmpdir): 89 """In `prompt_and_delete()`, if the user decline to delete/reclone the \ 90 repo, cookiecutter should exit.""" 91 mock_read_user = mocker.patch( 92 'cookiecutter.utils.read_user_yes_no', return_value=False, 93 ) 94 mock_sys_exit = mocker.patch('sys.exit', return_value=True) 95 repo_dir = tmpdir.mkdir('repo') 96 97 deleted = utils.prompt_and_delete(str(repo_dir)) 98 99 assert mock_read_user.called 100 assert repo_dir.exists() 101 assert not deleted 102 assert mock_sys_exit.called 103 104 105def test_prompt_should_ask_and_rm_repo_file(mocker, tmpdir): 106 """In `prompt_and_delete()`, if the user agrees to delete/reclone a \ 107 repo file, the repo should be deleted.""" 108 mock_read_user = mocker.patch( 109 'cookiecutter.utils.read_user_yes_no', return_value=True, autospec=True 110 ) 111 112 repo_file = tmpdir.join('repo.zip') 113 repo_file.write('this is zipfile content') 114 115 deleted = utils.prompt_and_delete(str(repo_file)) 116 117 assert mock_read_user.called 118 assert not repo_file.exists() 119 assert deleted 120 121 122def test_prompt_should_ask_and_keep_repo_on_no_reuse(mocker, tmpdir): 123 """In `prompt_and_delete()`, if the user wants to keep their old \ 124 cloned template repo, it should not be deleted.""" 125 mock_read_user = mocker.patch( 126 'cookiecutter.utils.read_user_yes_no', return_value=False, autospec=True 127 ) 128 repo_dir = tmpdir.mkdir('repo') 129 130 with pytest.raises(SystemExit): 131 utils.prompt_and_delete(str(repo_dir)) 132 133 assert mock_read_user.called 134 assert repo_dir.exists() 135 136 137def test_prompt_should_ask_and_keep_repo_on_reuse(mocker, tmpdir): 138 """In `prompt_and_delete()`, if the user wants to keep their old \ 139 cloned template repo, it should not be deleted.""" 140 141 def answer(question, default): 142 if 'okay to delete' in question: 143 return False 144 else: 145 return True 146 147 mock_read_user = mocker.patch( 148 'cookiecutter.utils.read_user_yes_no', side_effect=answer, autospec=True 149 ) 150 repo_dir = tmpdir.mkdir('repo') 151 152 deleted = utils.prompt_and_delete(str(repo_dir)) 153 154 assert mock_read_user.called 155 assert repo_dir.exists() 156 assert not deleted 157 158 159def test_prompt_should_not_ask_if_no_input_and_rm_repo_dir(mocker, tmpdir): 160 """Prompt should not ask if no input and rm dir. 161 162 In `prompt_and_delete()`, if `no_input` is True, the call to 163 `prompt.read_user_yes_no()` should be suppressed. 164 """ 165 mock_read_user = mocker.patch( 166 'cookiecutter.prompt.read_user_yes_no', return_value=True, autospec=True 167 ) 168 repo_dir = tmpdir.mkdir('repo') 169 170 deleted = utils.prompt_and_delete(str(repo_dir), no_input=True) 171 172 assert not mock_read_user.called 173 assert not repo_dir.exists() 174 assert deleted 175 176 177def test_prompt_should_not_ask_if_no_input_and_rm_repo_file(mocker, tmpdir): 178 """Prompt should not ask if no input and rm file. 179 180 In `prompt_and_delete()`, if `no_input` is True, the call to 181 `prompt.read_user_yes_no()` should be suppressed. 182 """ 183 mock_read_user = mocker.patch( 184 'cookiecutter.prompt.read_user_yes_no', return_value=True, autospec=True 185 ) 186 187 repo_file = tmpdir.join('repo.zip') 188 repo_file.write('this is zipfile content') 189 190 deleted = utils.prompt_and_delete(str(repo_file), no_input=True) 191 192 assert not mock_read_user.called 193 assert not repo_file.exists() 194 assert deleted 195