mirror of
https://github.com/basiliscos/syncspirit.git
synced 2026-06-01 17:47:32 +00:00
1931 lines
92 KiB
C++
1931 lines
92 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2019-2026 Ivan Baidakou
|
|
|
|
#include "test-utils.h"
|
|
#include "diff-builder.h"
|
|
#include "model/cluster.h"
|
|
#include "presentation/cluster_file_presence.h"
|
|
#include "presentation/file_presence.h"
|
|
#include "presentation/folder_presence.h"
|
|
#include "presentation/folder_entity.h"
|
|
#include "syncspirit-config.h"
|
|
#include <memory_resource>
|
|
|
|
using namespace syncspirit;
|
|
using namespace syncspirit::test;
|
|
using namespace syncspirit::utils;
|
|
using namespace syncspirit::proto;
|
|
using namespace syncspirit::model;
|
|
using namespace syncspirit::presentation;
|
|
|
|
using F = file_presence_t::features_t;
|
|
|
|
namespace Catch {
|
|
template <> struct StringMaker<presence_stats_t> {
|
|
static std::string convert(const presence_stats_t &value) {
|
|
return fmt::format("presence state, entities[{}, local: {}, cluster: {}], size = {}", value.entities,
|
|
value.local_entries, value.cluster_entries, value.size);
|
|
}
|
|
};
|
|
template <> struct StringMaker<entity_stats_t> {
|
|
static std::string convert(const entity_stats_t &value) {
|
|
return fmt::format("entity stats entities [{}], size = {}", value.entities, value.size);
|
|
}
|
|
};
|
|
|
|
} // namespace Catch
|
|
|
|
struct aug_sample_t : model::augmentation_t {
|
|
aug_sample_t(int &deleted_, int &updated_) : deleted{deleted_}, updated{updated_} {}
|
|
void on_update() noexcept { ++updated; };
|
|
void on_delete() noexcept { ++deleted; };
|
|
|
|
int &deleted;
|
|
int &updated;
|
|
};
|
|
|
|
auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
|
|
auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
auto peer_2_id =
|
|
model::device_id_t::from_string("LYXKCHX-VI3NYZR-ALCJBHF-WMZYSPK-QG6QJA3-MPFYMSO-U56GTUK-NA2MIAW").value();
|
|
auto my_device = device_t::create(my_id, "my-device").value();
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
auto peer_2_device = device_t::create(peer_2_id, "peer-device-2").value();
|
|
|
|
auto my_device_id = my_device->device_id().get_uint();
|
|
auto peer_device_id = peer_device->device_id().get_uint();
|
|
|
|
TEST_CASE("presentation", "[presentation]") {
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
cluster->get_devices().put(peer_device);
|
|
cluster->get_devices().put(peer_2_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
|
|
SECTION("empty folder") {
|
|
SECTION("shared with nobody") {
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
CHECK(folder->use_count() == 2);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(&folder_entity->get_folder() == folder.get());
|
|
CHECK(folder_entity->get_children().size() == 0);
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
SECTION("check forwarding") {
|
|
int deleted = 0;
|
|
int updated = 0;
|
|
auto aug = model::augmentation_ptr_t(new aug_sample_t(deleted, updated));
|
|
folder_entity->set_augmentation(aug);
|
|
REQUIRE(folder->use_count() == 2);
|
|
|
|
folder->notify_update();
|
|
CHECK(updated == 1);
|
|
CHECK(deleted == 0);
|
|
|
|
REQUIRE(folder->use_count() == 2);
|
|
REQUIRE(folder_entity->use_count() >= 2);
|
|
|
|
folder_entity.reset();
|
|
CHECK(updated == 1);
|
|
CHECK(deleted == 0);
|
|
|
|
SECTION("direct remvoal") {
|
|
folder.reset();
|
|
cluster->get_folders().clear();
|
|
CHECK(updated == 1);
|
|
CHECK(deleted == 1);
|
|
}
|
|
SECTION("remove via diff") {
|
|
REQUIRE(builder.remove_folder(*folder).apply());
|
|
folder.reset();
|
|
CHECK(updated == 1);
|
|
CHECK(deleted == 1);
|
|
}
|
|
}
|
|
SECTION("presence") {
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_2_device));
|
|
|
|
auto self_presense = static_cast<folder_presence_t *>(folder_entity->get_presence(&*my_device));
|
|
CHECK(self_presense);
|
|
CHECK(!self_presense->get_parent());
|
|
CHECK(self_presense->get_features() & (F::folder));
|
|
|
|
auto my_fi = folder->get_folder_infos().by_device(*my_device);
|
|
CHECK(&self_presense->get_folder_info() == my_fi);
|
|
}
|
|
}
|
|
SECTION("shared with a peer") {
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto my_presense = static_cast<folder_presence_t *>(folder_entity->get_presence(&*my_device));
|
|
auto peer_presense = static_cast<folder_presence_t *>(folder_entity->get_presence(&*peer_device));
|
|
REQUIRE(my_presense);
|
|
REQUIRE(peer_presense);
|
|
|
|
CHECK(!folder_entity->get_presence(&*peer_2_device));
|
|
auto my_fi = folder->get_folder_infos().by_device(*my_device);
|
|
auto peer_fi = folder->get_folder_infos().by_device(*peer_device);
|
|
CHECK(&my_presense->get_folder_info() == my_fi);
|
|
CHECK(&peer_presense->get_folder_info() == peer_fi);
|
|
CHECK(my_presense->get_features() & (F::folder));
|
|
CHECK(peer_presense->get_features() & (F::folder));
|
|
|
|
CHECK(!my_presense->get_parent());
|
|
CHECK(!peer_presense->get_parent());
|
|
}
|
|
}
|
|
|
|
SECTION("non-emtpy folder, flat hierarchy") {
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
REQUIRE(builder.share_folder(peer_2_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
|
|
auto add_file = [&](std::string_view name, model::device_t &device) {
|
|
auto folder_info = folder->get_folder_infos().by_device(device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(peer_device->device_id().get_uint(), 1));
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
folder_info->add_strict(file);
|
|
};
|
|
|
|
add_file("f.txt", *peer_device);
|
|
add_file("f.txt", *peer_2_device);
|
|
add_file("e.txt", *peer_2_device);
|
|
add_file("d.txt", *peer_device);
|
|
add_file("c.txt", *my_device);
|
|
add_file("c.txt", *peer_device);
|
|
add_file("c.txt", *peer_2_device);
|
|
add_file("b.txt", *my_device);
|
|
add_file("b.txt", *peer_device);
|
|
add_file("a.txt", *my_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto my_folder_presence = folder_entity->get_presence(&*my_device);
|
|
auto peer_folder_presence = folder_entity->get_presence(&*peer_device);
|
|
auto peer_2_folder_presence = folder_entity->get_presence(&*peer_2_device);
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 6);
|
|
auto it = children.begin();
|
|
auto next_entity = [&]() -> presentation::entity_ptr_t {
|
|
if (it != children.end()) {
|
|
return *it++;
|
|
}
|
|
return {};
|
|
};
|
|
|
|
auto file_a = next_entity();
|
|
auto file_b = next_entity();
|
|
auto file_c = next_entity();
|
|
auto file_d = next_entity();
|
|
auto file_e = next_entity();
|
|
auto file_f = next_entity();
|
|
REQUIRE(!next_entity());
|
|
|
|
CHECK(file_a->get_path()->get_own_name() == "a.txt");
|
|
CHECK(file_b->get_path()->get_own_name() == "b.txt");
|
|
CHECK(file_c->get_path()->get_own_name() == "c.txt");
|
|
CHECK(file_d->get_path()->get_own_name() == "d.txt");
|
|
CHECK(file_e->get_path()->get_own_name() == "e.txt");
|
|
CHECK(file_f->get_path()->get_own_name() == "f.txt");
|
|
CHECK(file_a->get_parent() == folder_entity);
|
|
CHECK(file_b->get_parent() == folder_entity);
|
|
CHECK(file_c->get_parent() == folder_entity);
|
|
CHECK(file_d->get_parent() == folder_entity);
|
|
CHECK(file_e->get_parent() == folder_entity);
|
|
CHECK(file_f->get_parent() == folder_entity);
|
|
|
|
SECTION("only my device has a file (a.txt)") {
|
|
auto f_my = static_cast<cluster_file_presence_t *>(file_a->get_presence(&*my_device));
|
|
auto f_peer = static_cast<file_presence_t *>(file_a->get_presence(&*peer_device));
|
|
auto f_peer_2 = static_cast<file_presence_t *>(file_a->get_presence(&*peer_2_device));
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
|
|
REQUIRE(f_my->get_device() == my_device.get());
|
|
CHECK(f_my->get_features() & (F::cluster | F::local | F::file));
|
|
CHECK(f_peer->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer == f_peer_2);
|
|
|
|
CHECK(f_my->get_parent() == my_folder_presence);
|
|
CHECK(!f_peer->get_parent());
|
|
}
|
|
|
|
SECTION("my & peer device have a file (b.txt)") {
|
|
auto f_my = static_cast<cluster_file_presence_t *>(file_b->get_presence(&*my_device));
|
|
auto f_peer = file_b->get_presence(&*peer_device);
|
|
auto f_peer_2 = file_b->get_presence(&*peer_2_device);
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
|
|
REQUIRE(f_my->get_device() == my_device.get());
|
|
CHECK(f_my->get_features() & (F::cluster | F::local | F::file));
|
|
CHECK(f_peer->get_features() & (F::cluster | F::peer | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::missing | F::file));
|
|
|
|
CHECK(f_my->get_parent() == my_folder_presence);
|
|
CHECK(f_peer->get_parent() == peer_folder_presence);
|
|
CHECK(!f_peer_2->get_parent());
|
|
}
|
|
|
|
SECTION("all devices have a file (c.txt)") {
|
|
auto f_my = static_cast<cluster_file_presence_t *>(file_c->get_presence(&*my_device));
|
|
auto f_peer = static_cast<cluster_file_presence_t *>(file_c->get_presence(&*peer_device));
|
|
auto f_peer_2 = static_cast<cluster_file_presence_t *>(file_c->get_presence(&*peer_2_device));
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
REQUIRE(f_my->get_device() == my_device.get());
|
|
REQUIRE(f_peer->get_device() == peer_device.get());
|
|
REQUIRE(f_peer_2->get_device() == peer_2_device.get());
|
|
CHECK(f_my->get_features() & (F::cluster | F::local | F::file));
|
|
CHECK(f_peer->get_features() & (F::cluster | F::peer | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::cluster | F::peer | F::file));
|
|
|
|
CHECK(f_my->get_parent() == my_folder_presence);
|
|
CHECK(f_peer->get_parent() == peer_folder_presence);
|
|
CHECK(f_peer_2->get_parent() == peer_2_folder_presence);
|
|
}
|
|
|
|
SECTION("only peer device has a file (d.txt)") {
|
|
auto f_my = file_d->get_presence(&*my_device);
|
|
auto f_peer = static_cast<cluster_file_presence_t *>(file_d->get_presence(&*peer_device));
|
|
auto f_peer_2 = file_d->get_presence(&*peer_2_device);
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
|
|
REQUIRE(f_peer->get_device() == peer_device.get());
|
|
CHECK(f_my->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer->get_features() & (F::cluster | F::peer | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::missing | F::file));
|
|
|
|
CHECK(!f_my->get_parent());
|
|
CHECK(f_peer->get_parent() == peer_folder_presence);
|
|
CHECK(!f_peer_2->get_parent());
|
|
}
|
|
|
|
SECTION("only peer device has a file (e.txt)") {
|
|
auto f_my = file_e->get_presence(&*my_device);
|
|
auto f_peer = file_e->get_presence(&*peer_device);
|
|
auto f_peer_2 = static_cast<cluster_file_presence_t *>(file_e->get_presence(&*peer_2_device));
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
|
|
REQUIRE(f_peer_2->get_device() == peer_2_device.get());
|
|
CHECK(f_my->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::cluster | F::peer | F::file));
|
|
|
|
CHECK(!f_my->get_parent());
|
|
CHECK(!f_peer->get_parent());
|
|
CHECK(f_peer_2->get_parent() == peer_2_folder_presence);
|
|
}
|
|
|
|
SECTION("only peer devices have a file (f.txt)") {
|
|
auto f_my = file_f->get_presence(&*my_device);
|
|
auto f_peer = static_cast<cluster_file_presence_t *>(file_f->get_presence(&*peer_device));
|
|
auto f_peer_2 = static_cast<cluster_file_presence_t *>(file_f->get_presence(&*peer_2_device));
|
|
|
|
REQUIRE(f_my);
|
|
REQUIRE(f_peer);
|
|
REQUIRE(f_peer_2);
|
|
|
|
REQUIRE(f_peer->get_device() == peer_device.get());
|
|
REQUIRE(f_peer_2->get_device() == peer_2_device.get());
|
|
CHECK(f_my->get_features() & (F::missing | F::file));
|
|
CHECK(f_peer->get_features() & (F::cluster | F::peer | F::file));
|
|
CHECK(f_peer_2->get_features() & (F::cluster | F::peer | F::file));
|
|
|
|
CHECK(!f_my->get_parent());
|
|
CHECK(f_peer->get_parent() == peer_folder_presence);
|
|
CHECK(f_peer_2->get_parent() == peer_2_folder_presence);
|
|
}
|
|
}
|
|
|
|
SECTION("folder shared with a peer, simple hierarchy") {
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto fi_my = folder->get_folder_infos().by_device(*my_device);
|
|
|
|
auto add_file = [&](std::string_view name, model::device_t &device,
|
|
proto::FileInfoType type = proto::FileInfoType::DIRECTORY) {
|
|
auto folder_info = folder->get_folder_infos().by_device(device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_type(pr_fi, type);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(peer_device->device_id().get_uint(), 1));
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
if (&device == my_device) {
|
|
file->mark_local(true);
|
|
}
|
|
folder_info->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
SECTION("create hierarchy up-front") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
add_file("a", *my_device);
|
|
add_file("a/b", *my_device);
|
|
add_file("a/b/c", *my_device);
|
|
add_file("a/b/c/d", *my_device);
|
|
add_file("a/b/c/d/e.txt", *my_device, proto::FileInfoType::FILE);
|
|
add_file("a", *peer_device);
|
|
add_file("a/b", *peer_device);
|
|
add_file("a/b/c", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto p_folder_my = folder_entity->get_presence(&*my_device);
|
|
auto p_folder_peer = folder_entity->get_presence(&*peer_device);
|
|
REQUIRE(p_folder_my);
|
|
REQUIRE(p_folder_peer);
|
|
CHECK(!p_folder_my->get_parent());
|
|
CHECK(!p_folder_peer->get_parent());
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 1);
|
|
auto &dir_a_entity = *children.begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
CHECK(dir_a_entity->get_parent() == folder_entity);
|
|
auto p_dir_a_my = dir_a_entity->get_presence(&*my_device);
|
|
auto p_dir_a_peer = dir_a_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_a_my->get_features() & (F::directory | F::local));
|
|
CHECK(p_dir_a_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(p_dir_a_my->get_parent() == p_folder_my);
|
|
CHECK(p_dir_a_peer->get_parent() == p_folder_peer);
|
|
|
|
auto &dir_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(dir_b_entity->get_path()->get_own_name() == "b");
|
|
REQUIRE(dir_b_entity->get_children().size() == 1);
|
|
CHECK(dir_b_entity->get_parent() == dir_a_entity);
|
|
auto p_dir_b_my = dir_b_entity->get_presence(&*my_device);
|
|
auto p_dir_b_peer = dir_b_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_b_my->get_features() & (F::directory | F::local));
|
|
CHECK(p_dir_b_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(p_dir_b_my->get_parent() == p_dir_a_my);
|
|
CHECK(p_dir_b_peer->get_parent() == p_dir_a_peer);
|
|
|
|
auto &dir_c_entity = *dir_b_entity->get_children().begin();
|
|
REQUIRE(dir_c_entity->get_path()->get_own_name() == "c");
|
|
REQUIRE(dir_c_entity->get_children().size() == 1);
|
|
CHECK(dir_c_entity->get_parent() == dir_b_entity);
|
|
auto p_dir_c_my = dir_c_entity->get_presence(&*my_device);
|
|
auto p_dir_c_peer = dir_c_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_c_my->get_features() & (F::directory | F::local));
|
|
CHECK(p_dir_c_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(p_dir_c_my->get_parent() == p_dir_b_my);
|
|
CHECK(p_dir_c_peer->get_parent() == p_dir_b_peer);
|
|
|
|
auto &dir_d_entity = *dir_c_entity->get_children().begin();
|
|
REQUIRE(dir_d_entity->get_path()->get_own_name() == "d");
|
|
REQUIRE(dir_d_entity->get_children().size() == 1);
|
|
CHECK(dir_d_entity->get_parent() == dir_c_entity);
|
|
auto p_dir_d_my = dir_d_entity->get_presence(&*my_device);
|
|
auto p_dir_d_peer = dir_d_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_d_my->get_features() & (F::directory | F::local));
|
|
CHECK(p_dir_d_peer->get_features() & (F::directory | F::missing));
|
|
CHECK(p_dir_d_my->get_parent() == p_dir_c_my);
|
|
CHECK(!p_dir_d_peer->get_parent());
|
|
|
|
auto &file_e_entity = *dir_d_entity->get_children().begin();
|
|
REQUIRE(file_e_entity->get_path()->get_own_name() == "e.txt");
|
|
REQUIRE(file_e_entity->get_children().size() == 0);
|
|
CHECK(file_e_entity->get_parent() == dir_d_entity);
|
|
auto p_file_my = file_e_entity->get_presence(&*my_device);
|
|
auto p_file_peer = file_e_entity->get_presence(&*peer_device);
|
|
CHECK(p_file_my->get_features() & (F::file | F::local));
|
|
CHECK(p_file_peer->get_features() & (F::file | F::missing));
|
|
CHECK(p_file_my->get_parent() == p_dir_d_my);
|
|
CHECK(!p_file_peer->get_parent());
|
|
}
|
|
SECTION("dynamically create hierarchy") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
add_file("a", *peer_device);
|
|
add_file("a/b", *peer_device);
|
|
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
folder_entity->on_insert(*fi_peer);
|
|
|
|
auto p_folder_my = folder_entity->get_presence(&*my_device);
|
|
auto p_folder_peer = folder_entity->get_presence(&*peer_device);
|
|
REQUIRE(p_folder_my);
|
|
REQUIRE(p_folder_peer);
|
|
CHECK(!p_folder_my->get_parent());
|
|
CHECK(!p_folder_peer->get_parent());
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 1);
|
|
auto &dir_a_entity = *children.begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
CHECK(dir_a_entity->get_parent() == folder_entity);
|
|
auto p_dir_a_my = dir_a_entity->get_presence(&*my_device);
|
|
auto p_dir_a_peer = dir_a_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_a_my->get_features() & (F::directory | F::missing));
|
|
CHECK(p_dir_a_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(!p_dir_a_my->get_parent());
|
|
CHECK(p_dir_a_peer->get_parent() == p_folder_peer);
|
|
|
|
auto &dir_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(dir_b_entity->get_path()->get_own_name() == "b");
|
|
REQUIRE(dir_b_entity->get_children().size() == 0);
|
|
CHECK(dir_b_entity->get_parent() == dir_a_entity);
|
|
auto p_dir_b_my = dir_b_entity->get_presence(&*my_device);
|
|
auto p_dir_b_peer = dir_b_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_b_my->get_features() & (F::directory | F::missing));
|
|
CHECK(p_dir_b_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(!p_dir_b_my->get_parent());
|
|
CHECK(p_dir_b_peer->get_parent() == p_dir_a_peer);
|
|
|
|
auto file_c_peer = add_file("a/b/c", *peer_device);
|
|
folder_entity->on_insert(*file_c_peer, *fi_peer);
|
|
folder_entity->on_insert(*file_c_peer, *fi_peer); // should be ignored
|
|
REQUIRE(dir_b_entity->get_children().size() == 1);
|
|
auto &dir_c_entity = *dir_b_entity->get_children().begin();
|
|
REQUIRE(dir_c_entity->get_path()->get_own_name() == "c");
|
|
REQUIRE(dir_c_entity->get_children().size() == 0);
|
|
CHECK(dir_c_entity->get_parent() == dir_b_entity);
|
|
auto p_dir_c_my = dir_c_entity->get_presence(&*my_device);
|
|
auto p_dir_c_peer = dir_c_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_c_my->get_features() & (F::missing));
|
|
CHECK(p_dir_c_peer->get_features() & (F::directory | F::peer | F::cluster));
|
|
CHECK(!p_dir_c_my->get_parent());
|
|
CHECK(p_dir_c_peer->get_parent() == p_dir_b_peer);
|
|
|
|
auto file_a_my = add_file("a", *my_device);
|
|
folder_entity->on_insert(*file_a_my, *fi_my);
|
|
REQUIRE(children.size() == 1);
|
|
p_dir_a_my = dir_a_entity->get_presence(&*my_device);
|
|
CHECK(p_dir_a_my->get_parent() == p_folder_my);
|
|
}
|
|
SECTION("dynamically create hierarchy-2") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto file_1 = add_file("a-file", *my_device, proto::FileInfoType::FILE);
|
|
auto e_file_1 = folder_entity->on_insert(*file_1, *fi_my);
|
|
REQUIRE(e_file_1);
|
|
CHECK(e_file_1->get_path() == file_1->get_name());
|
|
|
|
auto file_3 = add_file("a-file-3", *my_device, proto::FileInfoType::FILE);
|
|
auto e_file_3 = folder_entity->on_insert(*file_3, *fi_my);
|
|
REQUIRE(e_file_3);
|
|
CHECK(e_file_3->get_path() == file_3->get_name());
|
|
|
|
auto file_2 = add_file("a-file-2", *my_device, proto::FileInfoType::FILE);
|
|
auto e_file_2 = folder_entity->on_insert(*file_2, *fi_my);
|
|
REQUIRE(e_file_2);
|
|
CHECK(e_file_2->get_path() == file_2->get_name());
|
|
}
|
|
SECTION("presence & entity hierarchy order") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto file_1 = add_file("file-a", *my_device, proto::FileInfoType::FILE);
|
|
auto file_2 = add_file("file-b", *my_device, proto::FileInfoType::DIRECTORY);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
auto &children = folder_entity->get_children();
|
|
auto it_children = children.begin();
|
|
auto &c1 = *it_children++;
|
|
auto &c2 = *it_children++;
|
|
CHECK(c1->get_path()->get_full_name() == "file-a");
|
|
CHECK(c2->get_path()->get_full_name() == "file-b");
|
|
|
|
auto &p_my_children = folder_entity->get_presence(&*my_device)->get_children();
|
|
REQUIRE(p_my_children.size() == 2);
|
|
CHECK(p_my_children[0]->get_entity()->get_path()->get_full_name() == "file-b");
|
|
CHECK(p_my_children[1]->get_entity()->get_path()->get_full_name() == "file-a");
|
|
|
|
auto &p_peer_children = folder_entity->get_presence(&*peer_device)->get_children();
|
|
REQUIRE(p_peer_children.size() == 2);
|
|
CHECK(p_peer_children[0]->get_entity()->get_path()->get_full_name() == "file-b");
|
|
CHECK(p_peer_children[1]->get_entity()->get_path()->get_full_name() == "file-a");
|
|
}
|
|
SECTION("orphans") {
|
|
SECTION("simple") {
|
|
add_file("a/b", *my_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 0);
|
|
|
|
auto dir_a_my = add_file("a", *my_device);
|
|
folder_entity->on_insert(*dir_a_my, *fi_my);
|
|
|
|
REQUIRE(children.size() == 1);
|
|
auto &dir_a_entity = *children.begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
CHECK(dir_a_entity->get_parent() == folder_entity);
|
|
auto p_dir_a_my = dir_a_entity->get_presence(&*my_device);
|
|
CHECK(p_dir_a_my->get_features() & (F::directory | F::local));
|
|
|
|
auto &dir_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(dir_b_entity->get_path()->get_own_name() == "b");
|
|
REQUIRE(dir_b_entity->get_children().size() == 0);
|
|
CHECK(dir_b_entity->get_parent() == dir_a_entity);
|
|
auto p_dir_b_my = dir_b_entity->get_presence(&*my_device);
|
|
CHECK(p_dir_b_my->get_features() & (F::directory | F::local));
|
|
}
|
|
SECTION("lazy") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
|
|
auto f_b = add_file("a/b", *my_device);
|
|
auto f_c = add_file("a/b/c", *my_device);
|
|
|
|
folder_entity->on_insert(*f_b, *fi_my);
|
|
folder_entity->on_insert(*f_c, *fi_my);
|
|
|
|
auto f_a = add_file("a", *my_device);
|
|
folder_entity->on_insert(*f_a, *fi_my);
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 1);
|
|
|
|
auto &dir_a_entity = *children.begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
CHECK(dir_a_entity->get_parent() == folder_entity);
|
|
|
|
auto &dir_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(dir_b_entity->get_path()->get_own_name() == "b");
|
|
REQUIRE(dir_b_entity->get_children().size() == 1);
|
|
CHECK(dir_b_entity->get_parent() == dir_a_entity);
|
|
|
|
auto &dir_c_entity = *dir_b_entity->get_children().begin();
|
|
REQUIRE(dir_c_entity->get_path()->get_own_name() == "c");
|
|
REQUIRE(dir_c_entity->get_children().size() == 0);
|
|
CHECK(dir_c_entity->get_parent() == dir_b_entity);
|
|
}
|
|
SECTION("lazy, with peer") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
auto f_b_my = add_file("a/b.bin", *my_device);
|
|
auto f_b_peer = add_file("a/b.bin", *peer_device);
|
|
folder_entity->on_insert(*f_b_my, *fi_my);
|
|
folder_entity->on_insert(*f_b_peer, *fi_peer);
|
|
|
|
auto f_a_my = add_file("a", *my_device);
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
|
|
auto f_a_peer = add_file("a", *peer_device);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
|
|
auto &children = folder_entity->get_children();
|
|
REQUIRE(children.size() == 1);
|
|
|
|
auto &dir_a_entity = *children.begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
CHECK(dir_a_entity->get_parent() == folder_entity);
|
|
|
|
auto p_dir_a_my = dir_a_entity->get_presence(&*my_device);
|
|
auto p_dir_a_peer = dir_a_entity->get_presence(&*peer_device);
|
|
CHECK(p_dir_a_my->get_features() & (F::directory | F::local));
|
|
CHECK(p_dir_a_peer->get_features() & (F::directory | F::peer));
|
|
CHECK(p_dir_a_my->get_parent());
|
|
|
|
auto &file_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(file_b_entity->get_path()->get_own_name() == "b.bin");
|
|
REQUIRE(file_b_entity->get_children().size() == 0);
|
|
CHECK(file_b_entity->get_parent() == dir_a_entity);
|
|
|
|
auto p_file_my = file_b_entity->get_presence(&*my_device);
|
|
auto p_file_peer = file_b_entity->get_presence(&*peer_device);
|
|
CHECK(p_file_my->get_features() & (F::file | F::local));
|
|
CHECK(p_file_peer->get_features() & (F::file | F::peer));
|
|
CHECK(p_file_my->get_parent() == p_dir_a_my);
|
|
CHECK(p_file_peer->get_parent() == p_dir_a_peer);
|
|
}
|
|
SECTION("lazy, with peer-2") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
auto f_b_peer = add_file("a/b.bin", *peer_device);
|
|
auto f_c_my = add_file("a/c.bin", *my_device);
|
|
|
|
auto e_b = folder_entity->on_insert(*f_b_peer, *fi_peer);
|
|
auto e_c = folder_entity->on_insert(*f_c_my, *fi_my);
|
|
|
|
auto f_a_my = add_file("a", *my_device);
|
|
auto e_a = folder_entity->on_insert(*f_a_my, *fi_my);
|
|
CHECK(e_a->get_stats() == entity_stats_t{0, 2});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
|
|
auto p_a_my = e_a->get_presence(my_device.get());
|
|
CHECK(p_a_my->get_children().size() == 1);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 2, 2, 2});
|
|
|
|
auto p_c_my = p_a_my->get_children()[0];
|
|
CHECK(p_c_my->get_children().size() == 0);
|
|
CHECK(p_c_my->get_entity()->get_path()->get_full_name() == "a/c.bin");
|
|
|
|
auto f_a_peer = add_file("a", *peer_device);
|
|
auto e_a_2 = folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
CHECK(e_a_2 == e_a);
|
|
CHECK(e_a->get_stats() == entity_stats_t{0, 3});
|
|
|
|
auto p_a_peer = e_a->get_presence(peer_device.get());
|
|
REQUIRE(p_a_peer->get_children().size() == 2);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 2, 2, 0});
|
|
|
|
auto p_b_peer = p_a_peer->get_children()[0];
|
|
CHECK(p_b_peer->get_children().size() == 0);
|
|
CHECK(p_b_peer->get_entity()->get_path()->get_full_name() == "a/b.bin");
|
|
|
|
CHECK(p_a_peer->get_children()[1]->get_features() & F::missing);
|
|
CHECK(p_a_peer->get_children()[1]->get_entity()->get_path()->get_full_name() == "a/c.bin");
|
|
|
|
CHECK(p_a_my->get_children().size() == 2);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 2, 2, 2});
|
|
|
|
auto p_b_my = p_a_my->get_children()[0];
|
|
CHECK(p_b_my->get_children().size() == 0);
|
|
CHECK(p_b_my->get_features() & F::missing);
|
|
CHECK(p_b_my->get_entity()->get_path()->get_full_name() == "a/b.bin");
|
|
|
|
p_c_my = p_a_my->get_children()[1];
|
|
CHECK(p_c_my->get_children().size() == 0);
|
|
CHECK(p_c_my->get_entity()->get_path()->get_full_name() == "a/c.bin");
|
|
}
|
|
}
|
|
SECTION("removal") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
SECTION("empty folder info removal") {
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
REQUIRE(folder_entity->get_presence(&*peer_device));
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
}
|
|
SECTION("folder info with a file removal") {
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
add_file("a", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
REQUIRE(folder_entity->get_presence(&*peer_device));
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
}
|
|
SECTION("folder info with a file hierarchy removal") {
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
add_file("a", *peer_device);
|
|
add_file("a/b", *peer_device);
|
|
add_file("a/b/c", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
REQUIRE(folder_entity->get_presence(&*peer_device));
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
}
|
|
SECTION("folder info with a file hierarchy removal (2)") {
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
add_file("a", *my_device);
|
|
add_file("a", *peer_device);
|
|
add_file("a/b", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
REQUIRE(folder_entity->get_presence(&*peer_device));
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
|
|
auto &dir_a_entity = *folder_entity->get_children().begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
|
|
REQUIRE(dir_a_entity->get_children().size() == 0);
|
|
}
|
|
SECTION("folder info with a file hierarchy removal (3)") {
|
|
using strings_t = std::vector<std::string_view>;
|
|
|
|
struct sample_aug_t : model::augmentation_t {
|
|
sample_aug_t(strings_t &strings_, std::string_view name_) : strings{strings_}, name{name_} {}
|
|
~sample_aug_t() { strings.emplace_back(name); }
|
|
|
|
strings_t &strings;
|
|
std::string_view name;
|
|
};
|
|
|
|
auto strings = strings_t();
|
|
auto augment = [&](entity_t *presense, std::string_view name) {
|
|
auto aug = model::augmentation_ptr_t();
|
|
aug = new sample_aug_t(strings, name);
|
|
presense->set_augmentation(aug);
|
|
};
|
|
{
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
add_file("a", *peer_device);
|
|
add_file("a/b", *peer_device);
|
|
add_file("a/b/c", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
auto &dir_a_entity = *folder_entity->get_children().begin();
|
|
REQUIRE(dir_a_entity->get_path()->get_own_name() == "a");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
|
|
auto &dir_b_entity = *dir_a_entity->get_children().begin();
|
|
REQUIRE(dir_b_entity->get_path()->get_own_name() == "b");
|
|
REQUIRE(dir_a_entity->get_children().size() == 1);
|
|
|
|
auto &file_c_entity = *dir_b_entity->get_children().begin();
|
|
REQUIRE(file_c_entity->get_path()->get_own_name() == "c");
|
|
REQUIRE(file_c_entity->get_children().size() == 0);
|
|
|
|
augment(dir_a_entity, "a");
|
|
augment(dir_b_entity, "a/b");
|
|
augment(file_c_entity, "a/b/c");
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
|
|
auto p_folder_peer = folder_entity->get_presence(&*peer_device);
|
|
REQUIRE(p_folder_peer);
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
}
|
|
|
|
REQUIRE(strings.size() == 3);
|
|
CHECK(strings[0] == "a/b/c");
|
|
CHECK(strings[1] == "a/b");
|
|
CHECK(strings[2] == "a");
|
|
}
|
|
SECTION("folder info with an orphan removal") {
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
add_file("a/b", *peer_device);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(!folder_entity->get_parent());
|
|
|
|
REQUIRE(folder_entity->get_presence(&*my_device));
|
|
REQUIRE(folder_entity->get_presence(&*peer_device));
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
|
|
CHECK(folder_entity->get_presence(&*my_device));
|
|
CHECK(!folder_entity->get_presence(&*peer_device));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("statistics, update", "[presentation]") {
|
|
auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
|
|
auto my_device = device_t::create(my_id, "my-device").value();
|
|
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto &blocks = cluster->get_blocks();
|
|
|
|
auto add_file = [&](std::string_view name, model::device_t &device, std::int32_t file_size = 0,
|
|
proto::FileInfoType type = proto::FileInfoType::DIRECTORY, std::uint64_t modified_by = 0,
|
|
std::uint64_t modified_v = 0) {
|
|
auto folder_info = folder->get_folder_infos().by_device(device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_type(pr_fi, type);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
|
|
auto block = model::block_info_ptr_t();
|
|
if (file_size) {
|
|
proto::set_size(pr_fi, file_size);
|
|
proto::set_block_size(pr_fi, file_size);
|
|
assert(type == proto::FileInfoType::FILE);
|
|
auto bytes = utils::bytes_t(file_size);
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, file_size);
|
|
block = blocks.by_hash(b_hash);
|
|
if (!block) {
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
}
|
|
}
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto modified_device = modified_by == 0 ? device.device_id().get_uint() : modified_by;
|
|
auto modified_version = modified_v == 0 ? 1 : modified_v;
|
|
proto::add_counters(v, proto::Counter(modified_device, modified_version));
|
|
proto::set_modified_s(pr_fi, modified_version);
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
if (block) {
|
|
file->assign_block(block.get(), 0);
|
|
}
|
|
if (&device == my_device) {
|
|
file->mark_local(true);
|
|
if (block) {
|
|
file->mark_local_available(0);
|
|
}
|
|
}
|
|
folder_info->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
auto f_c_my = add_file("a/b.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 2});
|
|
|
|
auto p_a_my = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
|
|
f_a_my->get_augmentation()->on_update();
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
}
|
|
|
|
TEST_CASE("statistics (size & locality are idenpendent)", "[presentation]") {
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
cluster->get_devices().put(peer_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
|
|
auto &blocks = cluster->get_blocks();
|
|
|
|
auto add_file = [&](std::string_view name, model::device_t &device, std::int32_t file_size = 0,
|
|
proto::FileInfoType type = proto::FileInfoType::DIRECTORY, std::uint64_t modified_by = 0,
|
|
std::uint64_t modified_v = 0) {
|
|
auto folder_info = folder->get_folder_infos().by_device(device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_type(pr_fi, type);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
|
|
auto block = model::block_info_ptr_t();
|
|
if (file_size) {
|
|
proto::set_size(pr_fi, file_size);
|
|
proto::set_block_size(pr_fi, file_size);
|
|
assert(type == proto::FileInfoType::FILE);
|
|
auto bytes = utils::bytes_t(file_size);
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, file_size);
|
|
block = blocks.by_hash(b_hash);
|
|
if (!block) {
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
}
|
|
}
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto modified_device = modified_by == 0 ? device.device_id().get_uint() : modified_by;
|
|
auto modified_version = modified_v == 0 ? 1 : modified_v;
|
|
proto::add_counters(v, proto::Counter(modified_device, modified_version));
|
|
proto::set_modified_s(pr_fi, modified_version);
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
if (block) {
|
|
file->assign_block(block.get(), 0);
|
|
}
|
|
if (&device == my_device) {
|
|
file->mark_local(true);
|
|
if (block) {
|
|
file->mark_local_available(0);
|
|
}
|
|
}
|
|
folder_info->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
auto f_peer = add_file("b.txt", *peer_device, 5, proto::FileInfoType::FILE);
|
|
auto f_my = add_file("b.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
auto file_b = *folder_entity->get_children().begin();
|
|
auto file_b_my = file_b->get_presence(my_device.get());
|
|
auto file_b_peer = file_b->get_presence(peer_device.get());
|
|
|
|
CHECK(file_b_my->get_own_stats().size == 5);
|
|
CHECK(file_b_my->get_own_stats().local_entries == 1);
|
|
CHECK(file_b_peer->get_own_stats().size == 5);
|
|
CHECK(file_b_peer->get_own_stats().local_entries == 0);
|
|
|
|
f_my->mark_local(false);
|
|
f_my->notify_update();
|
|
CHECK(file_b_my->get_own_stats().size == 5);
|
|
CHECK(file_b_my->get_own_stats().local_entries == 0);
|
|
|
|
f_my->mark_local(true);
|
|
f_my->notify_update();
|
|
|
|
CHECK(file_b_my->get_own_stats().size == 5);
|
|
CHECK(file_b_my->get_own_stats().local_entries == 1);
|
|
}
|
|
|
|
TEST_CASE("statistics", "[presentation]") {
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
cluster->get_devices().put(peer_device);
|
|
cluster->get_devices().put(peer_2_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto fi_my = folder->get_folder_infos().by_device(*my_device);
|
|
auto &blocks = cluster->get_blocks();
|
|
|
|
auto add_file = [&](std::string_view name, model::device_t &device, std::int32_t file_size = 0,
|
|
proto::FileInfoType type = proto::FileInfoType::DIRECTORY, std::uint64_t modified_by = 0,
|
|
std::uint64_t modified_v = 0) {
|
|
auto folder_info = folder->get_folder_infos().by_device(device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_type(pr_fi, type);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
|
|
auto block = model::block_info_ptr_t();
|
|
if (file_size) {
|
|
proto::set_size(pr_fi, file_size);
|
|
proto::set_block_size(pr_fi, file_size);
|
|
assert(type == proto::FileInfoType::FILE);
|
|
auto bytes = utils::bytes_t(file_size);
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, file_size);
|
|
block = blocks.by_hash(b_hash);
|
|
if (!block) {
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
}
|
|
}
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto modified_device = modified_by == 0 ? device.device_id().get_uint() : modified_by;
|
|
auto modified_version = modified_v == 0 ? 1 : modified_v;
|
|
proto::add_counters(v, proto::Counter(modified_device, modified_version));
|
|
proto::set_modified_s(pr_fi, modified_version);
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
if (block) {
|
|
file->assign_block(block.get(), 0);
|
|
}
|
|
if (&device == my_device) {
|
|
file->mark_local(true);
|
|
if (block) {
|
|
file->mark_local_available(0);
|
|
}
|
|
}
|
|
folder_info->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
SECTION("emtpy folder") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{});
|
|
}
|
|
SECTION("shared with nobody folder") {
|
|
SECTION("single file") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
auto folder_my = folder_entity->get_presence(&*my_device);
|
|
CHECK(folder_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
}
|
|
SECTION("two files") {
|
|
SECTION("flat hierarchy") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
add_file("b.txt", *my_device, 4, proto::FileInfoType::FILE);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{9, 2});
|
|
}
|
|
SECTION("some hierarchy") {
|
|
add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
add_file("a/d.txt", *my_device, 4, proto::FileInfoType::FILE);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{9, 4});
|
|
|
|
auto folder_my = folder_entity->get_presence(&*my_device);
|
|
CHECK(folder_my->get_stats() == presence_stats_t{9, 4, 4, 4});
|
|
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
auto p_a = dir_a->get_presence(&*my_device);
|
|
auto it_a = dir_a->get_children().begin();
|
|
auto dir_b = *it_a++;
|
|
auto f_d = *it_a++;
|
|
auto p_b = dir_b->get_presence(&*my_device);
|
|
auto f_c = *dir_b->get_children().begin();
|
|
auto p_c = f_c->get_presence(&*my_device);
|
|
auto p_d = f_d->get_presence(&*my_device);
|
|
|
|
CHECK(dir_a->get_stats() == entity_stats_t{9, 4});
|
|
CHECK(p_a->get_stats() == presence_stats_t{9, 4, 4, 4});
|
|
CHECK(dir_b->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(p_b->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
CHECK(f_c->get_stats() == entity_stats_t{5, 1});
|
|
CHECK(p_c->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
CHECK(f_d->get_stats() == entity_stats_t{4, 1});
|
|
CHECK(p_d->get_stats() == presence_stats_t{4, 1, 1, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
SECTION("shared with a peer") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device).get();
|
|
SECTION("single file") {
|
|
SECTION("same file") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
add_file("a.txt", *peer_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
}
|
|
SECTION("peer has newer") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE, peer_device_id, 1);
|
|
add_file("a.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 2);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{6, 1});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
}
|
|
SECTION("me has newer") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 2);
|
|
add_file("a.txt", *peer_device, 6, proto::FileInfoType::FILE, my_device_id, 1);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
}
|
|
SECTION("coflicted file (peer has newer)") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
add_file("a.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 2);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
}
|
|
SECTION("coflicted file (me has newer)") {
|
|
add_file("a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 2);
|
|
add_file("a.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 1);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
}
|
|
}
|
|
SECTION("two files do not intersect") {
|
|
SECTION("simple hierarchy") {
|
|
add_file("dir", *my_device);
|
|
add_file("dir/a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
add_file("dir", *peer_device);
|
|
add_file("dir/b.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 1);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{11, 3});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
}
|
|
SECTION("complex hierarchy") {
|
|
add_file("dir", *my_device);
|
|
add_file("dir/a.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
add_file("dir", *peer_device);
|
|
add_file("dir/b", *peer_device);
|
|
add_file("dir/b/c", *peer_device);
|
|
add_file("dir/b/c/d", *peer_device);
|
|
add_file("dir/b/c/d/x.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 1);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{11, 6});
|
|
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
}
|
|
}
|
|
}
|
|
|
|
SECTION("dynamically add files") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
SECTION("for local device") {
|
|
SECTION("no orphans") {
|
|
auto f_a = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_a, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 1});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 1});
|
|
auto p_a = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
|
|
auto f_b = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_b, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 2});
|
|
|
|
auto dir_b = *dir_a->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 2});
|
|
CHECK(p_a->get_stats() == presence_stats_t{0, 2, 2, 2});
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 2});
|
|
|
|
auto p_b = dir_b->get_presence(&*my_device);
|
|
CHECK(p_b->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
CHECK(dir_b->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto f_c = add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
folder_entity->on_insert(*f_c, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 3});
|
|
CHECK(p_a->get_stats() == presence_stats_t{5, 3, 3, 3});
|
|
CHECK(dir_b->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(p_b->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
|
|
auto file_c = *dir_b->get_children().begin();
|
|
CHECK(file_c->get_stats() == entity_stats_t{5, 1});
|
|
auto p_c = file_c->get_presence(&*my_device);
|
|
CHECK(p_c->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
}
|
|
|
|
SECTION("with orphans") {
|
|
auto f_c = add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE);
|
|
folder_entity->on_insert(*f_c, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_b = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_b, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_a = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_a, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
}
|
|
}
|
|
SECTION("for local & peer device") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
SECTION("no orphans") {
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto f_b_peer = add_file("a/b", *peer_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_b_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 2});
|
|
|
|
auto f_b_my = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY);
|
|
folder_entity->on_insert(*f_b_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 2});
|
|
|
|
auto f_c = add_file("a/b/c.txt", *peer_device, 5, proto::FileInfoType::FILE);
|
|
folder_entity->on_insert(*f_c, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
}
|
|
SECTION("simple hierarchy") {
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 2);
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto p_a_my = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
|
|
auto p_a_peer = dir_a->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 0, 0, 0});
|
|
|
|
auto f_x_my = add_file("a/x.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 2);
|
|
folder_entity->on_insert(*f_x_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 0});
|
|
|
|
auto e_x = *dir_a->get_children().begin();
|
|
CHECK(e_x->get_stats() == entity_stats_t{5, 1});
|
|
auto p_x_my = e_x->get_presence(&*my_device);
|
|
CHECK(p_x_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
CHECK(p_x_my->get_parent() == p_a_my);
|
|
CHECK(p_a_my->get_children().size() == 1);
|
|
CHECK(p_a_my->get_children()[0] == p_x_my);
|
|
|
|
CHECK(p_a_peer->get_children().size() == 1);
|
|
CHECK(p_a_peer->get_children()[0]->get_features() & F::missing);
|
|
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
CHECK(p_x_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
|
|
p_a_peer = dir_a->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto p_x_peer = e_x->get_presence(&*peer_device);
|
|
CHECK(p_x_peer->get_features() & (F::missing | F::file));
|
|
CHECK(p_x_peer->get_stats() == presence_stats_t{0, 0});
|
|
|
|
auto pr_x = f_x_my->as_proto(true);
|
|
auto file_x_peer = model::file_info_t::create(sequencer->next_uuid(), pr_x, fi_peer.get()).value();
|
|
auto block = *blocks.begin();
|
|
file_x_peer->assign_block(block.get(), 0);
|
|
fi_peer->add_strict(file_x_peer);
|
|
|
|
CHECK(p_a_peer->get_children().size() == 1);
|
|
CHECK(p_a_peer->get_children()[0]->get_features() & F::missing);
|
|
folder_entity->on_insert(*file_x_peer, *fi_peer);
|
|
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 2});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
CHECK(p_x_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{5, 2, 1, 0});
|
|
|
|
p_x_peer = e_x->get_presence(&*peer_device);
|
|
CHECK(p_x_peer->get_parent());
|
|
CHECK(p_x_peer->get_stats() == presence_stats_t{5, 1, 1, 0});
|
|
CHECK(p_a_peer->get_children().size() == 1);
|
|
CHECK(p_a_peer->get_children()[0] == p_x_peer);
|
|
}
|
|
SECTION("with orphans") {
|
|
auto f_c_peer = add_file("a/b/c.txt", *peer_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
folder_entity->on_insert(*f_c_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_c_my = add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
folder_entity->on_insert(*f_c_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_b_peer = add_file("a/b", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_b_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_b_my = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_b_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 0});
|
|
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
auto dir_b = *dir_a->get_children().begin();
|
|
auto file_c = *dir_b->get_children().begin();
|
|
|
|
auto p_a_my = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 3, 3, 3});
|
|
|
|
auto p_a_peer = dir_a->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{5, 3, 3, 0});
|
|
|
|
auto p_b_my = dir_b->get_presence(&*my_device);
|
|
CHECK(p_b_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
|
|
auto p_b_peer = dir_b->get_presence(&*peer_device);
|
|
CHECK(p_b_peer->get_stats() == presence_stats_t{5, 2, 2, 0});
|
|
|
|
auto p_c_my = file_c->get_presence(&*my_device);
|
|
CHECK(p_c_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
|
|
auto p_c_peer = file_c->get_presence(&*peer_device);
|
|
CHECK(p_c_peer->get_stats() == presence_stats_t{5, 1, 1, 0});
|
|
}
|
|
}
|
|
}
|
|
|
|
SECTION("file updates") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_b_peer = add_file("a/b", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_b_my = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_c_my = add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{5, 3});
|
|
|
|
auto p_a_my = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 3, 3, 3});
|
|
|
|
auto p_a_peer = dir_a->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 2, 2, 0});
|
|
|
|
auto p_b_my = dir_a->get_presence(&*my_device)->get_children().front();
|
|
CHECK(p_b_my->get_stats() == presence_stats_t{5, 2, 2, 2});
|
|
|
|
auto p_c_my = p_b_my->get_entity()->get_presence(&*my_device)->get_children().front();
|
|
CHECK(p_c_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
|
|
auto pr_fi = [&]() {
|
|
auto pr_fi = f_c_my->as_proto(false);
|
|
auto block = model::block_info_ptr_t();
|
|
proto::set_size(pr_fi, 10);
|
|
proto::set_block_size(pr_fi, 10);
|
|
auto bytes = utils::bytes_t(10);
|
|
std::fill_n(bytes.data(), bytes.size(), '1');
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, 10);
|
|
proto::set_hash(b, b_hash);
|
|
block = blocks.by_hash(b_hash);
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
return pr_fi;
|
|
}();
|
|
REQUIRE(builder.local_update("1234-5678", pr_fi).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{10, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{10, 3});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{10, 3, 3, 3});
|
|
CHECK(p_c_my->get_stats() == presence_stats_t{10, 1, 1, 1});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 2, 2, 0});
|
|
|
|
// peer copies
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
f_c_my = fi_my->get_file_infos().by_name(f_c_my->get_name()->get_full_name());
|
|
proto::get_version(pr_fi) = f_c_my->get_version().as_proto();
|
|
auto file_c_peer = model::file_info_t::create(sequencer->next_uuid(), pr_fi, fi_peer.get()).value();
|
|
{
|
|
auto block = *blocks.begin();
|
|
file_c_peer->assign_block(block.get(), 0);
|
|
fi_peer->add_strict(file_c_peer);
|
|
folder_entity->on_insert(*file_c_peer, *fi_peer);
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{10, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{10, 3});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{10, 3, 3, 3});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{10, 3, 3, 0});
|
|
CHECK(p_c_my->get_stats() == presence_stats_t{10, 1, 1, 1});
|
|
}
|
|
|
|
// peer updates
|
|
{
|
|
auto [pr_fi, b_15] = [&]() -> std::pair<proto::FileInfo, model::block_info_ptr_t> {
|
|
auto pr_fi = f_c_my->as_proto(false);
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto value = proto::get_value(f_c_my->get_version().get_best());
|
|
proto::add_counters(v, proto::Counter(peer_device_id, value + 1));
|
|
auto block = model::block_info_ptr_t();
|
|
proto::set_size(pr_fi, 15);
|
|
proto::set_block_size(pr_fi, 15);
|
|
auto bytes = utils::bytes_t(15);
|
|
std::fill_n(bytes.data(), bytes.size(), '2');
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, 15);
|
|
proto::set_hash(b, b_hash);
|
|
block = blocks.by_hash(b_hash);
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
return {pr_fi, block};
|
|
}();
|
|
auto fi_peer_2 = folder->get_folder_infos().by_device(*peer_device);
|
|
bu::uuid file_uuid;
|
|
assign(file_uuid, file_c_peer->get_uuid());
|
|
auto file_c_peer_2 = model::file_info_t::create(file_uuid, pr_fi, fi_peer.get()).value();
|
|
auto block = *blocks.begin();
|
|
file_c_peer_2->assign_block(b_15.get(), 0);
|
|
fi_peer_2->add_strict(file_c_peer);
|
|
|
|
file_c_peer->update(*file_c_peer_2);
|
|
file_c_peer->notify_update();
|
|
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{15, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{15, 3});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{10, 3, 2, 3});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{15, 3, 3, 0});
|
|
}
|
|
|
|
// local copy
|
|
{
|
|
REQUIRE(builder.remote_copy(*file_c_peer, *fi_peer).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{15, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{15, 3});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{15, 3, 3, 3});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{15, 3, 3, 0});
|
|
}
|
|
|
|
// local update
|
|
{
|
|
auto pr_fi = [&]() {
|
|
auto pr_fi = file_c_peer->as_proto(false);
|
|
proto::clear_blocks(pr_fi);
|
|
proto::set_deleted(pr_fi, true);
|
|
proto::set_size(pr_fi, 0);
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(my_device_id, 3));
|
|
return pr_fi;
|
|
}();
|
|
REQUIRE(builder.local_update("1234-5678", pr_fi).apply());
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 3});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 3});
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 3, 3, 3});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{15, 3, 2, 0});
|
|
}
|
|
}
|
|
|
|
SECTION("file deletion & insertion") {
|
|
auto f_a = add_file("a", *my_device, 5, proto::FileInfoType::FILE);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto e_a = *folder_entity->get_children().begin();
|
|
CHECK(e_a->get_stats() == entity_stats_t{5, 1});
|
|
|
|
auto p_a_my = e_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
CHECK(!(p_a_my->get_features() & F::deleted));
|
|
|
|
auto pr_fi = [&]() {
|
|
auto pr_fi = f_a->as_proto(false);
|
|
auto block = model::block_info_ptr_t();
|
|
proto::set_size(pr_fi, 0);
|
|
proto::set_block_size(pr_fi, 0);
|
|
proto::set_deleted(pr_fi, true);
|
|
return pr_fi;
|
|
}();
|
|
REQUIRE(builder.local_update("1234-5678", pr_fi).apply());
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
CHECK(p_a_my->get_features() & F::deleted);
|
|
}
|
|
|
|
SECTION("folder updates & cluster entities") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY);
|
|
REQUIRE(builder.remote_copy(*f_a_peer, *fi_peer).apply());
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 1});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 1});
|
|
|
|
auto p_a_my = dir_a->get_presence(&*my_device);
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
|
|
auto p_a_peer = dir_a->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 1, 1, 0});
|
|
|
|
auto pr_fi = [&]() {
|
|
auto pr_fi = f_a_peer->as_proto(false);
|
|
proto::set_deleted(pr_fi, true);
|
|
return pr_fi;
|
|
}();
|
|
REQUIRE(builder.local_update("1234-5678", pr_fi).apply());
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
CHECK(dir_a->get_stats() == entity_stats_t{0, 1});
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{0, 1, 0, 0});
|
|
}
|
|
|
|
SECTION("yet not resolved conficts") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
SECTION("static") {
|
|
SECTION("local win") {
|
|
auto f_a_my = add_file("content.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 10);
|
|
auto f_a_peer = add_file("content.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 9);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 1});
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
|
|
auto file_entity = *folder_entity->get_children().begin();
|
|
CHECK(file_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
auto p_my = file_entity->get_presence(&*my_device);
|
|
CHECK(p_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
|
|
auto p_a_peer = file_entity->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{6, 1, 0, 0});
|
|
}
|
|
SECTION("remote win") {
|
|
auto f_a_my = add_file("content.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 9);
|
|
auto f_a_peer = add_file("content.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 10);
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{6, 1});
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
|
|
auto file_entity = *folder_entity->get_children().begin();
|
|
CHECK(file_entity->get_stats() == entity_stats_t{6, 1});
|
|
|
|
auto p_my = file_entity->get_presence(&*my_device);
|
|
CHECK(p_my->get_stats() == presence_stats_t{5, 1, 0, 1});
|
|
|
|
auto p_a_peer = file_entity->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{6, 1, 1, 0});
|
|
}
|
|
}
|
|
SECTION("dynamic") {
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
SECTION("local win") {
|
|
auto f_a_my = add_file("content-2.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 10);
|
|
auto f_a_peer =
|
|
add_file("content-2.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 9);
|
|
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto file_entity = *folder_entity->get_children().begin();
|
|
CHECK(file_entity->get_stats() == entity_stats_t{5, 1});
|
|
|
|
auto p_my = file_entity->get_presence(&*my_device);
|
|
CHECK(p_my->get_stats() == presence_stats_t{5, 1, 1, 1});
|
|
|
|
auto p_a_peer = file_entity->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{6, 1, 0, 0});
|
|
}
|
|
SECTION("remote win") {
|
|
auto f_a_my = add_file("content-3.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 9);
|
|
auto f_a_peer =
|
|
add_file("content-3.txt", *peer_device, 6, proto::FileInfoType::FILE, peer_device_id, 10);
|
|
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
|
|
auto p_folder_my = folder_entity->get_presence(&*my_device);
|
|
CHECK(p_folder_my->get_stats() == presence_stats_t{5, 1, 0, 1});
|
|
auto p_folder_peer = folder_entity->get_presence(&*peer_device);
|
|
CHECK(p_folder_peer->get_stats() == presence_stats_t{6, 1, 1, 0});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto file_entity = *folder_entity->get_children().begin();
|
|
CHECK(file_entity->get_stats() == entity_stats_t{6, 1});
|
|
|
|
auto p_my = file_entity->get_presence(&*my_device);
|
|
CHECK(p_my->get_stats() == presence_stats_t{5, 1, 0, 1});
|
|
|
|
auto p_a_peer = file_entity->get_presence(&*peer_device);
|
|
CHECK(p_a_peer->get_stats() == presence_stats_t{6, 1, 1, 0});
|
|
}
|
|
}
|
|
}
|
|
SECTION("updates propagation") {
|
|
using sorted_entities_t = std::pmr::set<const entity_t *>;
|
|
using unsorted_entities_t = std::pmr::set<entity_ptr_t, std::less<entity_ptr_t>>;
|
|
struct monitor_t final : entities_monitor_t {
|
|
monitor_t(unsorted_entities_t &deleted_, sorted_entities_t &updated_)
|
|
: deleted{deleted_}, updated{updated_} {}
|
|
void on_delete(entity_t &entity) noexcept override { deleted.emplace(entity_ptr_t{&entity}); }
|
|
void on_update(const entity_t &entity) noexcept override { updated.insert(&entity); }
|
|
|
|
unsorted_entities_t &deleted;
|
|
sorted_entities_t &updated;
|
|
};
|
|
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_b_my = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_c_my = add_file("a/b/c.txt", *my_device, 5, proto::FileInfoType::FILE, my_device_id, 1);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{5, 3});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
auto dir_b = *dir_a->get_children().begin();
|
|
auto file_c = *dir_b->get_children().begin();
|
|
|
|
auto buffer = std::array<std::byte, 1024>();
|
|
auto pool = std::pmr::monotonic_buffer_resource(buffer.data(), buffer.size());
|
|
auto allocator = std::pmr::polymorphic_allocator<std::string>(&pool);
|
|
|
|
auto updated_entities = sorted_entities_t(allocator);
|
|
auto deleted_entities = unsorted_entities_t(allocator);
|
|
auto monitor = monitor_t(deleted_entities, updated_entities);
|
|
auto monitor_guard = folder_entity->monitor(&monitor);
|
|
|
|
SECTION("file update") {
|
|
auto pr_fi = [&]() {
|
|
auto pr_fi = f_c_my->as_proto(false);
|
|
auto block = model::block_info_ptr_t();
|
|
proto::set_size(pr_fi, 6);
|
|
proto::set_block_size(pr_fi, 6);
|
|
auto bytes = utils::bytes_t(6);
|
|
std::fill_n(bytes.data(), bytes.size(), '1');
|
|
auto b_hash = utils::sha256_digest(bytes).value();
|
|
auto &b = proto::add_blocks(pr_fi);
|
|
proto::set_size(b, 6);
|
|
proto::set_hash(b, b_hash);
|
|
block = blocks.by_hash(b_hash);
|
|
block = model::block_info_t::create(b).value();
|
|
blocks.put(block);
|
|
return pr_fi;
|
|
}();
|
|
REQUIRE(builder.local_update("1234-5678", pr_fi).apply());
|
|
REQUIRE(deleted_entities.size() == 0);
|
|
REQUIRE(updated_entities.size() == 1);
|
|
auto it = updated_entities.begin();
|
|
REQUIRE(*it++ == file_c);
|
|
updated_entities.clear();
|
|
}
|
|
|
|
auto f_d_my = add_file("a/b/d.txt", *my_device, 7, proto::FileInfoType::FILE, my_device_id, 1);
|
|
folder_entity->on_insert(*f_d_my, *fi_my);
|
|
|
|
auto file_d = *(++dir_b->get_children().begin());
|
|
REQUIRE(deleted_entities.size() == 0);
|
|
REQUIRE(updated_entities.size() == 1);
|
|
REQUIRE(*updated_entities.begin() == file_d);
|
|
updated_entities.clear();
|
|
REQUIRE(folder_entity->get_stats() == entity_stats_t{13, 4});
|
|
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto fi_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
folder_entity->on_insert(*fi_peer);
|
|
REQUIRE(folder_entity->get_stats() == entity_stats_t{13, 4});
|
|
REQUIRE(deleted_entities.size() == 0);
|
|
REQUIRE(updated_entities.size() == 1);
|
|
REQUIRE(*updated_entities.begin() == folder_entity);
|
|
updated_entities.clear();
|
|
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
folder_entity->on_insert(*f_a_peer, *fi_peer);
|
|
REQUIRE(deleted_entities.size() == 0);
|
|
REQUIRE(updated_entities.size() == 1);
|
|
REQUIRE(*updated_entities.begin() == dir_a);
|
|
updated_entities.clear();
|
|
|
|
auto f_x_peer = add_file("x", *peer_device, 0, proto::FileInfoType::DIRECTORY, peer_device_id, 1);
|
|
auto f_y_peer = add_file("x/y", *peer_device, 0, proto::FileInfoType::DIRECTORY, peer_device_id, 1);
|
|
folder_entity->on_insert(*f_x_peer, *fi_peer);
|
|
folder_entity->on_insert(*f_y_peer, *fi_peer);
|
|
REQUIRE(deleted_entities.size() == 0);
|
|
REQUIRE(updated_entities.size() == 2);
|
|
updated_entities.clear();
|
|
|
|
f_x_peer.reset();
|
|
f_y_peer.reset();
|
|
REQUIRE(builder.unshare_folder(*fi_peer).apply());
|
|
REQUIRE(deleted_entities.size() == 2);
|
|
|
|
deleted_entities.clear();
|
|
f_a_my.reset();
|
|
f_b_my.reset();
|
|
f_c_my.reset();
|
|
REQUIRE(builder.remove_folder(*folder).apply());
|
|
REQUIRE(deleted_entities.size() == 3);
|
|
|
|
folder.reset();
|
|
REQUIRE(deleted_entities.size() == 5);
|
|
}
|
|
|
|
SECTION("detached presentation") {
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto f_a_peer = add_file("a", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_b_peer = add_file("a/b", *peer_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_c_peer = add_file("a/b/c.txt", *peer_device, 0, proto::FileInfoType::FILE, my_device_id, 1);
|
|
auto f_c_my = add_file("a/b/c.txt", *my_device, 0, proto::FileInfoType::FILE, my_device_id, 1);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
CHECK(folder_entity->get_stats() == entity_stats_t{0, 3});
|
|
|
|
REQUIRE(folder_entity->get_children().size() == 1);
|
|
auto dir_a = *folder_entity->get_children().begin();
|
|
auto dir_b = *dir_a->get_children().begin();
|
|
auto file_c = *dir_b->get_children().begin();
|
|
|
|
SECTION("destroy detached") {
|
|
CHECK(dir_a->get_presence(my_device.get())->get_features() & F::missing);
|
|
CHECK(dir_b->get_presence(my_device.get())->get_features() & F::missing);
|
|
CHECK(file_c->get_presence(my_device.get())->get_features() & F::local);
|
|
}
|
|
SECTION("attach") {
|
|
auto f_b_my = add_file("a/b", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
auto f_a_my = add_file("a", *my_device, 0, proto::FileInfoType::DIRECTORY, my_device_id, 1);
|
|
|
|
folder_entity->on_insert(*f_b_my, *fi_my);
|
|
folder_entity->on_insert(*f_a_my, *fi_my);
|
|
|
|
auto p_a_my = dir_a->get_presence(my_device.get());
|
|
auto p_b_my = dir_b->get_presence(my_device.get());
|
|
auto p_f_my = file_c->get_presence(my_device.get());
|
|
CHECK(p_a_my->get_features() & F::local);
|
|
CHECK(p_b_my->get_features() & F::local);
|
|
CHECK(p_f_my->get_features() & F::local);
|
|
|
|
CHECK(p_a_my->get_stats() == presence_stats_t{0, 3, 3, 3});
|
|
CHECK(p_b_my->get_stats() == presence_stats_t{0, 2, 2, 2});
|
|
CHECK(p_f_my->get_stats() == presence_stats_t{0, 1, 1, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("file uniqueness", "[presentation]") {
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto &blocks = cluster->get_blocks();
|
|
|
|
auto add_file = [&](std::string_view name, bool deleted = false) {
|
|
auto folder_info = folder->get_folder_infos().by_device(*my_device);
|
|
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_sequence(pr_fi, folder_info->get_max_sequence() + 1);
|
|
proto::set_deleted(pr_fi, deleted);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto modified_device = my_device->device_id().get_uint();
|
|
auto modified_version = 1;
|
|
proto::add_counters(v, proto::Counter(modified_device, modified_version));
|
|
proto::set_modified_s(pr_fi, modified_version);
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, folder_info.get()).value();
|
|
folder_info->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
auto file_a1 = add_file("a.bin");
|
|
auto file_a2 = add_file("A.bin");
|
|
auto file_b = add_file("b.bin");
|
|
auto file_c1 = add_file("файл-1.bin");
|
|
auto file_c2 = add_file("фаЙл-1.bin");
|
|
auto file_d = add_file("фаил-2.bin");
|
|
auto file_e1 = add_file("Файл-3.bin");
|
|
auto file_e2 = add_file("ФАЙЛ-3.bin", true);
|
|
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
|
|
auto p_file_a1 = static_cast<presentation::file_presence_t *>(file_a1->get_augmentation().get());
|
|
auto p_file_a2 = static_cast<presentation::file_presence_t *>(file_a2->get_augmentation().get());
|
|
auto p_file_b = static_cast<presentation::file_presence_t *>(file_b->get_augmentation().get());
|
|
auto p_file_c1 = static_cast<presentation::file_presence_t *>(file_c1->get_augmentation().get());
|
|
auto p_file_c2 = static_cast<presentation::file_presence_t *>(file_c2->get_augmentation().get());
|
|
auto p_file_d = static_cast<presentation::file_presence_t *>(file_d->get_augmentation().get());
|
|
auto p_file_e1 = static_cast<presentation::file_presence_t *>(file_e1->get_augmentation().get());
|
|
auto p_file_e2 = static_cast<presentation::file_presence_t *>(file_e2->get_augmentation().get());
|
|
|
|
#if defined(SYNCSPIRIT_WIN) || defined(SYNCSPIRIT_MAC)
|
|
CHECK(!p_file_a1->is_unique());
|
|
CHECK(!p_file_a2->is_unique());
|
|
CHECK(p_file_b->is_unique());
|
|
CHECK(!p_file_c1->is_unique());
|
|
CHECK(!p_file_c2->is_unique());
|
|
CHECK(p_file_d->is_unique());
|
|
CHECK(p_file_e1->is_unique());
|
|
CHECK(p_file_e2->is_unique());
|
|
#else
|
|
CHECK(p_file_a1->is_unique());
|
|
CHECK(p_file_a2->is_unique());
|
|
CHECK(p_file_b->is_unique());
|
|
CHECK(p_file_c1->is_unique());
|
|
CHECK(p_file_c2->is_unique());
|
|
CHECK(p_file_d->is_unique());
|
|
CHECK(p_file_e1->is_unique());
|
|
CHECK(p_file_e2->is_unique());
|
|
#endif
|
|
}
|
|
|
|
TEST_CASE("get_child", "[presentation]") {
|
|
auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
|
|
auto sequencer = make_sequencer(4);
|
|
cluster->get_devices().put(my_device);
|
|
|
|
auto builder = diff_builder_t(*cluster);
|
|
REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
|
|
auto folder = cluster->get_folders().by_id("1234-5678");
|
|
auto local_folder = folder->get_folder_infos().by_device(*my_device);
|
|
|
|
auto add_file = [&](std::string_view name, proto::FileInfoType type = proto::FileInfoType::DIRECTORY) {
|
|
proto::FileInfo pr_fi;
|
|
proto::set_name(pr_fi, name);
|
|
proto::set_sequence(pr_fi, local_folder->get_max_sequence() + 1);
|
|
proto::set_type(pr_fi, type);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto modified_device = my_device->device_id().get_uint();
|
|
auto modified_version = 1;
|
|
proto::add_counters(v, proto::Counter(modified_device, modified_version));
|
|
proto::set_modified_s(pr_fi, modified_version);
|
|
|
|
auto file = model::file_info_t::create(sequencer->next_uuid(), pr_fi, local_folder.get()).value();
|
|
local_folder->add_strict(file);
|
|
return file;
|
|
};
|
|
|
|
auto dir = add_file("dir");
|
|
auto file = add_file("dir/b");
|
|
auto folder_entity = folder_entity_ptr_t(new folder_entity_t(folder));
|
|
|
|
auto p_dir = static_cast<presentation::file_presence_t *>(dir->get_augmentation().get());
|
|
auto p_file = static_cast<presentation::file_presence_t *>(file->get_augmentation().get());
|
|
CHECK(p_dir->get_child("a", true) == nullptr);
|
|
CHECK(p_dir->get_child("b", true) == p_file);
|
|
CHECK(p_dir->get_child("c", true) == nullptr);
|
|
|
|
CHECK(p_dir->get_child("a", false) == nullptr);
|
|
CHECK(p_dir->get_child("b", false) == nullptr);
|
|
CHECK(p_dir->get_child("c", false) == nullptr);
|
|
}
|
|
|
|
static bool _init = []() -> bool {
|
|
test::init_logging();
|
|
return true;
|
|
}();
|