2017-07-18 16:31:01 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
2018-05-10 19:51:51 +00:00
|
|
|
Unit Tests for functions located in salt/utils/files.py
|
2017-07-18 16:31:01 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
# Import python libs
|
2018-01-13 00:10:52 +00:00
|
|
|
from __future__ import absolute_import, unicode_literals, print_function
|
2018-05-10 19:51:51 +00:00
|
|
|
import copy
|
2017-07-18 16:31:01 +00:00
|
|
|
import os
|
|
|
|
|
|
|
|
# Import Salt libs
|
|
|
|
import salt.utils.files
|
2018-02-28 00:13:08 +00:00
|
|
|
from salt.ext import six
|
2017-07-18 16:31:01 +00:00
|
|
|
|
|
|
|
# Import Salt Testing libs
|
2018-04-14 02:41:02 +00:00
|
|
|
from tests.support.helpers import with_tempdir
|
2017-07-18 16:31:01 +00:00
|
|
|
from tests.support.unit import TestCase, skipIf
|
|
|
|
from tests.support.mock import (
|
|
|
|
patch,
|
|
|
|
NO_MOCK,
|
|
|
|
NO_MOCK_REASON
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-05-10 19:51:51 +00:00
|
|
|
class FilesTestCase(TestCase):
|
2017-07-18 16:31:01 +00:00
|
|
|
'''
|
|
|
|
Test case for files util.
|
|
|
|
'''
|
|
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
|
|
def test_safe_rm(self):
|
|
|
|
with patch('os.remove') as os_remove_mock:
|
|
|
|
salt.utils.files.safe_rm('dummy_tgt')
|
|
|
|
self.assertTrue(os_remove_mock.called)
|
|
|
|
|
|
|
|
@skipIf(os.path.exists('/tmp/no_way_this_is_a_file_nope.sh'), 'Test file exists! Skipping safe_rm_exceptions test!')
|
|
|
|
def test_safe_rm_exceptions(self):
|
|
|
|
error = False
|
|
|
|
try:
|
|
|
|
salt.utils.files.safe_rm('/tmp/no_way_this_is_a_file_nope.sh')
|
|
|
|
except (IOError, OSError):
|
|
|
|
error = True
|
|
|
|
self.assertFalse(error, 'salt.utils.files.safe_rm raised exception when it should not have')
|
2017-10-14 01:52:56 +00:00
|
|
|
|
2018-04-14 02:41:02 +00:00
|
|
|
@with_tempdir()
|
|
|
|
def test_safe_walk_symlink_recursion(self, tmp):
|
|
|
|
if os.stat(tmp).st_ino == 0:
|
|
|
|
self.skipTest('inodes not supported in {0}'.format(tmp))
|
|
|
|
os.mkdir(os.path.join(tmp, 'fax'))
|
|
|
|
os.makedirs(os.path.join(tmp, 'foo/bar'))
|
|
|
|
os.symlink('../..', os.path.join(tmp, 'foo/bar/baz'))
|
|
|
|
os.symlink('foo', os.path.join(tmp, 'root'))
|
|
|
|
expected = [
|
|
|
|
(os.path.join(tmp, 'root'), ['bar'], []),
|
|
|
|
(os.path.join(tmp, 'root/bar'), ['baz'], []),
|
|
|
|
(os.path.join(tmp, 'root/bar/baz'), ['fax', 'foo', 'root'], []),
|
|
|
|
(os.path.join(tmp, 'root/bar/baz/fax'), [], []),
|
|
|
|
]
|
|
|
|
paths = []
|
|
|
|
for root, dirs, names in salt.utils.files.safe_walk(os.path.join(tmp, 'root')):
|
|
|
|
paths.append((root, sorted(dirs), names))
|
|
|
|
if paths != expected:
|
|
|
|
raise AssertionError(
|
|
|
|
'\n'.join(
|
|
|
|
['got:'] + [repr(p) for p in paths] +
|
|
|
|
['', 'expected:'] + [repr(p) for p in expected]
|
2017-10-14 01:52:56 +00:00
|
|
|
)
|
2018-04-14 02:41:02 +00:00
|
|
|
)
|
2018-02-28 00:13:08 +00:00
|
|
|
|
|
|
|
@skipIf(not six.PY3, 'This test only applies to Python 3')
|
|
|
|
def test_fopen_with_disallowed_fds(self):
|
|
|
|
'''
|
|
|
|
This is safe to have as a unit test since we aren't going to actually
|
|
|
|
try to read or write. We want to ensure that we are raising a
|
|
|
|
TypeError. Python 3's open() builtin will treat the booleans as file
|
|
|
|
descriptor numbers and try to open stdin/stdout. We also want to test
|
|
|
|
fd 2 which is stderr.
|
|
|
|
'''
|
|
|
|
for invalid_fn in (False, True, 0, 1, 2):
|
|
|
|
try:
|
|
|
|
with salt.utils.files.fopen(invalid_fn):
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
# This is expected. We aren't using an assertRaises here
|
|
|
|
# because we want to ensure that if we did somehow open the
|
|
|
|
# filehandle, that it doesn't remain open.
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
# We probably won't even get this far if we actually opened
|
|
|
|
# stdin/stdout as a file descriptor. It is likely to cause the
|
|
|
|
# integration suite to die since, news flash, closing
|
|
|
|
# stdin/stdout/stderr is usually not a wise thing to do in the
|
|
|
|
# middle of a program's execution.
|
|
|
|
self.fail(
|
|
|
|
'fopen() should have been prevented from opening a file '
|
|
|
|
'using {0} as the filename'.format(invalid_fn)
|
|
|
|
)
|
2018-05-10 19:51:51 +00:00
|
|
|
|
|
|
|
def _create_temp_structure(self, temp_directory, structure):
|
|
|
|
for folder, files in six.iteritems(structure):
|
|
|
|
current_directory = os.path.join(temp_directory, folder)
|
|
|
|
os.makedirs(current_directory)
|
|
|
|
for name, content in six.iteritems(files):
|
|
|
|
path = os.path.join(temp_directory, folder, name)
|
|
|
|
with salt.utils.files.fopen(path, 'w+') as fh:
|
|
|
|
fh.write(content)
|
|
|
|
|
|
|
|
def _validate_folder_structure_and_contents(self, target_directory,
|
|
|
|
desired_structure):
|
|
|
|
for folder, files in six.iteritems(desired_structure):
|
|
|
|
for name, content in six.iteritems(files):
|
|
|
|
path = os.path.join(target_directory, folder, name)
|
|
|
|
with salt.utils.files.fopen(path) as fh:
|
|
|
|
assert fh.read().strip() == content
|
|
|
|
|
|
|
|
@with_tempdir()
|
|
|
|
@with_tempdir()
|
|
|
|
def test_recursive_copy(self, src, dest):
|
|
|
|
src_structure = {
|
|
|
|
'foo': {
|
|
|
|
'foofile.txt': 'fooSTRUCTURE'
|
|
|
|
},
|
|
|
|
'bar': {
|
|
|
|
'barfile.txt': 'barSTRUCTURE'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dest_structure = {
|
|
|
|
'foo': {
|
|
|
|
'foo.txt': 'fooTARGET_STRUCTURE'
|
|
|
|
},
|
|
|
|
'baz': {
|
|
|
|
'baz.txt': 'bazTARGET_STRUCTURE'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Create the file structures in both src and dest dirs
|
|
|
|
self._create_temp_structure(src, src_structure)
|
|
|
|
self._create_temp_structure(dest, dest_structure)
|
|
|
|
|
|
|
|
# Perform the recursive copy
|
|
|
|
salt.utils.files.recursive_copy(src, dest)
|
|
|
|
|
|
|
|
# Confirm results match expected results
|
|
|
|
desired_structure = copy.copy(dest_structure)
|
|
|
|
desired_structure.update(src_structure)
|
|
|
|
self._validate_folder_structure_and_contents(
|
|
|
|
dest,
|
|
|
|
desired_structure)
|