tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_move_remove.py (8984B)


      1 #!/usr/bin/env python
      2 
      3 import errno
      4 import os
      5 import shutil
      6 import stat
      7 import threading
      8 import time
      9 import unittest
     10 from contextlib import contextmanager
     11 
     12 import mozfile
     13 import mozinfo
     14 import mozunit
     15 import stubs
     16 
     17 
     18 def mark_readonly(path):
     19    """Removes all write permissions from given file/directory.
     20 
     21    :param path: path of directory/file of which modes must be changed
     22    """
     23    mode = os.stat(path)[stat.ST_MODE]
     24    os.chmod(path, mode & ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH)
     25 
     26 
     27 class FileOpenCloseThread(threading.Thread):
     28    """Helper thread for asynchronous file handling"""
     29 
     30    def __init__(self, path, delay, delete=False):
     31        threading.Thread.__init__(self)
     32        self.file_opened = threading.Event()
     33        self.delay = delay
     34        self.path = path
     35        self.delete = delete
     36 
     37    def run(self):
     38        with open(self.path):
     39            self.file_opened.set()
     40            time.sleep(self.delay)
     41        if self.delete:
     42            try:
     43                os.remove(self.path)
     44            except Exception:
     45                pass
     46 
     47 
     48 @contextmanager
     49 def wait_file_opened_in_thread(*args, **kwargs):
     50    thread = FileOpenCloseThread(*args, **kwargs)
     51    thread.start()
     52    thread.file_opened.wait()
     53    try:
     54        yield thread
     55    finally:
     56        thread.join()
     57 
     58 
     59 class MozfileRemoveTestCase(unittest.TestCase):
     60    """Test our ability to remove directories and files"""
     61 
     62    def setUp(self):
     63        # Generate a stub
     64        self.tempdir = stubs.create_stub()
     65 
     66    def tearDown(self):
     67        if os.path.isdir(self.tempdir):
     68            shutil.rmtree(self.tempdir)
     69 
     70    def test_remove_directory(self):
     71        """Test the removal of a directory"""
     72        self.assertTrue(os.path.isdir(self.tempdir))
     73        mozfile.remove(self.tempdir)
     74        self.assertFalse(os.path.exists(self.tempdir))
     75 
     76    def test_remove_directory_with_open_file(self):
     77        """Test removing a directory with an open file"""
     78        # Open a file in the generated stub
     79        filepath = os.path.join(self.tempdir, *stubs.files[1])
     80        f = open(filepath, "w")
     81        f.write("foo-bar")
     82 
     83        # keep file open and then try removing the dir-tree
     84        if mozinfo.isWin:
     85            # On the Windows family WindowsError should be raised.
     86            self.assertRaises(OSError, mozfile.remove, self.tempdir)
     87            self.assertTrue(os.path.exists(self.tempdir))
     88        else:
     89            # Folder should be deleted on all other platforms
     90            mozfile.remove(self.tempdir)
     91            self.assertFalse(os.path.exists(self.tempdir))
     92 
     93    def test_remove_closed_file(self):
     94        """Test removing a closed file"""
     95        # Open a file in the generated stub
     96        filepath = os.path.join(self.tempdir, *stubs.files[1])
     97        with open(filepath, "w") as f:
     98            f.write("foo-bar")
     99 
    100        # Folder should be deleted on all platforms
    101        mozfile.remove(self.tempdir)
    102        self.assertFalse(os.path.exists(self.tempdir))
    103 
    104    def test_removing_open_file_with_retry(self):
    105        """Test removing a file in use with retry"""
    106        filepath = os.path.join(self.tempdir, *stubs.files[1])
    107 
    108        with wait_file_opened_in_thread(filepath, 0.2):
    109            # on windows first attempt will fail,
    110            # and it will be retried until the thread leave the handle
    111            mozfile.remove(filepath)
    112 
    113        # Check deletion was successful
    114        self.assertFalse(os.path.exists(filepath))
    115 
    116    def test_removing_already_deleted_file_with_retry(self):
    117        """Test removing a meanwhile removed file with retry"""
    118        filepath = os.path.join(self.tempdir, *stubs.files[1])
    119 
    120        with wait_file_opened_in_thread(filepath, 0.2, True):
    121            # on windows first attempt will fail, and before
    122            # the retry the opened file will be deleted in the thread
    123            mozfile.remove(filepath)
    124 
    125        # Check deletion was successful
    126        self.assertFalse(os.path.exists(filepath))
    127 
    128    def test_remove_readonly_tree(self):
    129        """Test removing a read-only directory"""
    130 
    131        dirpath = os.path.join(self.tempdir, "nested_tree")
    132        mark_readonly(dirpath)
    133 
    134        # However, mozfile should change write permissions and remove dir.
    135        mozfile.remove(dirpath)
    136 
    137        self.assertFalse(os.path.exists(dirpath))
    138 
    139    def test_remove_readonly_file(self):
    140        """Test removing read-only files"""
    141        filepath = os.path.join(self.tempdir, *stubs.files[1])
    142        mark_readonly(filepath)
    143 
    144        # However, mozfile should change write permission and then remove file.
    145        mozfile.remove(filepath)
    146 
    147        self.assertFalse(os.path.exists(filepath))
    148 
    149    @unittest.skipIf(mozinfo.isWin, "Symlinks are not supported on Windows")
    150    def test_remove_symlink(self):
    151        """Test removing a symlink"""
    152        file_path = os.path.join(self.tempdir, *stubs.files[1])
    153        symlink_path = os.path.join(self.tempdir, "symlink")
    154 
    155        os.symlink(file_path, symlink_path)
    156        self.assertTrue(os.path.islink(symlink_path))
    157 
    158        # The linked folder and files should not be deleted
    159        mozfile.remove(symlink_path)
    160        self.assertFalse(os.path.exists(symlink_path))
    161        self.assertTrue(os.path.exists(file_path))
    162 
    163    @unittest.skipIf(mozinfo.isWin, "Symlinks are not supported on Windows")
    164    def test_remove_symlink_in_subfolder(self):
    165        """Test removing a folder with an contained symlink"""
    166        file_path = os.path.join(self.tempdir, *stubs.files[0])
    167        dir_path = os.path.dirname(os.path.join(self.tempdir, *stubs.files[1]))
    168        symlink_path = os.path.join(dir_path, "symlink")
    169 
    170        os.symlink(file_path, symlink_path)
    171        self.assertTrue(os.path.islink(symlink_path))
    172 
    173        # The folder with the contained symlink will be deleted but not the
    174        # original linked file
    175        mozfile.remove(dir_path)
    176        self.assertFalse(os.path.exists(dir_path))
    177        self.assertFalse(os.path.exists(symlink_path))
    178        self.assertTrue(os.path.exists(file_path))
    179 
    180    @unittest.skipIf(mozinfo.isWin, "Symlinks are not supported on Windows")
    181    def test_remove_broken_symlink(self):
    182        """Test removing a folder with an contained symlink"""
    183        file_path = os.path.join(self.tempdir, "readonly.txt")
    184        working_link = os.path.join(self.tempdir, "link_to_readonly.txt")
    185        broken_link = os.path.join(self.tempdir, "broken_link")
    186        os.symlink(file_path, working_link)
    187        os.symlink(os.path.join(self.tempdir, "broken.txt"), broken_link)
    188 
    189        self.assertTrue(os.path.exists(file_path))
    190        self.assertTrue(os.path.islink(working_link))
    191        self.assertTrue(os.path.islink(broken_link))
    192 
    193        mozfile.remove(working_link)
    194        self.assertFalse(os.path.lexists(working_link))
    195        self.assertTrue(os.path.exists(file_path))
    196 
    197        mozfile.remove(broken_link)
    198        self.assertFalse(os.path.lexists(broken_link))
    199 
    200    @unittest.skipIf(
    201        mozinfo.isWin or not os.geteuid(),
    202        "Symlinks are not supported on Windows and cannot run test as root",
    203    )
    204    def test_remove_symlink_for_system_path(self):
    205        """Test removing a symlink which points to a system folder"""
    206        symlink_path = os.path.join(self.tempdir, "symlink")
    207 
    208        os.symlink(os.path.dirname(self.tempdir), symlink_path)
    209        self.assertTrue(os.path.islink(symlink_path))
    210 
    211        # The folder with the contained symlink will be deleted but not the
    212        # original linked file
    213        mozfile.remove(symlink_path)
    214        self.assertFalse(os.path.exists(symlink_path))
    215 
    216    def test_remove_path_that_does_not_exists(self):
    217        not_existing_path = os.path.join(self.tempdir, "I_do_not_not_exists")
    218        try:
    219            mozfile.remove(not_existing_path)
    220        except OSError as exc:
    221            if exc.errno == errno.ENOENT:
    222                self.fail("removing non existing path must not raise error")
    223            raise
    224 
    225 
    226 class MozFileMoveTestCase(unittest.TestCase):
    227    def setUp(self):
    228        # Generate a stub
    229        self.tempdir = stubs.create_stub()
    230        self.addCleanup(mozfile.rmtree, self.tempdir)
    231 
    232    def test_move_file(self):
    233        file_path = os.path.join(self.tempdir, *stubs.files[1])
    234        moved_path = file_path + ".moved"
    235        self.assertTrue(os.path.isfile(file_path))
    236        self.assertFalse(os.path.exists(moved_path))
    237        mozfile.move(file_path, moved_path)
    238        self.assertFalse(os.path.exists(file_path))
    239        self.assertTrue(os.path.isfile(moved_path))
    240 
    241    def test_move_file_with_retry(self):
    242        file_path = os.path.join(self.tempdir, *stubs.files[1])
    243        moved_path = file_path + ".moved"
    244 
    245        with wait_file_opened_in_thread(file_path, 0.2):
    246            # first move attempt should fail on windows and be retried
    247            mozfile.move(file_path, moved_path)
    248            self.assertFalse(os.path.exists(file_path))
    249            self.assertTrue(os.path.isfile(moved_path))
    250 
    251 
    252 if __name__ == "__main__":
    253    mozunit.main()