blob: 304c6e954dcd00646b8984ea395b9465c20a5f53 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2019 The Cobalt Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Unit tests for port_symlink.py."""
import os
import shutil
import tempfile
import unittest
from starboard.tools import port_symlink
from starboard.tools import util
_TARGET_FILENAME = 'target.txt'
# Replace this function signature for other implementations of symlink
# functions.
def MakeSymLink(*args, **kwargs):
return port_symlink.MakeSymLink(*args, **kwargs)
def IsSymLink(*args, **kwargs):
return port_symlink.IsSymLink(*args, **kwargs)
def ReadSymLink(*args, **kwargs):
return port_symlink.ReadSymLink(*args, **kwargs)
def Rmtree(*args, **kwargs):
return port_symlink.Rmtree(*args, **kwargs)
def OsWalk(*args, **kwargs):
return port_symlink.OsWalk(*args, **kwargs)
class PortSymlinkTest(unittest.TestCase):
def setUp(self):
super().setUp()
self.tmp_dir = tempfile.mkdtemp(prefix='port_symlink_')
self.target_dir = os.path.join(self.tmp_dir, 'target')
self.inner_dir = os.path.join(self.target_dir, 'inner')
self.link_dir = os.path.join(self.tmp_dir, 'link')
self.target_file = os.path.join(self.target_dir, _TARGET_FILENAME)
_MakeDirs(self.target_dir)
_MakeDirs(self.inner_dir)
with open(self.target_file, 'w', encoding='utf-8') as fd:
fd.write('hallo welt!')
MakeSymLink(self.target_dir, self.link_dir)
def tearDown(self):
Rmtree(self.tmp_dir)
super().tearDown()
def testSanity(self):
self.assertTrue(os.path.isdir(self.tmp_dir))
self.assertTrue(os.path.isdir(self.target_dir))
self.assertTrue(os.path.isdir(self.inner_dir))
def testReadSymlinkNormalPath(self):
self.assertIsNone(ReadSymLink(self.target_dir))
def testSymlinkPath(self):
self.assertTrue(os.path.exists(self.link_dir))
self.assertTrue(IsSymLink(self.link_dir))
from_dir_2 = ReadSymLink(self.link_dir)
self.assertTrue(_IsSamePath(from_dir_2, self.target_dir))
def testRelativeSymlinkPath(self):
rel_link_dir = os.path.join(self.tmp_dir, 'foo', 'rel_link')
rel_dir_path = os.path.relpath(self.target_dir, rel_link_dir)
MakeSymLink(rel_dir_path, rel_link_dir)
self.assertTrue(IsSymLink(rel_link_dir))
link_value = ReadSymLink(rel_link_dir)
self.assertIn(
'..', link_value, msg=f'Expected ".." in relative path {link_value}')
def testDelSymlink(self):
link_dir2 = os.path.join(self.tmp_dir, 'link2')
MakeSymLink(self.target_dir, link_dir2)
self.assertTrue(IsSymLink(link_dir2))
port_symlink.DelSymLink(link_dir2)
self.assertFalse(os.path.exists(link_dir2))
def testRmtreeRemovesLink(self):
Rmtree(self.link_dir)
self.assertFalse(os.path.exists(self.link_dir))
self.assertTrue(os.path.exists(self.target_dir))
def testRmtreeRemovesBrokenLink(self):
Rmtree(self.target_dir)
# os.path.exists() will return false for broken links (not true for reparse
# points on Windows) since their target does not exist. Rmtree
# implementations should still be able to remove the link.
if not port_symlink.IsWindows():
self.assertFalse(os.path.exists(self.link_dir))
self.assertTrue(IsSymLink(self.link_dir))
Rmtree(self.link_dir)
self.assertFalse(IsSymLink(self.link_dir))
def testRmtreeDoesNotFollowSymlinks(self):
"""Tests that Rmtree(...) will delete the symlink and not the target."""
external_temp_dir = tempfile.mkdtemp()
try:
external_temp_file = os.path.join(external_temp_dir, _TARGET_FILENAME)
with open(external_temp_file, 'w', encoding='utf-8') as fd:
fd.write('hallo!')
link_dir = os.path.join(self.tmp_dir, 'foo', 'link_dir')
MakeSymLink(external_temp_file, link_dir)
Rmtree(self.tmp_dir)
# The target file should still exist.
self.assertTrue(os.path.isfile(external_temp_file))
finally:
shutil.rmtree(external_temp_file, ignore_errors=True)
def testOsWalk(self):
paths_nofollow_links = _GetAllPaths(self.tmp_dir, followlinks=False)
paths_follow_links = _GetAllPaths(self.tmp_dir, followlinks=True)
print('\nOsWalk Follow links:')
for path in paths_follow_links:
print(' ' + path + ' (' + _PathTypeToString(path) + ')')
print('\nOsWalk No-Follow links:')
for path in paths_nofollow_links:
print(' ' + path + ' (' + _PathTypeToString(path) + ')')
print('')
self.assertIn(self.link_dir, paths_nofollow_links)
self.assertIn(self.link_dir, paths_follow_links)
self.assertIn(
os.path.join(self.link_dir, _TARGET_FILENAME), paths_follow_links)
self.assertNotIn(
os.path.join(self.link_dir, _TARGET_FILENAME), paths_nofollow_links)
def _MakeDirs(path):
if not os.path.isdir(path):
os.makedirs(path)
def _PathTypeToString(path):
if IsSymLink(path):
return 'link'
if os.path.isdir(path):
return 'dir'
return 'file'
def _GetAllPaths(start_dir, followlinks):
paths = []
for root, dirs, files in OsWalk(start_dir, followlinks=followlinks):
for name in files:
path = os.path.join(root, name)
paths.append(path)
for name in dirs:
path = os.path.join(root, name)
paths.append(path)
return paths
def _IsSamePath(p1, p2):
if not p1:
p1 = None
if not p2:
p2 = None
if p1 == p2:
return True
if (not p1) or (not p2):
return False
p1 = os.path.abspath(os.path.normpath(p1))
p2 = os.path.abspath(os.path.normpath(p2))
if p1 == p2:
return True
try:
return os.stat(p1) == os.stat(p2)
except Exception: # pylint: disable=broad-except
return False
if __name__ == '__main__':
util.SetupDefaultLoggingConfig()
unittest.main(verbosity=2)