maestral/tests/offline/test_clean_local_events.py
2024-07-27 11:02:34 +02:00

269 lines
7.9 KiB
Python

import pytest
from watchdog.events import (
DirCreatedEvent,
DirDeletedEvent,
DirMovedEvent,
FileCreatedEvent,
FileDeletedEvent,
FileModifiedEvent,
FileMovedEvent,
)
from maestral.client import DropboxClient
from maestral.config import remove_configuration
from maestral.keyring import CredentialStorage
from maestral.sync import SyncEngine
@pytest.fixture
def sync():
sync = SyncEngine(DropboxClient("test-config", CredentialStorage("test-config")))
sync.dropbox_path = "/"
yield sync
remove_configuration("test-config")
def ipath(i):
"""Returns path names '/test 1', '/test 2', ..."""
return f"/test {i}"
def test_single_file_events(sync: SyncEngine) -> None:
# only a single event for every path -> no consolidation
file_events = [
FileModifiedEvent(ipath(1)),
FileCreatedEvent(ipath(2)),
FileDeletedEvent(ipath(3)),
FileMovedEvent(ipath(4), ipath(5)),
]
res = [
FileModifiedEvent(ipath(1)),
FileCreatedEvent(ipath(2)),
FileDeletedEvent(ipath(3)),
FileMovedEvent(ipath(4), ipath(5)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_single_path_cases(sync: SyncEngine) -> None:
file_events = [
# created + deleted -> None
FileCreatedEvent(ipath(1)),
FileDeletedEvent(ipath(1)),
# deleted + created -> modified
FileDeletedEvent(ipath(2)),
FileCreatedEvent(ipath(2)),
# created + modified -> created
FileCreatedEvent(ipath(3)),
FileModifiedEvent(ipath(3)),
]
res = [
# created + deleted -> None
# deleted + created -> modified
FileModifiedEvent(ipath(2)),
# created + modified -> created
FileCreatedEvent(ipath(3)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_move_events(sync: SyncEngine) -> None:
file_events = [
# created + moved -> created
FileCreatedEvent(ipath(1)),
FileMovedEvent(ipath(1), ipath(2)),
# moved + deleted -> deleted
FileMovedEvent(ipath(3), ipath(4)),
FileDeletedEvent(ipath(4)),
# moved + moved back -> modified
FileMovedEvent(ipath(5), ipath(6)),
FileMovedEvent(ipath(6), ipath(5)),
# moved + moved -> deleted + created
# (this is currently not handled as a single moved)
FileMovedEvent(ipath(7), ipath(8)),
FileMovedEvent(ipath(8), ipath(9)),
]
res = [
# created + moved -> created
FileCreatedEvent(ipath(2)),
# moved + deleted -> deleted
FileDeletedEvent(ipath(3)),
# moved + moved back -> modified
FileModifiedEvent(ipath(5)),
# moved + moved -> deleted + created
# (this is currently not handled as a single moved)
FileDeletedEvent(ipath(7)),
FileCreatedEvent(ipath(9)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_gedit_save(sync: SyncEngine) -> None:
file_events = [
FileCreatedEvent("/.gedit-save-UR4EC0"), # save new version to tmp file
FileModifiedEvent("/.gedit-save-UR4EC0"), # modify tmp file
FileMovedEvent(ipath(1), ipath(1) + "~"), # move old version to backup
FileMovedEvent("/.gedit-save-UR4EC0", ipath(1)), # replace old version with tmp
]
res = [
FileModifiedEvent(ipath(1)), # modified file
FileCreatedEvent(ipath(1) + "~"), # backup
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_macos_safe_save(sync: SyncEngine) -> None:
file_events = [
FileMovedEvent(ipath(1), ipath(1) + ".sb-b78ef837-dLht38"), # move to backup
FileCreatedEvent(ipath(1)), # create new version
FileDeletedEvent(ipath(1) + ".sb-b78ef837-dLht38"), # delete backup
]
res = [
FileModifiedEvent(ipath(1)), # modified file
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_msoffice_created(sync: SyncEngine) -> None:
file_events = [
FileCreatedEvent(ipath(1)),
FileDeletedEvent(ipath(1)),
FileCreatedEvent(ipath(1)),
FileCreatedEvent("/~$" + ipath(1)),
]
res = [
FileCreatedEvent(ipath(1)), # created file
FileCreatedEvent("/~$" + ipath(1)), # backup
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_type_changes(sync: SyncEngine) -> None:
file_events = [
# keep as is
FileDeletedEvent(ipath(1)),
DirCreatedEvent(ipath(1)),
# keep as is
DirDeletedEvent(ipath(2)),
FileCreatedEvent(ipath(2)),
]
res = [
# keep as is
FileDeletedEvent(ipath(1)),
DirCreatedEvent(ipath(1)),
# keep as is
DirDeletedEvent(ipath(2)),
FileCreatedEvent(ipath(2)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_type_changes_difficult(sync: SyncEngine) -> None:
file_events = [
# convert to FileDeleted -> DirCreated
FileModifiedEvent(ipath(1)),
FileDeletedEvent(ipath(1)),
FileCreatedEvent(ipath(1)),
FileDeletedEvent(ipath(1)),
DirCreatedEvent(ipath(1)),
# convert to FileDeleted(path1) -> DirCreated(path2)
FileModifiedEvent(ipath(2)),
FileDeletedEvent(ipath(2)),
FileCreatedEvent(ipath(2)),
FileDeletedEvent(ipath(2)),
DirCreatedEvent(ipath(2)),
DirMovedEvent(ipath(2), ipath(3)),
]
res = [
FileDeletedEvent(ipath(1)),
DirCreatedEvent(ipath(1)),
FileDeletedEvent(ipath(2)),
DirCreatedEvent(ipath(3)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
def test_nested_events(sync: SyncEngine) -> None:
file_events = [
# convert to a single DirDeleted
DirDeletedEvent(ipath(1)),
FileDeletedEvent(ipath(1) + "/file1.txt"),
FileDeletedEvent(ipath(1) + "/file2.txt"),
DirDeletedEvent(ipath(1) + "/sub"),
FileDeletedEvent(ipath(1) + "/sub/file3.txt"),
# convert to a single DirMoved
DirMovedEvent(ipath(2), ipath(3)),
FileMovedEvent(ipath(2) + "/file1.txt", ipath(3) + "/file1.txt"),
FileMovedEvent(ipath(2) + "/file2.txt", ipath(3) + "/file2.txt"),
DirMovedEvent(ipath(2) + "/sub", ipath(3) + "/sub"),
FileMovedEvent(ipath(2) + "/sub/file3.txt", ipath(3) + "/sub/file3.txt"),
]
res = [
DirDeletedEvent(ipath(1)),
DirMovedEvent(ipath(2), ipath(3)),
]
cleaned_events = sync._clean_local_events(file_events)
assert cleaned_events == res
@pytest.mark.benchmark(
group="local-event-processing",
min_time=0.1,
max_time=5,
)
def test_performance(sync: SyncEngine, benchmark) -> None:
# 10,000 nested deleted events (5,000 folders, 5,000 files)
file_events = [DirDeletedEvent(n * ipath(1)) for n in range(1, 5001)]
file_events += [FileDeletedEvent(n * ipath(1) + ".txt") for n in range(1, 5001)]
# 10,000 nested moved events (5,000 folders, 5,000 files)
file_events += [DirMovedEvent(n * ipath(2), n * ipath(3)) for n in range(1, 5001)]
file_events += [
FileMovedEvent(n * ipath(2) + ".txt", n * ipath(3) + ".txt")
for n in range(1, 5001)
]
# 4,995 unrelated created events
file_events += [FileCreatedEvent(ipath(n)) for n in range(5, 5001)]
res = [
DirDeletedEvent(ipath(1)),
FileDeletedEvent(ipath(1) + ".txt"),
DirMovedEvent(ipath(2), ipath(3)),
FileMovedEvent(ipath(2) + ".txt", ipath(3) + ".txt"),
]
res += [FileCreatedEvent(ipath(n)) for n in range(5, 5001)]
cleaned_events = benchmark(sync._clean_local_events, file_events)
assert cleaned_events == res