Beispiel #1
0
    def test_local_folder_replaced_by_file_and_unsynced_remote_changes(self):

        # remote folder is currently not checked for unsynced changes but replaced

        os.mkdir(self.test_folder_local + "/folder")
        self.wait_for_idle()

        self.m.pause_sync()
        self.wait_for_idle()

        # replace local folder with file
        delete(self.test_folder_local + "/folder")
        shutil.copy(self.resources + "/file.txt",
                    self.test_folder_local + "/folder")

        # create remote changes
        self.m.client.upload(self.resources + "/file1.txt",
                             self.test_folder_dbx + "/folder/file.txt")

        self.m.resume_sync()
        self.wait_for_idle()

        self.assert_synced(self.test_folder_local, self.test_folder_dbx)
        self.assert_exists(self.test_folder_dbx, "folder")
        self.assert_count(self.test_folder_dbx, 1)

        # check for fatal errors
        self.assertFalse(self.m.fatal_errors)
Beispiel #2
0
    def test_folder_tree_local(self):

        # test creating tree

        shutil.copytree(self.resources + "/test_folder",
                        self.test_folder_local + "/test_folder")

        snap = DirectorySnapshot(self.resources + "/test_folder")
        num_items = len(
            list(p for p in snap.paths if not self.m.sync.is_excluded(p)))

        self.wait_for_idle(10)

        self.assert_synced(self.test_folder_local, self.test_folder_dbx)
        self.assert_count(self.test_folder_dbx, num_items)

        # test deleting tree

        delete(self.test_folder_local + "/test_folder")

        self.wait_for_idle()
        self.assert_synced(self.test_folder_local, self.test_folder_dbx)
        self.assert_count(self.test_folder_dbx, 0)

        # check for fatal errors
        self.assertFalse(self.m.fatal_errors)
Beispiel #3
0
    def test_upload_sync_issues(self):

        # paths with backslash are not allowed on Dropbox
        # we create such a local folder and assert that it triggers a sync issue

        test_path_local = self.test_folder_local + "/folder\\"
        test_path_dbx = self.test_folder_dbx + "/folder\\"

        n_errors_initial = len(self.m.sync_errors)

        os.mkdir(test_path_local)
        self.wait_for_idle()

        self.assertEqual(len(self.m.sync_errors), n_errors_initial + 1)
        self.assertEqual(self.m.sync_errors[-1]["local_path"], test_path_local)
        self.assertEqual(self.m.sync_errors[-1]["dbx_path"], test_path_dbx)
        self.assertEqual(self.m.sync_errors[-1]["type"], "PathError")

        # remove folder with invalid name and assert that sync issue is cleared

        delete(test_path_local)
        self.wait_for_idle()

        self.assertEqual(len(self.m.sync_errors), n_errors_initial)
        self.assertTrue(
            all(e["local_path"] != test_path_local
                for e in self.m.sync_errors))
        self.assertTrue(
            all(e["dbx_path"] != test_path_dbx for e in self.m.sync_errors))

        # check for fatal errors
        self.assertFalse(self.m.fatal_errors)
Beispiel #4
0
    def test_parallel_deletion_when_paused(self):

        # create a local file
        shutil.copy(self.resources + "/file.txt", self.test_folder_local)

        self.wait_for_idle()
        self.assert_synced(self.test_folder_local, self.test_folder_dbx)

        self.m.pause_sync()
        self.wait_for_idle()

        # delete local file
        delete(self.test_folder_local + "/file.txt")

        # delete remote file
        self.m.client.remove(self.test_folder_dbx + "/file.txt")

        self.m.resume_sync()
        self.wait_for_idle()

        self.assert_synced(self.test_folder_local, self.test_folder_dbx)
        self.assert_count(self.test_folder_dbx, 0)

        # check for fatal errors
        self.assertFalse(self.m.fatal_errors)
Beispiel #5
0
    def tearDown(self):

        self.observer.stop()
        self.observer.join()

        remove_configuration("test-config")
        delete(self.sync.dropbox_path)
Beispiel #6
0
def test_unix_permissions(m):
    """
    Tests that a newly downloaded file is created with default permissions for our
    process and that any locally set permissions are preserved on remote file
    modifications.
    """

    dbx_path = "/sync_tests/file"
    local_path = m.to_local_path(dbx_path)

    m.client.upload(resources + "/file.txt", dbx_path)
    wait_for_idle(m)

    # create a local file and compare its permissions to the new download
    reference_file = osp.join(get_home_dir(), "reference")

    try:
        open(reference_file, "ab").close()
        assert os.stat(local_path).st_mode == os.stat(reference_file).st_mode
    finally:
        delete(reference_file)

    # make the local file executable
    os.chmod(local_path, 0o744)
    new_mode = os.stat(local_path).st_mode  # might not be 744...
    wait_for_idle(m)

    # perform some remote modifications
    m.client.upload(resources + "/file1.txt",
                    dbx_path,
                    mode=WriteMode.overwrite)
    wait_for_idle(m)

    # check that the local permissions have not changed
    assert os.stat(local_path).st_mode == new_mode
Beispiel #7
0
def test_upload_sync_issues(m):
    """
    Tests error handling for issues during upload sync. This is done by creating a local
    folder with a name that ends with a backslash (not allowed by Dropbox).
    """

    # paths with backslash are not allowed on Dropbox
    # we create such a local folder and assert that it triggers a sync issue

    test_path_local = m.test_folder_local + "/folder\\"
    test_path_dbx = "/sync_tests/folder\\"

    os.mkdir(test_path_local)
    wait_for_idle(m)

    assert len(m.sync_errors) == 1
    assert m.sync_errors[-1]["local_path"] == test_path_local
    assert m.sync_errors[-1]["dbx_path"] == test_path_dbx
    assert m.sync_errors[-1]["type"] == "PathError"
    assert test_path_dbx in m.sync.upload_errors

    # remove folder with invalid name and assert that sync issue is cleared

    delete(test_path_local)
    wait_for_idle(m)

    assert len(m.sync_errors) == 0
    assert test_path_dbx not in m.sync.upload_errors

    # check for fatal errors
    assert not m.fatal_errors
Beispiel #8
0
def test_local_folder_replaced_by_file_and_unsynced_remote_changes(m):
    """
    Tests the upload sync when a local folder is replaced by a file and the remote
    folder has unsynced changes.
    """

    # remote folder is currently not checked for unsynced changes but replaced

    os.mkdir(m.test_folder_local + "/folder")
    wait_for_idle(m)

    m.pause_sync()
    wait_for_idle(m)

    # replace local folder with file
    delete(m.test_folder_local + "/folder")
    shutil.copy(resources + "/file.txt", m.test_folder_local + "/folder")

    # create remote changes
    m.client.upload(resources + "/file1.txt", "/sync_tests/folder/file.txt")

    m.resume_sync()
    wait_for_idle(m)

    assert_synced(m)
    assert_exists(m, "/sync_tests", "folder")
    assert_child_count(m, "/sync_tests", 1)

    # check for fatal errors
    assert not m.fatal_errors
Beispiel #9
0
def test_folder_tree_created_local(m):
    """Tests the upload sync of a nested local folder structure."""

    # test creating tree

    shutil.copytree(resources + "/test_folder",
                    m.test_folder_local + "/test_folder")

    snap = DirectorySnapshot(resources + "/test_folder")
    num_items = len(list(p for p in snap.paths if not m.sync.is_excluded(p)))

    wait_for_idle(m, 10)

    assert_synced(m)
    assert_child_count(m, "/sync_tests", num_items)

    # test deleting tree

    delete(m.test_folder_local + "/test_folder")

    wait_for_idle(m)
    assert_synced(m)
    assert_child_count(m, "/sync_tests", 0)

    # check for fatal errors
    assert not m.fatal_errors
Beispiel #10
0
def test_parallel_deletion_when_paused(m):
    """Tests parallel remote and local deletions of an item."""

    # create a local file
    shutil.copy(resources + "/file.txt", m.test_folder_local)

    wait_for_idle(m)
    assert_synced(m)

    m.pause_sync()
    wait_for_idle(m)

    # delete local file
    delete(m.test_folder_local + "/file.txt")

    # delete remote file
    m.client.remove("/sync_tests/file.txt")

    m.resume_sync()
    wait_for_idle(m)

    assert_synced(m)
    assert_child_count(m, "/sync_tests", 0)

    # check for fatal errors
    assert not m.fatal_errors
Beispiel #11
0
def test_unknown_path_encoding(m, capsys):
    """
    Tests the handling of a local path with bytes that cannot be decoded with the
    file system encoding reported by the platform.
    """

    # create a path with Python surrogate escapes and convert it to bytes
    test_path_dbx = "/sync_tests/my_folder_\udce4"
    test_path_local = m.sync.to_local_path(test_path_dbx)
    test_path_local_bytes = os.fsencode(test_path_local)

    # create the local directory while we are syncing
    os.mkdir(test_path_local_bytes)
    wait_for_idle(m)

    # 1) Check that the sync issue is logged

    # This requires that our "syncing" logic from the emitted watchdog event all the
    # way to `SyncEngine._on_local_created` can handle strings with surrogate escapes.

    assert len(m.fatal_errors) == 0
    assert len(m.sync_errors) == 1
    assert m.sync_errors[-1]["local_path"] == sanitize_string(test_path_local)
    assert m.sync_errors[-1]["dbx_path"] == sanitize_string(test_path_dbx)
    assert m.sync_errors[-1]["type"] == "PathError"
    assert test_path_dbx in m.sync.upload_errors

    # 2) Check that the sync is retried after pause / resume

    # This requires that our logic to save failed paths in our state file and retry the
    # sync on startup can handle strings with surrogate escapes.

    m.pause_sync()
    m.resume_sync()

    wait_for_idle(m)

    assert len(m.fatal_errors) == 0
    assert len(m.sync_errors) == 1
    assert m.sync_errors[-1]["local_path"] == sanitize_string(test_path_local)
    assert m.sync_errors[-1]["dbx_path"] == sanitize_string(test_path_dbx)
    assert m.sync_errors[-1]["type"] == "PathError"
    assert test_path_dbx in m.sync.upload_errors

    # 3) Check that the error is cleared when the file is deleted

    # This requires that `SyncEngine.upload_local_changes_while_inactive` can handle
    # strings with surrogate escapes all they way to `SyncEngine._on_local_deleted`.

    delete(test_path_local_bytes)  # type: ignore
    wait_for_idle(m)

    assert len(m.fatal_errors) == 0
    assert len(m.sync_errors) == 0
    assert test_path_dbx not in m.sync.upload_errors
Beispiel #12
0
def test_local_folder_replaced_by_file(m):
    """Tests the upload sync when a local folder is replaced by a file."""

    os.mkdir(m.test_folder_local + "/folder")
    wait_for_idle(m)

    with m.sync.sync_lock:

        # replace local folder with file
        delete(m.test_folder_local + "/folder")
        shutil.copy(resources + "/file.txt", m.test_folder_local + "/folder")

    wait_for_idle(m)

    assert_synced(m)
    assert osp.isfile(m.test_folder_local + "/folder")
    assert_child_count(m, "/sync_tests", 1)

    # check for fatal errors
    assert not m.fatal_errors
Beispiel #13
0
    def test_local_folder_replaced_by_file(self):

        os.mkdir(self.test_folder_local + "/folder")
        self.wait_for_idle()

        self.m.pause_sync()

        # replace local folder with file
        delete(self.test_folder_local + "/folder")
        shutil.copy(self.resources + "/file.txt",
                    self.test_folder_local + "/folder")

        self.m.resume_sync()
        self.wait_for_idle()

        self.assert_synced(self.test_folder_local, self.test_folder_dbx)
        self.assertTrue(osp.isfile(self.test_folder_local + "/folder"))
        self.assert_count(self.test_folder_dbx, 1)

        # check for fatal errors
        self.assertFalse(self.m.fatal_errors)
Beispiel #14
0
def sync():
    syncing = Event()
    startup = Event()
    syncing.set()

    local_dir = osp.join(get_home_dir(), "dummy_dir")
    os.mkdir(local_dir)

    sync = SyncEngine(DropboxClient("test-config"),
                      FSEventHandler(syncing, startup))

    sync.dropbox_path = local_dir

    observer = Observer()
    observer.schedule(sync.fs_events, sync.dropbox_path, recursive=True)
    observer.start()

    yield sync

    observer.stop()
    observer.join()

    remove_configuration("test-config")
    delete(sync.dropbox_path)
Beispiel #15
0
 def clean_local(self):
     """Recreates a fresh test folder locally."""
     delete(self.m.dropbox_path + "/.mignore")
     delete(self.test_folder_local)
     os.mkdir(self.test_folder_local)