You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Tools/LyTestTools/tests/unit/test_file_system.py

915 lines
36 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import errno
import logging
import os
import psutil
import subprocess
import sys
import tarfile
import unittest.mock as mock
import unittest
import zipfile
import pytest
from ly_test_tools.environment import file_system
logger = logging.getLogger(__name__)
pytestmark = pytest.mark.SUITE_smoke
class TestCheckFreeSpace(unittest.TestCase):
def setUp(self):
# sdiskusage is: (total, used, free, percent)
self.disk_usage = psutil._common.sdiskusage(100 * file_system.ONE_GIB, 0, 100 * file_system.ONE_GIB, 100)
def tearDown(self):
self.disk_usage = None
@mock.patch('psutil.disk_usage')
def test_CheckFreeSpace_NoSpace_RaisesIOError(self, mock_disk_usage):
total = 100 * file_system.ONE_GIB
used = total
mock_disk_usage.return_value = psutil._common.sdiskusage(total, used, total - used, used / total)
with self.assertRaises(IOError):
file_system.check_free_space('', 1, 'Raise')
@mock.patch('psutil.disk_usage')
def test_CheckFreeSpace_EnoughSpace_NoRaise(self, mock_disk_usage):
total = 100 * file_system.ONE_GIB
needed = 1
used = total - needed
mock_disk_usage.return_value = psutil._common.sdiskusage(total, used, total - used, used / total)
dest_path = 'dest'
file_system.check_free_space(dest_path, needed, 'No Raise')
mock_disk_usage.assert_called_once_with(dest_path)
class TestSafeMakedirs(unittest.TestCase):
@mock.patch('os.makedirs')
def test_SafeMakedirs_RaisedOSErrorErrnoEEXIST_DoesNotPropagate(self, mock_makedirs):
error = OSError()
error.errno = errno.EEXIST
mock_makedirs.side_effect = error
file_system.safe_makedirs('')
@mock.patch('os.makedirs')
def test_SafeMakedirs_RaisedOSErrorNotErrnoEEXIST_Propagates(self, mock_makedirs):
error = OSError()
error.errno = errno.EINTR
mock_makedirs.side_effect = error
with self.assertRaises(OSError):
file_system.safe_makedirs('')
@mock.patch('os.makedirs')
def test_SafeMakedirs_RootDir_DoesNotPropagate(self, mock_makedirs):
error = OSError()
error.errno = errno.EACCES
if sys.platform == 'win32':
mock_makedirs.side_effect = error
file_system.safe_makedirs('C:\\')
class TestGetNewestFileInDir(unittest.TestCase):
@mock.patch('glob.iglob')
def test_GetNewestFileInDir_NoResultsFound_ReturnsNone(self, mock_glob):
mock_glob.return_value.iglob = None
result = file_system.get_newest_file_in_dir('', '')
self.assertEqual(result, None)
@mock.patch('os.path.getctime')
@mock.patch('glob.iglob')
def test_GetNewestFileInDir_TwoResultsFound_ReturnsNewer(self, mock_glob, mock_ctime):
mock_glob.return_value = ['fileA.zip', 'fileB.zip']
mock_ctime.side_effect = [1, 2]
result = file_system.get_newest_file_in_dir('', [''])
self.assertEqual(result, 'fileB.zip')
@mock.patch('os.path.getctime')
@mock.patch('glob.iglob')
def test_GetNewestFileInDir_ThreeResultsTwoExts_CtimeCalledSixTimes(self, mock_glob, mock_ctime):
mock_glob.return_value = ['fileA.zip', 'fileB.zip', 'fileC.zip']
mock_ctime.side_effect = range(6)
file_system.get_newest_file_in_dir('', ['.zip', '.tgz'])
self.assertEqual(len(mock_ctime.mock_calls), 6)
class TestUnZip(unittest.TestCase):
decomp_obj_name = 'zipfile.ZipFile'
def setUp(self):
self.file_list = []
for i in range(25):
new_src_info = zipfile.ZipInfo('{}.txt'.format(i))
new_src_info.file_size = i
self.file_list.append(new_src_info)
self.mock_handle = mock.MagicMock()
self.mock_handle.infolist.return_value = self.file_list
self.mock_handle.__enter__.return_value = self.mock_handle
self.mock_decomp = mock.MagicMock()
self.mock_decomp.return_value = self.mock_handle
self.src_path = 'src.zip'
self.dest_path = 'dest'
self.exists = False
def tearDown(self):
self.mock_handle = None
self.mock_decomp = None
def call_decomp(self, dest, src, force=True, allow_exists=False):
return file_system.unzip(dest, src, force, allow_exists)
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch(decomp_obj_name)
def test_Unzip_DefaultArgs_CallsDecompressorWithSrc(self, mock_decomp, mock_join, mock_check_free, mock_exists):
mock_exists.return_value = self.exists
self.call_decomp(self.dest_path, self.src_path)
mock_decomp.assert_called_once_with(self.src_path, 'r')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_DefaultArgs_CheckFreeSpaceCalledOnceWithOneGiBAdded(self, mock_join,
mock_check_free, mock_exists):
mock_exists.return_value = self.exists
total_size = sum(info.file_size for info in self.file_list)
with mock.patch(self.decomp_obj_name, self.mock_decomp):
self.call_decomp(self.dest_path, self.src_path)
mock_check_free.assert_called_once_with(self.dest_path,
total_size + file_system.ONE_GIB,
'Not enough space to safely extract: ')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_DefaultArgs_JoinCalledWithNoPathNoExtension(self, mock_join, mock_check_free, mock_exists):
expected_name, _ = os.path.splitext(self.src_path)
mock_exists.return_value = self.exists
with mock.patch(self.decomp_obj_name, self.mock_decomp):
self.call_decomp(self.dest_path, self.src_path)
mock_join.assert_called_once_with(self.dest_path, expected_name)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_DefaultArgs_ReturnsCorrectPath(self, mock_join, mock_check_free):
build_name = 'build_name'
expected_path = self.dest_path+'\\'+build_name
mock_join.return_value = expected_path
path = ''
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
self.assertEqual(path, expected_path)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_ReleaseBuild_ReturnsCorrectPath(self, mock_join, mock_check_free):
build_name = 'lumberyard-1.2.0.3-54321-pc-1234'
expected_path = self.dest_path + '\\' + build_name
mock_join.return_value = expected_path
path = ''
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
self.assertEqual(path, expected_path)
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_join, mock_check_free, mock_exists):
path = ''
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
mock_exists.return_value = self.exists
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234')
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_BuildDirExistsForceAndAllowExistsNotSet_CRITICALLogged(self, mock_join, mock_check_free,
mock_exists, mock_log):
force = False
allow_exists = False
self.exists = True
mock_exists.return_value = self.exists
level = logging.getLevelName("CRITICAL")
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path, force)
mock_log.log.assert_called_with(level, 'Found existing {}. Will not overwrite.'.format(path))
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_AllowExistsSet_INFOLogged(self, mock_join, mock_check_free, mock_exists, mock_log):
force = False
allow_exists = True
self.exists = True
mock_exists.return_value = self.exists
level = logging.getLevelName("INFO")
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path, force, allow_exists)
mock_log.log.assert_called_with(level, 'Found existing {}. Will not overwrite.'.format(path))
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
def test_Unzip_BuildDirExistsForceSetTrue_INFOLogged(self, mock_join, mock_check_free, mock_exists, mock_log):
path = ''
self.exists = True
mock_exists.return_value = self.exists
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
mock_log.info.assert_called_once()
class TestUnTgz(unittest.TestCase):
decomp_obj_name = 'tarfile.open'
def setUp(self):
self.file_list = []
for i in range(25):
new_src_info = tarfile.TarInfo('{}.txt'.format(i))
new_src_info.size = i
self.file_list.append(new_src_info)
self.mock_handle = mock.MagicMock()
self.mock_handle.__iter__.return_value = self.file_list
self.mock_handle.__enter__.return_value = self.mock_handle
self.mock_decomp = mock.MagicMock()
self.mock_decomp.return_value = self.mock_handle
self.src_path = 'src.tgz'
self.dest_path = 'dest'
# os.stat_result is (mode, inode, device, hard links, owner uid, size, atime, mtime, ctime)
self.src_stat = os.stat_result((0, 0, 0, 0, 0, 0, file_system.ONE_GIB, 0, 0, 0))
def tearDown(self):
self.mock_decomp = None
self.mock_handle = None
def call_decomp(self, dest, src, exact=False, force=True, allow_exists=False):
return file_system.untgz(dest, src, exact, force, allow_exists)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch(decomp_obj_name)
@mock.patch('os.stat')
def test_UnTgz_DefaultArgs_CallsDecompressorWithSrc(self, mock_stat, mock_decomp, mock_join, mock_check_free):
mock_stat.return_value = self.src_stat
self.call_decomp(self.dest_path, self.src_path)
mock_decomp.assert_called_once_with(self.src_path)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_UnTgz_DefaultArgs_CheckFreeSpaceCalledOnceWithOneGiBAdded(self, mock_stat, mock_join, mock_check_free):
mock_stat.return_value = self.src_stat
total_size = sum(info.size for info in self.file_list)
with mock.patch(self.decomp_obj_name, self.mock_decomp):
self.call_decomp(self.dest_path, self.src_path, True)
mock_check_free.assert_called_once_with(self.dest_path,
total_size + file_system.ONE_GIB,
'Not enough space to safely extract: ')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_UnTgz_DefaultArgs_JoinCalledWithNoPathNoExtension(self, mock_stat, mock_join, mock_check_free):
expected_path, _ = os.path.splitext(os.path.basename(self.src_path))
mock_stat.return_value = self.src_stat
with mock.patch(self.decomp_obj_name, self.mock_decomp):
self.call_decomp(self.dest_path, self.src_path)
mock_join.assert_called_once_with(self.dest_path, expected_path)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_DefaultArgs_ReturnsCorrectPath(self, mock_stat, mock_join, mock_check_free):
build_name = 'build_name'
expected_path = self.dest_path+'\\'+build_name
mock_join.return_value = expected_path
mock_stat.return_value = self.src_stat
path = ''
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
self.assertEqual(path, expected_path)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_ReleaseBuild_ReturnsCorrectPath(self, mock_stat, mock_join, mock_check_free):
build_name = 'lumberyard-1.2.0.3-54321-pc-1234'
expected_path = self.dest_path + '\\' + build_name
mock_join.return_value = expected_path
mock_stat.return_value = self.src_stat
path = ''
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
self.assertEqual(path, expected_path)
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_stat, mock_join, mock_check_free):
mock_stat.return_value = self.src_stat
path = ''
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234')
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_BuildDirExistsForceAndAllowExistsNotSet_CRITICALLogged(self, mock_stat, mock_join,
mock_check_free, mock_exists, mock_log):
force = False
mock_exists.return_value = True
mock_stat.return_value = self.src_stat
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path, False, force)
level = logging.getLevelName("CRITICAL")
mock_log.log.assert_called_with(level, 'Found existing {}. Will not overwrite.'.format(path))
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_AllowExiststSet_INFOLogged(self, mock_stat, mock_join, mock_check_free, mock_exists, mock_log):
allow_exists = True
force = False
mock_exists.return_value = True
mock_stat.return_value = self.src_stat
level = logging.getLevelName("INFO")
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path, False, force, allow_exists)
mock_log.log.assert_called_with(level, 'Found existing {}. Will not overwrite.'.format(path))
@mock.patch('ly_test_tools.environment.file_system.logger')
@mock.patch('os.path.exists')
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
@mock.patch('os.path.join')
@mock.patch('os.stat')
def test_Untgz_BuildDirExistsForceSetTrue_INFOLogged(self, mock_stat, mock_join,
mock_check_free, mock_exists, mock_log):
path = ''
mock_exists.return_value = True
mock_stat.return_value = self.src_stat
with mock.patch(self.decomp_obj_name, self.mock_decomp):
path = self.call_decomp(self.dest_path, self.src_path)
mock_log.info.assert_called_once()
class TestChangePermissions(unittest.TestCase):
def setUp(self):
# Create a mock of a os.walk return iterable.
self.root = 'root'
self.dirs = ['dir1', 'dir2']
self.files = ['file1', 'file2']
self.walk_iter = iter([(self.root, self.dirs, self.files)])
def tearDown(self):
self.root = None
self.dirs = None
self.files = None
self.walk_iter = None
@mock.patch('os.walk')
@mock.patch('os.chmod')
def test_ChangePermissions_DefaultValues_ChmodCalledCorrectly(self, mock_chmod, mock_walk):
os.walk.return_value = self.walk_iter
file_system.change_permissions('.', 0o777)
self.assertEqual(mock_chmod.mock_calls, [mock.call(os.path.join(self.root, self.dirs[0]), 0o777),
mock.call(os.path.join(self.root, self.dirs[1]), 0o777),
mock.call(os.path.join(self.root, self.files[0]), 0o777),
mock.call(os.path.join(self.root, self.files[1]), 0o777)])
@mock.patch('os.walk')
@mock.patch('os.chmod')
def test_ChangePermissions_DefaultValues_ReturnsTrueOnSuccess(self, mock_chmod, mock_walk):
os.walk.return_value = self.walk_iter
self.assertEqual(file_system.change_permissions('.', 0o777), True)
@mock.patch('os.walk')
@mock.patch('os.chmod')
def test_ChangePermissions_OSErrorRaised_ReturnsFalse(self, mock_chmod, mock_walk):
os.walk.return_value = self.walk_iter
os.chmod.side_effect = OSError()
self.assertEqual(file_system.change_permissions('.', 0o777), False)
class TestUnlockFile(unittest.TestCase):
def setUp(self):
self.file_name = 'file'
@mock.patch('os.chmod')
@mock.patch('os.access')
def test_UnlockFile_WriteLocked_UnlockFile(self, mock_access, mock_chmod):
mock_access.return_value = False
success = file_system.unlock_file(self.file_name)
self.assertTrue(success)
@mock.patch('os.chmod')
@mock.patch('os.access')
def test_UnlockFile_AlreadyUnlocked_LogAlreadyUnlocked(self, mock_access, mock_chmod):
mock_access.return_value = True
success = file_system.unlock_file(self.file_name)
self.assertFalse(success)
class TestLockFile(unittest.TestCase):
def setUp(self):
self.file_name = 'file'
@mock.patch('os.chmod')
@mock.patch('os.access')
def test_UnlockFile_UnlockedFile_FileLockedSuccessReturnsTrue(self, mock_access, mock_chmod):
mock_access.return_value = True
success = file_system.lock_file(self.file_name)
self.assertTrue(success)
@mock.patch('os.chmod')
@mock.patch('os.access')
def test_UnlockFile_AlreadyLocked_FileLockedFailedReturnsFalse(self, mock_access, mock_chmod):
mock_access.return_value = False
success = file_system.lock_file(self.file_name)
self.assertFalse(success)
class TestRemoveSymlinks(unittest.TestCase):
def setUp(self):
# Create a mock of a os.walk return iterable.
self.root = 'root'
self.dirs = ['dir1', 'dir2']
self.files = ['file1', 'file2']
self.walk_iter = iter([(self.root, self.dirs, self.files)])
@mock.patch('os.walk')
@mock.patch('os.rmdir')
def test_RemoveSymlinks_DefaultValues_RmdirCalledCorrectly(self, mock_rmdir, mock_walk):
os.walk.return_value = self.walk_iter
file_system.remove_symlinks('.')
self.assertEqual(mock_rmdir.mock_calls, [mock.call(os.path.join(self.root, self.dirs[0])),
mock.call(os.path.join(self.root, self.dirs[1]))])
@mock.patch('os.walk')
@mock.patch('os.rmdir')
def test_RemoveSymlinks_OSErrnoEEXISTRaised_RaiseOSError(self, mock_rmdir, mock_walk):
os.walk.return_value = self.walk_iter
error = OSError()
error.errno = errno.EEXIST
mock_rmdir.side_effect = error
with self.assertRaises(OSError):
file_system.remove_symlinks('.')
class TestDelete(unittest.TestCase):
def setUp(self):
self.path_list = ['file1', 'file2', 'dir1', 'dir2']
def tearDown(self):
self.path_list = None
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
@mock.patch('ly_test_tools.environment.file_system.change_permissions', mock.MagicMock())
@mock.patch('shutil.rmtree', mock.MagicMock())
@mock.patch('os.remove', mock.MagicMock())
def test_Delete_StringArg_ConvertsToList(self, mock_isfile, mock_isdir):
mock_file_str = 'foo'
mock_isdir.return_value = False
mock_isfile.return_value = False
file_system.delete(mock_file_str, del_files=True, del_dirs=True)
mock_isfile.assert_called_once_with(mock_file_str)
mock_isdir.assert_called_once_with(mock_file_str)
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_OSErrorRaised_ReturnsZero(self, mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper):
mock_rmtree.side_effect = OSError()
self.assertEqual(file_system.delete(self.path_list, del_files=True, del_dirs=True), False)
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_DefaultValues_ReturnsLenOfList(self,
mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper):
self.assertEqual(file_system.delete(self.path_list, del_files=True, del_dirs=True), True)
@mock.patch('os.chmod')
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_DirsFalse_RMTreeNotCalled(self,
mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper,
mock_chmod):
file_system.delete(self.path_list, del_files=True, del_dirs=False)
self.assertEqual(mock_rmtree.called, False)
self.assertEqual(mock_chmod.called, True)
self.assertEqual(mock_remove.called, True)
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_NoDirs_RMTreeNotCalled(self,
mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper):
mock_isdir.return_value = False
file_system.delete(self.path_list, del_files=False, del_dirs=True)
self.assertEqual(mock_rmtree.called, False)
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_FilesFalse_RemoveNotCalled(self,
mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper):
file_system.delete(self.path_list, del_files=False, del_dirs=True)
self.assertEqual(mock_rmtree.called, True)
self.assertEqual(mock_remove.called, False)
@mock.patch('ly_test_tools.environment.file_system.change_permissions')
@mock.patch('shutil.rmtree')
@mock.patch('os.remove')
@mock.patch('os.path.isdir')
@mock.patch('os.path.isfile')
def test_ChangePermissions_NoFiles_RemoveNotCalled(self,
mock_isfile, mock_isdir, mock_remove, mock_rmtree, mock_chper):
mock_isfile.return_value = False
file_system.delete(self.path_list, del_files=True, del_dirs=False)
self.assertEqual(mock_remove.called, False)
class TestDeleteOldest(unittest.TestCase):
def setUp(self):
self.path_list = ['file1', 'file2', 'dir1', 'dir2']
self.age_list = [4, 3, 2, 1]
def tearDown(self):
self.path_list = None
self.age_list = None
@mock.patch('glob.iglob')
@mock.patch('os.path.getctime')
@mock.patch('ly_test_tools.environment.file_system.delete')
def test_DeleteOldest_DefaultValuesKeepAll_DeleteCalledWithEmptyList(self, mock_delete, mock_ctime, mock_glob):
mock_glob.return_value = self.path_list
mock_ctime.side_effect = self.age_list
# Nothing will be deleted because it's keeping everything.
file_system.delete_oldest('', len(self.path_list), del_files=True, del_dirs=False)
mock_delete.assert_called_once_with([], True, False)
@mock.patch('glob.iglob')
@mock.patch('os.path.getctime')
@mock.patch('ly_test_tools.environment.file_system.delete')
def test_DeleteOldest_DefaultValuesKeepNone_DeleteCalledWithList(self, mock_delete, mock_ctime, mock_glob):
mock_glob.return_value = self.path_list
mock_ctime.side_effect = self.age_list
# Everything will be deleted because it's keeping nothing.
file_system.delete_oldest('', 0, del_files=True, del_dirs=False)
mock_delete.assert_called_once_with(self.path_list, True, False)
@mock.patch('glob.iglob')
@mock.patch('os.path.getctime')
@mock.patch('ly_test_tools.environment.file_system.delete')
def test_DeleteOldest_DefaultValuesKeepOne_DeleteCalledWithoutNewest(self, mock_delete, mock_ctime, mock_glob):
mock_glob.return_value = self.path_list
mock_ctime.side_effect = self.age_list
file_system.delete_oldest('', 1, del_files=True, del_dirs=False)
self.path_list.pop(0)
mock_delete.assert_called_once_with(self.path_list, True, False)
@mock.patch('glob.iglob')
@mock.patch('os.path.getctime')
@mock.patch('ly_test_tools.environment.file_system.delete')
def test_DeleteOldest_UnsortedListDeleteOldest_DeleteCalledWithOldest(self, mock_delete, mock_ctime, mock_glob):
mock_glob.return_value = ['newest', 'old', 'newer']
mock_ctime.side_effect = [100, 0, 50]
file_system.delete_oldest('', 2, del_files=True, del_dirs=False)
mock_delete.assert_called_once_with(['old'], True, False)
class TestMakeJunction(unittest.TestCase):
def test_MakeJunction_Nondir_RaisesIOError(self):
with self.assertRaises(IOError):
file_system.make_junction('', '')
@mock.patch('os.path.isdir')
@mock.patch('sys.platform', 'linux2')
def test_MakeJunction_NoSupportPlatform_RaisesIOError(self, mock_isdir):
mock_isdir.return_value = True
with self.assertRaises(IOError):
file_system.make_junction('', '')
@mock.patch('subprocess.check_call')
@mock.patch('os.path.isdir')
@mock.patch('sys.platform', 'win32')
def test_MakeJunction_Win32_SubprocessFails_RaiseSubprocessError(self, mock_isdir, mock_sub_call):
mock_isdir.return_value = True
mock_sub_call.side_effect = subprocess.CalledProcessError(1, 'cmd', 'output')
with self.assertRaises(subprocess.CalledProcessError):
file_system.make_junction('', '')
@mock.patch('subprocess.check_output')
@mock.patch('os.path.isdir')
@mock.patch('sys.platform', 'darwin')
def test_MakeJunction_Darwin_SubprocessFails_RaiseSubprocessError(self, mock_isdir, mock_sub_call):
mock_isdir.return_value = True
mock_sub_call.side_effect = subprocess.CalledProcessError(1, 'cmd', 'output')
with self.assertRaises(subprocess.CalledProcessError):
file_system.make_junction('', '')
@mock.patch('subprocess.check_output')
@mock.patch('os.path.isdir')
@mock.patch('sys.platform', 'win32')
def test_MakeJunction_Win32_SubprocessCall_Calls(self, mock_isdir, mock_sub_call):
mock_isdir.return_value = True
src = 'source'
dest = 'destination'
file_system.make_junction(dest, src)
mock_sub_call.assert_called_once_with(['mklink', '/J', dest, src], shell=True)
@mock.patch('subprocess.check_output')
@mock.patch('os.path.isdir')
@mock.patch('sys.platform', 'darwin')
def test_MakeJunction_Darwin_SubprocessCall_Calls(self, mock_isdir, mock_sub_call):
mock_isdir.return_value = True
src = 'source'
dest = 'destination'
file_system.make_junction(dest, src)
mock_sub_call.assert_called_once_with(['ln', dest, src])
class TestFileBackup(unittest.TestCase):
def setUp(self):
self._dummy_dir = os.path.join('somewhere', 'something')
self._dummy_file = 'dummy.txt'
self._dummy_backup_file = os.path.join(self._dummy_dir, '{}.bak'.format(self._dummy_file))
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_SourceExists_BackupCreated(self, mock_path_isdir, mock_backup_exists, mock_copy):
mock_path_isdir.return_value = True
mock_backup_exists.side_effect = [True, False]
file_system.create_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_called_with(self._dummy_file, self._dummy_backup_file)
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_BackupExists_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
mock_path_isdir.return_value = True
mock_backup_exists.return_value = True
file_system.create_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_called_with(self._dummy_file, self._dummy_backup_file)
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_SourceNotExists_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
mock_path_isdir.return_value = True
mock_backup_exists.return_value = False
file_system.create_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_not_called()
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_CannotCopy_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
mock_path_isdir.return_value = True
mock_backup_exists.side_effect = [True, False]
mock_copy.side_effect = Exception('some error')
file_system.create_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_called_with(self._dummy_file, self._dummy_backup_file)
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.error')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_InvalidDir_ErrorLogged(self, mock_path_isdir, mock_backup_exists, mock_logger_error):
mock_path_isdir.return_value = False
mock_backup_exists.return_value = False
file_system.create_backup(self._dummy_file, None)
mock_logger_error.assert_called_once()
class TestFileBackupRestore(unittest.TestCase):
def setUp(self):
self._dummy_dir = os.path.join('somewhere', 'something')
self._dummy_file = 'dummy.txt'
self._dummy_backup_file = os.path.join(self._dummy_dir, '{}.bak'.format(self._dummy_file))
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_BackupRestore_Success(self, mock_path_isdir, mock_exists, mock_copy):
mock_path_isdir.return_value = True
mock_exists.return_value = True
file_system.restore_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_called_with(self._dummy_backup_file, self._dummy_file)
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_CannotCopy_WarningLogged(self, mock_path_isdir, mock_exists, mock_copy, mock_logger_warning):
mock_path_isdir.return_value = True
mock_exists.return_value = True
mock_copy.side_effect = Exception('some error')
file_system.restore_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_called_with(self._dummy_backup_file, self._dummy_file)
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_BackupNotExists_WarningLogged(self, mock_path_isdir, mock_exists, mock_copy, mock_logger_warning):
mock_path_isdir.return_value = True
mock_exists.return_value = False
file_system.restore_backup(self._dummy_file, self._dummy_dir)
mock_copy.assert_not_called()
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.error')
def test_RestoreSettings_InvalidDir_ErrorLogged(self, mock_logger_error):
file_system.restore_backup(self._dummy_file, None)
mock_logger_error.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.error')
@mock.patch('os.path.isdir')
def test_RestoreSettings_InvalidDir_ErrorLogged(self, mock_path_isdir, mock_logger_error):
mock_path_isdir.return_value = False
file_system.restore_backup(self._dummy_file, self._dummy_dir)
mock_logger_error.assert_called_once()
class TestReduceFileName(unittest.TestCase):
def test_Reduce_LongString_ReturnsReducedString(self):
target_name = 'really_long_string_that_needs_reduction' # len(mock_file_name) == 39
max_length = 25
under_test = file_system.reduce_file_name_length(target_name, max_length)
assert len(under_test) == max_length
def test_Reduce_ShortString_ReturnsSameString(self):
target_name = 'less_than_max' # len(mock_file_name) == 13
max_length = 25
under_test = file_system.reduce_file_name_length(target_name, max_length)
assert under_test == target_name
def test_Reduce_NoString_RaisesTypeError(self):
with pytest.raises(TypeError):
file_system.reduce_file_name_length(max_length=25)
def test_Reduce_NoMaxLength_RaisesTypeError(self):
target_name = 'raises_type_error'
with pytest.raises(TypeError):
file_system.reduce_file_name_length(file_name=target_name)