# -*- coding: utf-8 -*- ''' Unit Tests for functions located in salt/utils/files.py ''' # Import python libs from __future__ import absolute_import, unicode_literals, print_function import copy import os # Import Salt libs import salt.utils.files from salt.ext import six # Import Salt Testing libs from tests.support.helpers import with_tempdir from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, NO_MOCK, NO_MOCK_REASON ) class FilesTestCase(TestCase): ''' 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') @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('..', '..'), 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] ) ) @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) ) 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)