def test_merge_manifests_with_a_placeholder(alice, bob): timestamp = alice.timestamp() my_device = alice.device_id other_device = bob.device_id parent = EntryID.new() m1 = LocalFolderManifest.new_placeholder(my_device, parent=parent, timestamp=timestamp) m2 = merge_manifests(my_device, timestamp, empty_pattern, m1) assert m2 == m1 v1 = m1.to_remote(author=my_device, timestamp=timestamp) m2a = merge_manifests(my_device, timestamp, empty_pattern, m1, v1) assert m2a == LocalFolderManifest.from_remote(v1, empty_pattern) m2b = m1.evolve_children_and_mark_updated({EntryName("a"): EntryID.new()}, empty_pattern, timestamp=timestamp) m3b = merge_manifests(my_device, timestamp, empty_pattern, m2b, v1) assert m3b == m2b.evolve(base=v1) v2 = v1.evolve(version=2, author=other_device, children={EntryName("b"): EntryID.new()}) m2c = m1.evolve_children_and_mark_updated({EntryName("a"): EntryID.new()}, empty_pattern, timestamp=timestamp) m3c = merge_manifests(my_device, timestamp, empty_pattern, m2c, v2) children = {**v2.children, **m2c.children} assert m3c == m2c.evolve(base=v2, children=children, updated=m3c.updated)
def test_merge_folder_manifests(): my_device = DeviceID("b@b") other_device = DeviceID("a@a") parent = EntryID() v1 = LocalFolderManifest.new_placeholder( my_device, parent=parent).to_remote(author=other_device) # Initial base manifest m1 = LocalFolderManifest.from_remote(v1, empty_pattern) assert merge_manifests(my_device, empty_pattern, m1) == m1 # Local change m2 = m1.evolve_children_and_mark_updated({"a": EntryID()}, empty_pattern) assert merge_manifests(my_device, empty_pattern, m2) == m2 # Successful upload v2 = m2.to_remote(author=my_device) m3 = merge_manifests(my_device, empty_pattern, m2, v2) assert m3 == LocalFolderManifest.from_remote(v2, empty_pattern) # Two local changes m4 = m3.evolve_children_and_mark_updated({"b": EntryID()}, empty_pattern) assert merge_manifests(my_device, empty_pattern, m4) == m4 m5 = m4.evolve_children_and_mark_updated({"c": EntryID()}, empty_pattern) assert merge_manifests(my_device, empty_pattern, m4) == m4 # M4 has been successfully uploaded v3 = m4.to_remote(author=my_device) m6 = merge_manifests(my_device, empty_pattern, m5, v3) assert m6 == m5.evolve(base=v3) # The remote has changed v4 = v3.evolve(version=4, children={ "d": EntryID(), **v3.children }, author=other_device) m7 = merge_manifests(my_device, empty_pattern, m6, v4) assert m7.base_version == 4 assert sorted(m7.children) == ["a", "b", "c", "d"] assert m7.need_sync # Successful upload v5 = m7.to_remote(author=my_device) m8 = merge_manifests(my_device, empty_pattern, m7, v5) assert m8 == LocalFolderManifest.from_remote(v5, empty_pattern) # The remote has changed v6 = v5.evolve(version=6, children={ "e": EntryID(), **v5.children }, author=other_device) m9 = merge_manifests(my_device, empty_pattern, m8, v6) assert m9 == LocalFolderManifest.from_remote(v6, empty_pattern)
def test_merge_manifests_with_a_placeholder(): my_device = DeviceID("b@b") other_device = DeviceID("a@a") parent = EntryID() m1 = LocalFolderManifest.new_placeholder(my_device, parent=parent) m2 = merge_manifests(my_device, empty_pattern, m1) assert m2 == m1 v1 = m1.to_remote(author=my_device) m2a = merge_manifests(my_device, empty_pattern, m1, v1) assert m2a == LocalFolderManifest.from_remote(v1, empty_pattern) m2b = m1.evolve_children_and_mark_updated({"a": EntryID()}, empty_pattern) m3b = merge_manifests(my_device, empty_pattern, m2b, v1) assert m3b == m2b.evolve(base=v1) v2 = v1.evolve(version=2, author=other_device, children={"b": EntryID()}) m2c = m1.evolve_children_and_mark_updated({"a": EntryID()}, empty_pattern) m3c = merge_manifests(my_device, empty_pattern, m2c, v2) children = {**v2.children, **m2c.children} assert m3c == m2c.evolve(base=v2, children=children, updated=m3c.updated)
def test_merge_file_manifests(alice, bob): timestamp = alice.timestamp() my_device = alice.device_id other_device = bob.device_id parent = EntryID.new() v1 = LocalFileManifest.new_placeholder(my_device, parent=parent, timestamp=timestamp).to_remote( author=other_device, timestamp=timestamp) def evolve(m, n): chunk = Chunk.new(0, n).evolve_as_block(b"a" * n) blocks = ((chunk, ), ) return m1.evolve_and_mark_updated(size=n, blocks=blocks, timestamp=timestamp) # Initial base manifest m1 = LocalFileManifest.from_remote(v1) assert merge_manifests(my_device, timestamp, empty_pattern, m1) == m1 # Local change m2 = evolve(m1, 1) assert merge_manifests(my_device, timestamp, empty_pattern, m2) == m2 # Successful upload v2 = m2.to_remote(author=my_device, timestamp=timestamp) m3 = merge_manifests(my_device, timestamp, empty_pattern, m2, v2) assert m3 == LocalFileManifest.from_remote(v2) # Two local changes m4 = evolve(m3, 2) assert merge_manifests(my_device, timestamp, empty_pattern, m4) == m4 m5 = evolve(m4, 3) assert merge_manifests(my_device, timestamp, empty_pattern, m4) == m4 # M4 has been successfully uploaded v3 = m4.to_remote(author=my_device, timestamp=timestamp) m6 = merge_manifests(my_device, timestamp, empty_pattern, m5, v3) assert m6 == m5.evolve(base=v3) # The remote has changed v4 = v3.evolve(version=4, size=0, author=other_device) with pytest.raises(FSFileConflictError): merge_manifests(my_device, timestamp, empty_pattern, m6, v4)
def test_merge_speculative_with_it_unsuspected_former_self( local_changes, core_config): d1 = datetime(2000, 1, 1) d2 = datetime(2000, 1, 2) d3 = datetime(2000, 1, 3) d4 = datetime(2000, 1, 4) d5 = datetime(2000, 1, 5) my_device = DeviceID("a@a") # 1) Workspace manifest is originally created by our device local = LocalWorkspaceManifest.new_placeholder(author=my_device, timestamp=d1) foo_id = EntryID.new() local = local.evolve(updated=d2, children=FrozenDict({EntryName("foo"): foo_id})) # 2) We sync the workspace manifest v1 = local.to_remote(author=my_device, timestamp=d3) # 3) Now let's pretend we lost local storage, hence creating a new speculative manifest new_local = LocalWorkspaceManifest.new_placeholder(author=my_device, id=local.id, timestamp=d3, speculative=True) if local_changes: bar_id = EntryID.new() new_local = new_local.evolve(updated=d4, children=FrozenDict( {EntryName("bar"): bar_id})) # 4) When syncing the manifest, we shouldn't remove any data from the remote merged = merge_manifests( local_author=my_device, timestamp=d5, prevent_sync_pattern=empty_pattern, local_manifest=new_local, remote_manifest=v1, ) if local_changes: assert merged == LocalWorkspaceManifest( base=v1, need_sync=True, updated=d5, children=FrozenDict({ **v1.children, **new_local.children }), local_confinement_points=frozenset(), remote_confinement_points=frozenset(), speculative=False, ) else: assert merged == LocalWorkspaceManifest( base=v1, need_sync=False, updated=v1.updated, children=v1.children, local_confinement_points=frozenset(), remote_confinement_points=frozenset(), speculative=False, )
def test_merge_folder_manifests_with_concurrent_remote_change( local_change, remote_change, alice, bob): timestamp = alice.timestamp() my_device = alice.device_id other_device = bob.device_id parent = EntryID.new() foo_txt = EntryID.new() remote_manifest_v1 = (LocalFolderManifest.new_placeholder( my_device, parent=parent, timestamp=timestamp).evolve(children={ EntryName("foo.txt"): foo_txt }).to_remote(author=my_device, timestamp=timestamp)) prevent_sync_pattern = re.compile(r".*\.tmp\Z") # Load the manifest in local local_manifest = LocalFolderManifest.from_remote( remote_manifest_v1, prevent_sync_pattern=prevent_sync_pattern) # In local, `foo.txt` is renamed if local_change == "rename": foo_txt_new_name = EntryName("foo2.txt") else: assert local_change == "prevent_sync_rename" foo_txt_new_name = EntryName("foo.txt.tmp") local_manifest = local_manifest.evolve_children_and_mark_updated( data={ EntryName("foo.txt"): None, foo_txt_new_name: foo_txt }, prevent_sync_pattern=prevent_sync_pattern, timestamp=timestamp, ) # In remote, a change also occurs if remote_change == "same_entry_moved": remote_manifest_v2_children = { EntryName("bar.txt"): remote_manifest_v1.children[EntryName("foo.txt")] } else: assert remote_change == "new_entry_added" remote_manifest_v2_children = { **remote_manifest_v1.children, EntryName("bar.txt"): EntryID.new(), } remote_manifest_v2 = remote_manifest_v1.evolve( author=other_device, version=remote_manifest_v1.version + 1, children=remote_manifest_v2_children, ) # Now merging should detect the duplication merged_manifest = merge_manifests( local_author=my_device, timestamp=timestamp, prevent_sync_pattern=prevent_sync_pattern, local_manifest=local_manifest, remote_manifest=remote_manifest_v2, force_apply_pattern=False, ) if remote_change == "same_entry_moved": assert list(merged_manifest.children) == [EntryName("bar.txt")] else: assert remote_change == "new_entry_added" if local_change == "rename": assert sorted(merged_manifest.children) == [ EntryName("bar.txt"), EntryName("foo2.txt") ] else: assert local_change == "prevent_sync_rename" assert sorted(merged_manifest.children) == [ EntryName("bar.txt"), EntryName("foo.txt.tmp"), ]
def test_merge_folder_manifests(alice, bob): timestamp = alice.timestamp() my_device = alice.device_id other_device = bob.device_id parent = EntryID.new() v1 = LocalFolderManifest.new_placeholder(my_device, parent=parent, timestamp=timestamp).to_remote( author=other_device, timestamp=timestamp) # Initial base manifest m1 = LocalFolderManifest.from_remote(v1, empty_pattern) assert merge_manifests(my_device, timestamp, empty_pattern, m1) == m1 # Local change m2 = m1.evolve_children_and_mark_updated({EntryName("a"): EntryID.new()}, empty_pattern, timestamp=timestamp) assert merge_manifests(my_device, timestamp, empty_pattern, m2) == m2 # Successful upload v2 = m2.to_remote(author=my_device, timestamp=timestamp) m3 = merge_manifests(my_device, timestamp, empty_pattern, m2, v2) assert m3 == LocalFolderManifest.from_remote(v2, empty_pattern) # Two local changes m4 = m3.evolve_children_and_mark_updated({EntryName("b"): EntryID.new()}, empty_pattern, timestamp=timestamp) assert merge_manifests(my_device, timestamp, empty_pattern, m4) == m4 m5 = m4.evolve_children_and_mark_updated({EntryName("c"): EntryID.new()}, empty_pattern, timestamp=timestamp) assert merge_manifests(my_device, timestamp, empty_pattern, m4) == m4 # M4 has been successfully uploaded v3 = m4.to_remote(author=my_device, timestamp=timestamp) m6 = merge_manifests(my_device, timestamp, empty_pattern, m5, v3) assert m6 == m5.evolve(base=v3) # The remote has changed v4 = v3.evolve(version=4, children={ EntryName("d"): EntryID.new(), **v3.children }, author=other_device) m7 = merge_manifests(my_device, timestamp, empty_pattern, m6, v4) assert m7.base_version == 4 assert sorted(m7.children) == [ EntryName("a"), EntryName("b"), EntryName("c"), EntryName("d") ] assert m7.need_sync # Successful upload v5 = m7.to_remote(author=my_device, timestamp=timestamp) m8 = merge_manifests(my_device, timestamp, empty_pattern, m7, v5) assert m8 == LocalFolderManifest.from_remote(v5, empty_pattern) # The remote has changed v6 = v5.evolve(version=6, children={ EntryName("e"): EntryID.new(), **v5.children }, author=other_device) m9 = merge_manifests(my_device, timestamp, empty_pattern, m8, v6) assert m9 == LocalFolderManifest.from_remote(v6, empty_pattern)