commit 201bfb4363f0747c5f0fa97abd41828c9420154b
Author: Mickaël Schoentgen <mschoentgen@nuxeo.com>
Date:   Mon Oct 19 16:42:10 2020 +0200

    NXDRIVE-2319: Move LocalClient tests from old_functional to unit

diff --git a/docs/changes/4.4.6.md b/docs/changes/4.4.6.md
index 72313d3b..c5cdc094 100644
--- a/docs/changes/4.4.6.md
+++ b/docs/changes/4.4.6.md
@@ -86,6 +86,8 @@ Release date: `2020-xx-xx`
 - Added `EngineDAO.get_session_uploads()`
 - Added `EngineDAO.pause_session()`
 - Added `EngineDAO.sessionUpdated`
+- Added `check` keyword argument to `LocalClientMixin.is_equal_digests()`
+- Added `check` keyword argument to `LocalClientMixin.try_get_info()`
 - Added `Options.disallowed_types_for_dt`
 - Added `Session.completed_on`
 - Added `Session.created_on`
diff --git a/nxdrive/client/local/base.py b/nxdrive/client/local/base.py
index 2c592a28..bfa3936e 100644
--- a/nxdrive/client/local/base.py
+++ b/nxdrive/client/local/base.py
@@ -258,7 +258,7 @@ class LocalClientMixin:
         # uid = str(stat_info.st_ino)
         return FileInfo(
             self.base_folder,
-            ref,
+            self.get_path(os_path),
             folderish,
             mtime,
             digest_func=self._digest_func,
@@ -267,9 +267,9 @@ class LocalClientMixin:
             size=size,
         )
 
-    def try_get_info(self, ref: Path) -> Optional[FileInfo]:
+    def try_get_info(self, ref: Path, check: bool = True) -> Optional[FileInfo]:
         try:
-            return self.get_info(ref)
+            return self.get_info(ref, check=check)
         except NotFound:
             return None
 
@@ -279,6 +279,7 @@ class LocalClientMixin:
         remote_digest: Optional[str],
         local_path: Path,
         remote_digest_algorithm: str = None,
+        check: bool = True,
     ) -> bool:
         """
         Compare 2 document's digests.
@@ -301,7 +302,7 @@ class LocalClientMixin:
             if not remote_digest_algorithm:
                 raise UnknownDigest(str(remote_digest))
 
-        file_info = self.try_get_info(local_path)
+        file_info = self.try_get_info(local_path, check=check)
         if not file_info:
             return False
         digest = file_info.get_digest(digest_func=remote_digest_algorithm)
diff --git a/tests/old_functional/test_local_client.py b/tests/old_functional/test_local_client.py
index f981ea58..af683d9b 100644
--- a/tests/old_functional/test_local_client.py
+++ b/tests/old_functional/test_local_client.py
@@ -5,14 +5,12 @@ See local_client_darwin.py and local_client_windows.py for more information.
 
 See NXDRIVE-742.
 """
-import hashlib
 import os
-from pathlib import Path
 
 import pytest
 
-from nxdrive.constants import ROOT, WINDOWS
-from nxdrive.exceptions import DuplicationDisabledError, NotFound
+from nxdrive.constants import WINDOWS
+from nxdrive.exceptions import DuplicationDisabledError
 
 from ..markers import not_linux, windows_only
 from ..utils import random_png
@@ -23,11 +21,6 @@ if WINDOWS:
     import win32api
 
 
-EMPTY_DIGEST = hashlib.md5().hexdigest()
-SOME_TEXT_CONTENT = b"Some text content."
-SOME_TEXT_DIGEST = hashlib.md5(SOME_TEXT_CONTENT).hexdigest()
-
-
 class StubLocalClient:
     """
     All tests goes here. If you need to implement a special behavior for
@@ -40,101 +33,6 @@ class StubLocalClient:
         self.engine_1.start()
         self.wait_sync(wait_for_async=True)
 
-    def test_make_documents(self):
-        local = self.local_1
-        doc_1 = local.make_file("/", "Document 1.txt")
-        assert local.exists(doc_1)
-        assert not local.get_content(doc_1)
-        doc_1_info = local.get_info(doc_1)
-        assert doc_1_info.name == "Document 1.txt"
-        assert doc_1_info.path == doc_1
-        assert doc_1_info.get_digest() == EMPTY_DIGEST
-        assert not doc_1_info.folderish
-        assert doc_1_info.size == 0
-
-        doc_2 = local.make_file("/", "Document 2.txt", content=SOME_TEXT_CONTENT)
-        assert local.exists(doc_2)
-        assert local.get_content(doc_2) == SOME_TEXT_CONTENT
-        doc_2_info = local.get_info(doc_2)
-        assert doc_2_info.name == "Document 2.txt"
-        assert doc_2_info.path == doc_2
-        assert doc_2_info.get_digest() == SOME_TEXT_DIGEST
-        assert not doc_2_info.folderish
-        assert doc_2_info.size > 0
-
-        local.delete(doc_2)
-        assert local.exists(doc_1)
-        assert not local.exists(doc_2)
-
-        folder_1 = local.make_folder("/", "A new folder")
-        assert local.exists(folder_1)
-        folder_1_info = local.get_info(folder_1)
-        assert folder_1_info.name == "A new folder"
-        assert folder_1_info.path == folder_1
-        assert folder_1_info.folderish
-        # A folder has no size
-        assert folder_1_info.size == 0
-
-        doc_3 = local.make_file(folder_1, "Document 3.txt", content=SOME_TEXT_CONTENT)
-        local.delete(folder_1)
-        assert not local.exists(folder_1)
-        assert not local.exists(doc_3)
-
-    def test_get_info_invalid_date(self):
-        local = self.local_1
-        doc_1 = local.make_file("/", "Document 1.txt")
-        os.utime(local.abspath("/Document 1.txt"), (0, 999999999999999))
-        doc_1_info = local.get_info(doc_1)
-        assert doc_1_info.name == "Document 1.txt"
-        assert doc_1_info.path == doc_1
-        assert doc_1_info.get_digest() == EMPTY_DIGEST
-        assert not doc_1_info.folderish
-
-    def test_complex_filenames(self):
-        local = self.local_1
-        # create another folder with the same title
-        title_with_accents = "\xc7a c'est l'\xe9t\xe9 !"
-
-        folder_1 = local.make_folder("/", title_with_accents)
-        folder_1_info = local.get_info(folder_1)
-        assert folder_1_info.name == title_with_accents
-
-        # create another folder with the same title
-        with pytest.raises(DuplicationDisabledError):
-            local.make_folder("/", title_with_accents)
-
-        # Create a long file name with weird chars
-        long_filename = "\xe9" * 50 + "%$#!()[]{}+_-=';&^" + ".doc"
-        file_1 = local.make_file(folder_1, long_filename)
-        file_1 = local.get_info(file_1)
-        assert file_1.name == long_filename
-        assert file_1.path == folder_1_info.path / long_filename
-
-        # Create a file with invalid chars
-        invalid_filename = 'a/b\\c*d:e<f>g?h"i|j.doc'
-        escaped_filename = "a-b-c-d-e-f-g-h-i-j.doc"
-        file_2 = local.make_file(folder_1, invalid_filename)
-        file_2 = local.get_info(file_2)
-        assert file_2.name == escaped_filename
-        assert file_2.path == folder_1_info.path / escaped_filename
-
-    def test_missing_file(self):
-        with pytest.raises(NotFound):
-            self.local_1.get_info("/Something Missing")
-
-    @pytest.mark.timeout(30)
-    def test_case_sensitivity(self):
-        local = self.local_1
-        sensitive = local.is_case_sensitive()
-
-        local.make_file("/", "abc.txt")
-        if sensitive:
-            local.make_file("/", "ABC.txt")
-        else:
-            with pytest.raises(DuplicationDisabledError):
-                local.make_file("/", "ABC.txt")
-        assert len(local.get_children_info("/")) == sensitive + 1
-
     @windows_only
     def test_windows_short_names(self):
         """
@@ -164,117 +62,6 @@ class StubLocalClient:
         assert len(children) == 1
         assert children[0].name == long_name
 
-    def test_get_children_info(self):
-        local = self.local_1
-        folder_1 = local.make_folder("/", "Folder 1")
-        folder_2 = local.make_folder("/", "Folder 2")
-        file_1 = local.make_file("/", "File 1.txt", content=b"foo\n")
-
-        # not a direct child of '/'
-        local.make_file(folder_1, "File 2.txt", content=b"bar\n")
-
-        # ignored files
-        data = b"baz\n"
-        local.make_file("/", ".File 2.txt", content=data)
-        local.make_file("/", "~$File 2.txt", content=data)
-        local.make_file("/", "File 2.txt~", content=data)
-        local.make_file("/", "File 2.txt.swp", content=data)
-        local.make_file("/", "File 2.txt.lock", content=data)
-        local.make_file("/", "File 2.txt.part", content=data)
-        if local.is_case_sensitive():
-            local.make_file("/", "File 2.txt.LOCK", content=data)
-        else:
-            with pytest.raises(DuplicationDisabledError):
-                local.make_file("/", "File 2.txt.LOCK", content=data)
-
-        workspace_children = local.get_children_info("/")
-        assert len(workspace_children) == 3
-        assert workspace_children[0].path == file_1
-        assert workspace_children[1].path == folder_1
-        assert workspace_children[2].path == folder_2
-
-    def test_deep_folders(self):
-        # Check that local client can workaround the default Windows
-        # MAX_PATH limit
-        local = self.local_1
-        folder = "/"
-        for _i in range(30):
-            folder = local.make_folder(folder, "0123456789")
-
-        # Last Level
-        last_level_folder_info = local.get_info(folder)
-        assert last_level_folder_info.path == Path("0123456789/" * 30)
-
-        # Create a nested file
-        deep_file = local.make_file(folder, "File.txt", content=b"Some Content.")
-
-        # Check the consistency of get_children_info and get_info
-        deep_file_info = local.get_info(deep_file)
-        deep_children = local.get_children_info(folder)
-        assert len(deep_children) == 1
-        deep_child_info = deep_children[0]
-        assert deep_file_info.name == deep_child_info.name
-        assert deep_file_info.path == deep_child_info.path
-        assert deep_file_info.get_digest() == deep_child_info.get_digest()
-
-        # Update the file content
-        local.update_content(deep_file, b"New Content.")
-        assert local.get_content(deep_file) == b"New Content."
-
-        # Delete the folder
-        local.delete(folder)
-        assert not local.exists(folder)
-        assert not local.exists(deep_file)
-
-        # Delete the root folder and descendants
-        local.delete("/0123456789")
-        assert not local.exists("/0123456789")
-
-    def test_get_new_file(self):
-        local = self.local_1
-        path, os_path, name = local.get_new_file("/", "Document 1.txt")
-        assert path == Path("Document 1.txt")
-        assert str(os_path).endswith(
-            os.path.join(self.workspace_title, "Document 1.txt")
-        )
-        assert name == "Document 1.txt"
-        assert not local.exists(path)
-        assert not os_path.exists()
-
-    def test_get_path(self):
-        local = self.local_1
-        doc = Path("doc.txt")
-        abs_path = self.local_nxdrive_folder_1 / self.workspace_title / doc
-        assert local.get_path(abs_path) == doc
-
-        # Encoding test
-        assert local.get_path("été.txt") == ROOT
-
-    def test_is_equal_digests(self):
-        local = self.local_1
-        content = b"joe"
-        local_path = local.make_file("/", "File.txt", content=content)
-        local_digest = hashlib.md5(content).hexdigest()
-        # Equal digests
-        assert local.is_equal_digests(local_digest, local_digest, local_path)
-
-        # Different digests with same digest algorithm
-        other_content = b"jack"
-        remote_digest = hashlib.md5(other_content).hexdigest()
-        assert local_digest != remote_digest
-        assert not local.is_equal_digests(local_digest, remote_digest, local_path)
-
-        # Different digests with different digest algorithms but same content
-        remote_digest = hashlib.sha1(content).hexdigest()
-        assert local_digest != remote_digest
-        assert local.is_equal_digests(local_digest, remote_digest, local_path)
-
-        # Different digests with different digest algorithms and different
-        # content
-        remote_digest = hashlib.sha1(other_content).hexdigest()
-        assert local_digest != remote_digest
-        assert not local.is_equal_digests(local_digest, remote_digest, local_path)
-
     def test_long_path(self):
         """NXDRIVE-1090: Long path names generates duplicata on folder creation.
 
@@ -349,17 +136,6 @@ class TestLocalClientNative(StubLocalClient, OneUserTest):
     def get_local_client(self, path):
         return LocalTest(path)
 
-    @pytest.mark.xfail(
-        WINDOWS, raises=OSError, reason="Explorer cannot deal with very long paths"
-    )
-    def test_deep_folders(self):
-        """
-        It should fail on Windows:
-            WindowsError: [Error 206] The filename or extension is too long
-        Explorer cannot deal with very long paths.
-        """
-        super().test_deep_folders()
-
     def test_remote_changing_case_accentued_folder(self):
         """
         NXDRIVE-1061: Remote rename of an accentued folder on Windows fails.
@@ -396,25 +172,9 @@ class TestLocalClientSimulation(StubLocalClient, OneUserTest):
         - File Manager (macOS)
     """
 
-    @pytest.mark.xfail(
-        WINDOWS,
-        raises=OSError,
-        reason="Explorer cannot find the directory as the path is way to long",
-    )
-    def test_complex_filenames(self):
-        """OSError: [Errno 2] No such file or directory"""
-        super().test_complex_filenames()
-
     @pytest.mark.xfail(
         WINDOWS, reason="Explorer cannot find the directory as the path is too long"
     )
     def test_long_path(self):
         """WindowsError: [Errno 3] The system cannot find the specified file"""
         super().test_long_path()
-
-    @pytest.mark.xfail(
-        WINDOWS, raises=OSError, reason="Explorer cannot deal with very long paths"
-    )
-    def test_deep_folders(self):
-        """WindowsError: [Error 206] The filename or extension is too long"""
-        super().test_deep_folders()
diff --git a/tests/unit/test_local_client.py b/tests/unit/test_local_client.py
index 33dd8c2c..8e493ea8 100644
--- a/tests/unit/test_local_client.py
+++ b/tests/unit/test_local_client.py
@@ -1,30 +1,260 @@
-import pathlib
+import hashlib
+import os
+from pathlib import Path
 from time import sleep
 
+import pytest
+
 from nxdrive.client.local import LocalClient
-from nxdrive.constants import ROOT
+from nxdrive.constants import ROOT, WINDOWS
+from nxdrive.exceptions import DuplicationDisabledError, NotFound
+
+EMPTY_DIGEST = hashlib.md5().hexdigest()
+SOME_TEXT_CONTENT = b"Some text content."
+SOME_TEXT_DIGEST = hashlib.md5(SOME_TEXT_CONTENT).hexdigest()
+
+
+class MockedLocalClient(LocalClient):
+    def make_file(self, parent: Path, name: str, content: bytes = None) -> Path:
+        file = self.get_new_file(parent, name)[1]
+        if content:
+            file.write_bytes(content)
+        else:
+            file.touch()
+        return file
+
+
+@pytest.fixture
+def local(tmp_path):
+    yield MockedLocalClient(tmp_path)
+
+
+@pytest.mark.timeout(30)
+def test_case_sensitivity(local):
+    sensitive = local.is_case_sensitive()
+
+    local.make_file(ROOT, "abc.txt")
+    if sensitive:
+        local.make_file(ROOT, "ABC.txt")
+    else:
+        with pytest.raises(DuplicationDisabledError):
+            local.make_file(ROOT, "ABC.txt")
+    assert len(local.get_children_info(ROOT)) == sensitive + 1
+
+
+@pytest.mark.xfail(
+    WINDOWS,
+    raises=OSError,
+    reason="Explorer cannot find the directory as the path is way to long",
+)
+def test_complex_filenames(local):
+    # Create another folder with the same title
+    title_with_accents = "\xc7a c'est l'\xe9t\xe9 !"
+
+    folder_1 = local.make_folder(ROOT, title_with_accents)
+    folder_1_info = local.get_info(folder_1)
+    assert folder_1_info.name == title_with_accents
+
+    # Create another folder with the same title
+    with pytest.raises(DuplicationDisabledError):
+        local.make_folder(ROOT, title_with_accents)
+
+    # Create a long file name with weird chars
+    long_filename = "\xe9" * 50 + "%$#!()[]{}+_-=';&^.doc"
+    file_1 = local.make_file(folder_1, long_filename)
+    file_1_info = local.get_info(Path(folder_1) / file_1.name)
+    assert file_1_info.name == long_filename
+    assert file_1_info.path == folder_1_info.path / long_filename
+
+    # Create a file with invalid chars
+    invalid_filename = 'a/b\\c*d:e<f>g?h"i|j.doc'
+    escaped_filename = "a-b-c-d-e-f-g-h-i-j.doc"
+    file_2 = local.make_file(folder_1, invalid_filename)
+    file_2_info = local.get_info(Path(folder_1) / file_2.name)
+    assert file_2_info.name == escaped_filename
+    assert file_2_info.path == folder_1_info.path / escaped_filename
+
+
+@pytest.mark.xfail(
+    WINDOWS, raises=OSError, reason="Explorer cannot deal with very long paths"
+)
+def test_deep_folders(local):
+    """Check the LocalClient can workaround the default Windows MAX_PATH limit."""
+    folder = ROOT
+    for _ in range(30):
+        folder = local.make_folder(folder, "0123456789")
+
+    # Last Level
+    last_level_folder_info = local.get_info(folder)
+    assert last_level_folder_info.path == Path("0123456789/" * 30)
+
+    # Create a nested file
+    deep_file = local.make_file(folder, "File.txt", content=b"Some Content.")
+    deep_file = local.get_path(deep_file)
+
+    # Check the consistency of get_children_info and get_info
+    deep_file_info = local.get_info(deep_file)
+    deep_children = local.get_children_info(folder)
+    assert len(deep_children) == 1
+    deep_child_info = deep_children[0]
+    assert deep_file_info.name == deep_child_info.name
+    assert deep_file_info.path == deep_child_info.path
+    assert deep_file_info.get_digest() == deep_child_info.get_digest()
+
+    # Update the file content
+    local.abspath(deep_file).write_bytes(b"New Content.")
+
+    # Delete the folder
+    local.delete(folder)
+    assert not local.exists(folder)
+    assert not local.exists(deep_file)
+
+    # Delete the root folder and descendants
+    local.delete("/0123456789")
+    assert not local.exists("/0123456789")
+
+
+def test_get_children_info(local):
+    folder_1 = local.make_folder(ROOT, "Folder 1")
+    folder_2 = local.make_folder(ROOT, "Folder 2")
+    file_1 = local.make_file(ROOT, "File 1.txt", content=b"foo\n")
+
+    # Not a direct child of '/'
+    local.make_file(ROOT / folder_1.name, "File 2.txt", content=b"bar\n")
+
+    # Ignored files
+    data = b"baz\n"
+    ignored_files = [
+        ".File 2.txt",
+        "~$File 2.txt",
+        "File 2.txt~",
+        "File 2.txt.swp",
+        "File 2.txt.lock",
+        "File 2.txt.part",
+    ]
+    for file in ignored_files:
+        local.make_file(ROOT, file, content=data)
+    if local.is_case_sensitive():
+        local.make_file(ROOT, "File 2.txt.LOCK", content=data)
+    else:
+        with pytest.raises(DuplicationDisabledError):
+            local.make_file(ROOT, "File 2.txt.LOCK", content=data)
+
+    workspace_children = local.get_children_info(ROOT)
+    assert len(workspace_children) == 3
+    assert workspace_children[0].path == Path(file_1.name)
+    assert workspace_children[1].path == folder_1
+    assert workspace_children[2].path == folder_2
+
+
+def test_get_info_invalid_date(local):
+    doc_1 = local.make_file(ROOT, "Document 1.txt")
+    os.utime(local.abspath("Document 1.txt"), (0, 999999999999999))
+    doc_1_info = local.get_info(doc_1, check=False)
+    assert doc_1_info.name == "Document 1.txt"
+    assert doc_1_info.path == Path(doc_1.name)
+    assert doc_1_info.get_digest() == EMPTY_DIGEST
+    assert not doc_1_info.folderish
+
 
+def test_get_new_file(local):
+    path, os_path, name = local.get_new_file(ROOT, "Document 1.txt")
+    assert path == Path("Document 1.txt")
+    assert str(os_path).endswith("Document 1.txt")
+    assert name == "Document 1.txt"
+    assert not local.exists(path)
+    assert not os_path.exists()
 
-def test_get_path(tmp_path):
-    local = LocalClient(tmp_path)
-    path = tmp_path / "foo.txt"
-    path_upper = tmp_path / "FOO.TXT"
+
+def test_get_path(local):
+    path = local.base_folder / "foo.txt"
+    path_upper = local.base_folder / "FOO.TXT"
 
     # The path does not exist, it returns ROOT
-    assert local.get_path(pathlib.Path("bar.doc")) == ROOT
+    assert local.get_path(Path("été.doc")) == ROOT
 
     # The path exists, it returns
-    assert local.get_path(path) == pathlib.Path("foo.txt")
-    assert local.get_path(path_upper) == pathlib.Path("FOO.TXT")
+    assert local.get_path(path) == Path("foo.txt")
+    assert local.get_path(path_upper) == Path("FOO.TXT")
 
 
-def test_xattr_crud(tmp_path):
-    """CRUD tests."""
+def test_is_equal_digests(local):
+    content = b"joe"
+    local_path = local.make_file(ROOT, "File.txt", content=content)
+    local_digest = hashlib.md5(content).hexdigest()
+    # Equal digests
+    assert local.is_equal_digests(local_digest, local_digest, local_path, check=False)
+
+    # Different digests with same digest algorithm
+    other_content = b"jack"
+    remote_digest = hashlib.md5(other_content).hexdigest()
+    assert local_digest != remote_digest
+    assert not local.is_equal_digests(
+        local_digest, remote_digest, local_path, check=False
+    )
+
+    # Different digests with different digest algorithms but same content
+    remote_digest = hashlib.sha1(content).hexdigest()
+    assert local_digest != remote_digest
+    assert local.is_equal_digests(local_digest, remote_digest, local_path, check=False)
+
+    # Different digests with different digest algorithms and different
+    # content
+    remote_digest = hashlib.sha1(other_content).hexdigest()
+    assert local_digest != remote_digest
+    assert not local.is_equal_digests(
+        local_digest, remote_digest, local_path, check=False
+    )
+
+
+def test_make_documents(local):
+    doc_1 = local.make_file(ROOT, "Document 1.txt")
+    assert local.exists(local.get_path(doc_1))
+    doc_1_info = local.get_info(doc_1, check=False)
+    assert doc_1_info.name == "Document 1.txt"
+    assert doc_1_info.path == Path(doc_1.name)
+    assert doc_1_info.get_digest() == EMPTY_DIGEST
+    assert not doc_1_info.folderish
+    assert doc_1_info.size == 0
+
+    doc_2 = local.make_file(ROOT, "Document 2.txt", content=SOME_TEXT_CONTENT)
+    assert local.exists(local.get_path(doc_2))
+    doc_2_info = local.get_info(doc_2, check=False)
+    assert doc_2_info.name == "Document 2.txt"
+    assert doc_2_info.path == Path(doc_2.name)
+    assert doc_2_info.get_digest() == SOME_TEXT_DIGEST
+    assert not doc_2_info.folderish
+    assert doc_2_info.size > 0
 
-    local = LocalClient(tmp_path)
-    file = tmp_path / "File 1.txt"
-    file.write_bytes(b"baz\n")
+    local.delete(local.get_path(doc_2))
+    assert local.exists(local.get_path(doc_1))
+    assert not local.exists(local.get_path(doc_2))
 
+    folder_1 = local.make_folder(ROOT, "A new folder")
+    assert local.exists(folder_1)
+    folder_1_info = local.get_info(folder_1)
+    assert folder_1_info.name == "A new folder"
+    assert folder_1_info.path == folder_1
+    assert folder_1_info.folderish
+    # A folder has no size
+    assert folder_1_info.size == 0
+
+    doc_3 = local.make_file(
+        ROOT / folder_1.name, "Document 3.txt", content=SOME_TEXT_CONTENT
+    )
+    local.delete(folder_1)
+    assert not local.exists(folder_1)
+    assert not local.exists(local.get_path(doc_3))
+
+
+def test_missing_file(local):
+    with pytest.raises(NotFound):
+        local.get_info("/Something Missing")
+
+
+def test_xattr_crud(local):
+    """CRUD tests."""
+    file = local.make_file(ROOT, "File 1.txt", content=b"baz\n")
     ref = file.name
 
     # Create
@@ -50,13 +280,19 @@ def test_xattr_crud(tmp_path):
     local.get_remote_id(ref, name="foo") == ""
 
 
-def test_xattr_mtime(tmp_path):
-    """Ensure that playing with xattr does not change the file mtime."""
+def test_xattr_error_invalid_start_byte(local):
+    """Ensure this will never happen again:
+    UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 8: invalid start byte
+    """
+    file = local.make_file(ROOT, "test-xattr.txt", content=b"bla" * 3)
+    raw_value, result_needed = b"fdrpMACS\x80", "fdrpMACS"
+    local.set_path_remote_id(file, raw_value)
+    assert local.get_path_remote_id(file) == result_needed
 
-    local = LocalClient(tmp_path)
-    file = tmp_path / "File 2.txt"
-    file.write_bytes(b"baz\n")
 
+def test_xattr_mtime(local):
+    """Ensure that playing with xattr does not change the file mtime."""
+    file = local.make_file(ROOT, "File 2.txt", content=b"baz\n")
     ref = file.name
     path = local.abspath(ref)
     mtime = int(path.stat().st_mtime)
@@ -66,16 +302,3 @@ def test_xattr_mtime(tmp_path):
     sleep(1)
     local.remove_remote_id(ref)
     assert mtime == int(path.stat().st_mtime)
-
-
-def test_xattr_error_invalid_start_byte(tmp_path):
-    """Ensure this will never happen again:
-    UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 8: invalid start byte
-    """
-    local = LocalClient(tmp_path)
-    file = tmp_path / "test-xattr.txt"
-    file.write_text("bla" * 3)
-
-    raw_value, result_needed = b"fdrpMACS\x80", "fdrpMACS"
-    local.set_path_remote_id(file, raw_value)
-    assert local.get_path_remote_id(file) == result_needed
