mirror of
https://github.com/basiliscos/syncspirit.git
synced 2026-06-01 17:47:32 +00:00
818 lines
33 KiB
C++
818 lines
33 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2019-2026 Ivan Baidakou
|
|
|
|
#include "test-utils.h"
|
|
#include "access.h"
|
|
#include "model/cluster.h"
|
|
#include "model/misc/file_iterator.h"
|
|
#include "model/misc/sequencer.h"
|
|
#include "diff-builder.h"
|
|
#include <set>
|
|
|
|
using namespace syncspirit;
|
|
using namespace syncspirit::test;
|
|
using namespace syncspirit::model;
|
|
|
|
using R = file_iterator_t::result_t;
|
|
using A = advance_action_t;
|
|
|
|
namespace syncspirit::model {
|
|
|
|
inline bool operator==(const R &lhs, const R &rhs) noexcept {
|
|
return lhs.action == rhs.action && lhs.local_file == rhs.local_file && lhs.peer_file == rhs.peer_file &&
|
|
lhs.peer_folder == rhs.peer_folder;
|
|
}
|
|
|
|
} // namespace syncspirit::model
|
|
|
|
TEST_CASE("file iterator, single folder", "[model]") {
|
|
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 peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
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);
|
|
|
|
auto &folders = cluster->get_folders();
|
|
REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = folders.by_id("1234-5678");
|
|
auto &folder_infos = cluster->get_folders().by_id(folder->get_id())->get_folder_infos();
|
|
REQUIRE(folder_infos.size() == 2u);
|
|
|
|
auto peer_folder = folder_infos.by_device(*peer_device);
|
|
auto &peer_files = peer_folder->get_file_infos();
|
|
|
|
auto my_folder = folder_infos.by_device(*my_device);
|
|
auto &my_files = my_folder->get_file_infos();
|
|
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
SECTION("emtpy folders (1)") { CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore}); }
|
|
|
|
REQUIRE(builder.configure_cluster(peer_id.get_sha256())
|
|
.add(peer_id.get_sha256(), folder->get_id(), 123, 10u)
|
|
.finish()
|
|
.apply());
|
|
|
|
SECTION("emtpy folders (2)") { CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore}); }
|
|
|
|
auto &views = peer_device->get_remote_view_map();
|
|
// local seq is always "fresh"
|
|
views.push(peer_id.get_sha256(), folder->get_id(), peer_folder->get_index(), -1);
|
|
|
|
SECTION("cloning (empty files)") {
|
|
SECTION("1 file") {
|
|
SECTION("no local file") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(peer_device->device_id().get_uint(), 0));
|
|
auto index_builder = builder.make_index(peer_id.get_sha256(), folder->get_id());
|
|
|
|
SECTION("regular file") {
|
|
auto ec = index_builder.add(pr_fi, peer_device).finish().apply();
|
|
REQUIRE(ec);
|
|
|
|
SECTION("local max sequence is outdated") {
|
|
views.push(peer_id.get_sha256(), folder->get_id(), peer_folder->get_index(), 100);
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(!f);
|
|
REQUIRE(!lf);
|
|
}
|
|
|
|
SECTION("no path lock") {
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
REQUIRE(!lf);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
CHECK(action == A::remote_copy);
|
|
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
|
|
REQUIRE(builder.remote_copy(*f, *fi).apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("path is locked") {
|
|
auto &cache = cluster->get_path_cache();
|
|
auto path = cache.get_path("a.txt");
|
|
auto lock = cluster->lock(path.get());
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(!f);
|
|
REQUIRE(!lf);
|
|
}
|
|
}
|
|
SECTION("symblink") {
|
|
proto::set_symlink_target(pr_fi, "b.txt");
|
|
proto::set_type(pr_fi, proto::FileInfoType::SYMLINK);
|
|
auto ec = index_builder.add(pr_fi, peer_device).finish().apply();
|
|
REQUIRE(ec);
|
|
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
CHECK(!lf);
|
|
#ifdef SYNCSPIRIT_WIN
|
|
CHECK(action == A::ignore);
|
|
#else
|
|
REQUIRE(f);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
CHECK(action == A::remote_copy);
|
|
#endif
|
|
}
|
|
}
|
|
SECTION("invalid file is ignored") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
proto::set_invalid(pr_fi, true);
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("version checks") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
auto &v = proto::get_version(pr_fi);
|
|
auto &c_1 = proto::add_counters(v);
|
|
proto::set_id(c_1, 1);
|
|
proto::set_value(c_1, 5);
|
|
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish();
|
|
|
|
SECTION("my version < peer version") {
|
|
proto::set_value(c_1, 3);
|
|
REQUIRE(builder.apply());
|
|
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_file->mark_local(true);
|
|
my_files.put(my_file);
|
|
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(lf == my_file);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(fi->get_device() == peer_device.get());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("my version < peer version, but not scanned yet") {
|
|
REQUIRE(builder.apply());
|
|
proto::set_value(c_1, 3);
|
|
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_files.put(my_file);
|
|
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("my version > peer version") {
|
|
REQUIRE(builder.apply());
|
|
proto::set_value(c_1, 10);
|
|
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_file->mark_local(true);
|
|
my_files.put(my_file);
|
|
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("my version == peer version") {
|
|
REQUIRE(builder.apply());
|
|
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_file->mark_local(true);
|
|
my_files.put(my_file);
|
|
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
}
|
|
}
|
|
SECTION("2+ files") {
|
|
auto files = model::file_infos_map_t();
|
|
auto pr_fi_1 = proto::FileInfo();
|
|
proto::set_name(pr_fi_1, "a.txt");
|
|
proto::set_sequence(pr_fi_1, 10ul);
|
|
auto pr_fi_2 = proto::FileInfo();
|
|
proto::set_name(pr_fi_2, "b.txt");
|
|
proto::set_sequence(pr_fi_2, 11ul);
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id())
|
|
.add(pr_fi_1, peer_device)
|
|
.add(pr_fi_2, peer_device)
|
|
.finish();
|
|
REQUIRE(builder.apply());
|
|
|
|
SECTION("both files are missing on my side") {
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
auto full_name = f->get_name()->get_full_name();
|
|
CHECK((full_name == "a.txt" || full_name == "b.txt"));
|
|
files.put(f);
|
|
REQUIRE(builder.apply());
|
|
|
|
REQUIRE(builder.remote_copy(*f, *fi).apply());
|
|
|
|
auto [f_2, fi_2, lf_2, action_2] = file_iterator->next();
|
|
REQUIRE(f_2);
|
|
CHECK(!lf_2);
|
|
full_name = f_2->get_name()->get_full_name();
|
|
CHECK((full_name == "a.txt" || full_name == "b.txt"));
|
|
CHECK(action_2 == A::remote_copy);
|
|
files.put(f_2);
|
|
REQUIRE(builder.apply());
|
|
REQUIRE(builder.remote_copy(*f_2, *fi_2).apply());
|
|
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
|
|
CHECK(files.by_name("a.txt"));
|
|
CHECK(files.by_name("b.txt"));
|
|
}
|
|
|
|
SECTION("1 file is missing on my side") {
|
|
auto peer_file = peer_files.by_name("a.txt");
|
|
REQUIRE(peer_file);
|
|
REQUIRE(builder.remote_copy(*peer_file, *peer_folder).apply());
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "b.txt");
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
|
|
REQUIRE(builder.remote_copy(*f, *fi).apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
SECTION("0 files are missing on my side") {
|
|
auto peer_file_1 = peer_files.by_name("a.txt");
|
|
auto peer_file_2 = peer_files.by_name("b.txt");
|
|
builder.remote_copy(*peer_file_1, *peer_folder).remote_copy(*peer_file_2, *peer_folder);
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
SECTION("new file in new peer update") {
|
|
auto [f_1, fi_1, lf_1, action_1] = file_iterator->next();
|
|
REQUIRE(f_1);
|
|
CHECK(!lf_1);
|
|
CHECK(action_1 == A::remote_copy);
|
|
CHECK((f_1->get_name()->get_full_name() == "a.txt" || f_1->get_name()->get_full_name() == "b.txt"));
|
|
files.put(f_1);
|
|
|
|
auto [f_2, fi_2, lf_2, action_2] = file_iterator->next();
|
|
REQUIRE(f_2);
|
|
CHECK(!lf_2);
|
|
CHECK(action_2 == A::remote_copy);
|
|
CHECK((f_2->get_name()->get_full_name() == "a.txt" || f_2->get_name()->get_full_name() == "b.txt"));
|
|
files.put(f_2);
|
|
|
|
CHECK(files.by_name("a.txt"));
|
|
CHECK(files.by_name("b.txt"));
|
|
|
|
auto pr_fi_3 = proto::FileInfo();
|
|
proto::set_name(pr_fi_3, "c.txt");
|
|
proto::set_sequence(pr_fi_3, 12ul);
|
|
auto ec = builder.make_index(peer_id.get_sha256(), folder->get_id())
|
|
.add(pr_fi_3, peer_device)
|
|
.finish()
|
|
.apply();
|
|
|
|
auto [f_3, fi_3, lf_3, action_3] = file_iterator->next();
|
|
REQUIRE(f_3);
|
|
CHECK(!lf_3);
|
|
CHECK(action_3 == A::remote_copy);
|
|
CHECK(f_3->get_name()->get_full_name() == "c.txt");
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
}
|
|
}
|
|
SECTION("synchronizing non-empty files") {
|
|
auto b1_hash = utils::sha256_digest(as_bytes("12345")).value();
|
|
auto b1 = proto::BlockInfo();
|
|
proto::set_hash(b1, b1_hash);
|
|
proto::set_size(b1, 5);
|
|
|
|
auto b2_hash = utils::sha256_digest(as_bytes("67890")).value();
|
|
auto b2 = proto::BlockInfo();
|
|
proto::set_hash(b2, b2_hash);
|
|
proto::set_size(b2, 5);
|
|
|
|
auto &blocks_map = cluster->get_blocks();
|
|
blocks_map.put(block_info_t::create(b1).value());
|
|
blocks_map.put(block_info_t::create(b2).value());
|
|
|
|
SECTION("missing locally") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
proto::set_size(pr_fi, proto::get_size(b1));
|
|
proto::set_block_size(pr_fi, proto::get_size(b1));
|
|
proto::add_blocks(pr_fi, b1);
|
|
|
|
REQUIRE(
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
|
|
SECTION("folder is suspended") {
|
|
REQUIRE(builder.suspend(*folder).apply());
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
CHECK(!f);
|
|
CHECK(!lf);
|
|
CHECK(action == A::ignore);
|
|
}
|
|
|
|
SECTION("folder is not suspended") {
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
}
|
|
|
|
SECTION("have local, but outdated") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
proto::set_size(pr_fi, proto::get_size(b1));
|
|
proto::set_block_size(pr_fi, proto::get_size(b1));
|
|
proto::add_blocks(pr_fi, b1);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(1, 1));
|
|
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_files.put(my_file);
|
|
|
|
proto::add_counters(v, proto::Counter(2, 2));
|
|
REQUIRE(
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
|
|
SECTION("has not bee scanned") {
|
|
auto &peer_file = *peer_files.begin();
|
|
file_iterator->recheck(*peer_folder, *peer_file);
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
SECTION("has been scanned") {
|
|
my_file->mark_local(true);
|
|
my_file->recheck(*my_folder);
|
|
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(lf);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
REQUIRE(builder.apply());
|
|
}
|
|
}
|
|
|
|
SECTION("have local, local is newer") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
proto::set_size(pr_fi, proto::get_size(b1));
|
|
proto::set_block_size(pr_fi, proto::get_size(b1));
|
|
proto::add_blocks(pr_fi, b1);
|
|
|
|
auto &v = proto::get_version(pr_fi);
|
|
proto::add_counters(v, proto::Counter(1, 1));
|
|
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish();
|
|
|
|
proto::add_counters(v, proto::Counter(2, 2));
|
|
auto my_file = file_info_t::create(sequencer->next_uuid(), pr_fi, my_folder).value();
|
|
my_files.put(my_file);
|
|
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("peer file is unreacheable") {
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
proto::set_size(pr_fi, proto::get_size(b1));
|
|
proto::set_block_size(pr_fi, proto::get_size(b1));
|
|
proto::add_blocks(pr_fi, b1);
|
|
|
|
REQUIRE(
|
|
builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
auto f = peer_files.by_name(proto::get_name(pr_fi));
|
|
REQUIRE(builder.remote_copy(*f, *peer_folder).mark_reacheable(f, *peer_folder, false).apply());
|
|
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("file iterator for 2 folders", "[model]") {
|
|
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 peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
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);
|
|
|
|
auto &folders = cluster->get_folders();
|
|
REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder_1 = folders.by_id("1234-5678");
|
|
auto &folder_infos = cluster->get_folders().by_id(folder_1->get_id())->get_folder_infos();
|
|
REQUIRE(folder_infos.size() == 2u);
|
|
|
|
auto peer_folder = folder_infos.by_device(*peer_device);
|
|
auto my_folder = folder_infos.by_device(*my_device);
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
auto sha256 = peer_id.get_sha256();
|
|
|
|
REQUIRE(builder.upsert_folder("1234", "/", "my-label-1").upsert_folder("5678", "/", "my-label-2").apply());
|
|
REQUIRE(builder.share_folder(sha256, "1234").share_folder(sha256, "5678").apply());
|
|
auto folder1 = folders.by_id("1234");
|
|
auto folder2 = folders.by_id("5678");
|
|
|
|
auto local_fi_1 = folder1->get_folder_infos().by_device(*my_device);
|
|
auto local_fi_2 = folder2->get_folder_infos().by_device(*my_device);
|
|
|
|
REQUIRE(builder.configure_cluster(sha256)
|
|
.add(sha256, "1234", 123, 10u)
|
|
.add(my_device->device_id().get_sha256(), "1234", local_fi_1->get_index(), 10u)
|
|
.add(sha256, "5678", 1234u, 11)
|
|
.add(my_device->device_id().get_sha256(), "5678", local_fi_1->get_index(), 11u)
|
|
.finish()
|
|
.apply());
|
|
|
|
auto pr_fi_1 = proto::FileInfo();
|
|
proto::set_name(pr_fi_1, "a.txt");
|
|
proto::set_sequence(pr_fi_1, 10);
|
|
|
|
auto pr_fi_2 = proto::FileInfo();
|
|
proto::set_name(pr_fi_2, "b.txt");
|
|
proto::set_sequence(pr_fi_2, 11);
|
|
|
|
using set_t = std::set<std::string_view>;
|
|
|
|
SECTION("cloning") {
|
|
REQUIRE(
|
|
builder.make_index(sha256, "1234").add(pr_fi_1, peer_device).add(pr_fi_2, peer_device).finish().apply());
|
|
|
|
auto [f1, fi_1, lf_1, action1] = file_iterator->next();
|
|
auto [f2, fi_2, lf_2, action2] = file_iterator->next();
|
|
REQUIRE(f1);
|
|
REQUIRE(f2);
|
|
CHECK(!lf_1);
|
|
CHECK(!lf_2);
|
|
CHECK(action1 == A::remote_copy);
|
|
CHECK(action2 == A::remote_copy);
|
|
|
|
auto files = set_t{};
|
|
files.emplace(f1->get_name()->get_full_name());
|
|
files.emplace(f2->get_name()->get_full_name());
|
|
|
|
CHECK((files == set_t{"a.txt", "b.txt"}));
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
}
|
|
|
|
SECTION("syncing") {
|
|
auto b1_hash = utils::sha256_digest(as_bytes("12345")).value();
|
|
auto b1 = proto::BlockInfo();
|
|
proto::set_hash(b1, b1_hash);
|
|
proto::set_size(b1, 5);
|
|
|
|
auto b2_hash = utils::sha256_digest(as_bytes("67890")).value();
|
|
auto b2 = proto::BlockInfo();
|
|
proto::set_hash(b2, b1_hash);
|
|
proto::set_size(b2, 5);
|
|
|
|
auto &blocks_map = cluster->get_blocks();
|
|
blocks_map.put(block_info_t::create(b1).value());
|
|
blocks_map.put(block_info_t::create(b2).value());
|
|
|
|
proto::add_blocks(pr_fi_1, b1);
|
|
proto::set_size(pr_fi_1, proto::get_size(b1));
|
|
|
|
proto::add_blocks(pr_fi_2, b2);
|
|
proto::set_size(pr_fi_2, proto::get_size(b2));
|
|
|
|
using set_t = std::set<std::string_view>;
|
|
builder.make_index(sha256, "1234").add(pr_fi_1, peer_device).add(pr_fi_2, peer_device).finish();
|
|
REQUIRE(builder.apply());
|
|
|
|
auto files = set_t{};
|
|
auto [f1, fi_1, lf_1, action1] = file_iterator->next();
|
|
REQUIRE(f1);
|
|
CHECK(!lf_1);
|
|
CHECK(action1 == A::remote_copy);
|
|
files.emplace(f1->get_name()->get_full_name());
|
|
|
|
auto [f2, fi_2, lf_2, action2] = file_iterator->next();
|
|
REQUIRE(f2);
|
|
CHECK(!lf_2);
|
|
CHECK(action2 == A::remote_copy);
|
|
files.emplace(f1->get_name()->get_full_name());
|
|
files.emplace(f2->get_name()->get_full_name());
|
|
|
|
CHECK((files == set_t{"a.txt", "b.txt"}));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("file iterator, create, share, iterae, unshare, share, iterate", "[model]") {
|
|
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 peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
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);
|
|
|
|
auto &folders = cluster->get_folders();
|
|
REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = folders.by_id("1234-5678");
|
|
auto folder_infos = &folder->get_folder_infos();
|
|
REQUIRE(folder_infos->size() == 2u);
|
|
auto local_fi = folder_infos->by_device(*my_device);
|
|
|
|
auto peer_sha = peer_id.get_sha256();
|
|
auto local_sha = my_id.get_sha256();
|
|
REQUIRE(builder.configure_cluster(peer_sha)
|
|
.add(peer_sha, "1234-5678", 123, 10u)
|
|
.add(local_sha, "1234-5678", local_fi->get_index(), 10u)
|
|
.finish()
|
|
.apply());
|
|
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
CHECK(action == A::remote_copy);
|
|
|
|
REQUIRE(builder.apply());
|
|
CHECK(file_iterator->next() == R{{}, {}, {}, A::ignore});
|
|
REQUIRE(builder.remove_folder(*folder).apply());
|
|
REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
folder = folders.by_id("1234-5678");
|
|
folder_infos = &folder->get_folder_infos();
|
|
REQUIRE(folder_infos->size() == 2u);
|
|
REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
|
|
{
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
}
|
|
|
|
peer_device->release_iterator(file_iterator);
|
|
file_iterator = peer_device->create_iterator(*cluster);
|
|
|
|
{
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
REQUIRE(f);
|
|
CHECK(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
}
|
|
|
|
auto folder_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
peer_device->release_iterator(file_iterator);
|
|
file_iterator = peer_device->create_iterator(*cluster);
|
|
REQUIRE(builder.upsert_folder_info(*folder_peer, folder_peer->get_index() + 1).apply());
|
|
|
|
folder_peer = folder->get_folder_infos().by_device(*peer_device);
|
|
file_iterator->on_upsert(folder_peer);
|
|
CHECK(file_iterator->next().action == A::ignore);
|
|
}
|
|
|
|
TEST_CASE("file pull order", "[model]") {
|
|
using names_t = std::vector<std::string>;
|
|
struct file_meta_t {
|
|
std::string_view name;
|
|
std::int64_t size;
|
|
std::int64_t modified;
|
|
};
|
|
file_meta_t file_metas[5] = {
|
|
{"0.txt", 0, 123}, {"1/a.txt", 5, 5000}, {"1/c.txt", 15, 4000}, {"1/d.txt", 10, 4500}, {"1/e.txt", 20, 6000},
|
|
};
|
|
|
|
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 peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
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);
|
|
|
|
auto &folders = cluster->get_folders();
|
|
REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = folders.by_id("1234-5678");
|
|
auto folder_infos = &folder->get_folder_infos();
|
|
REQUIRE(folder_infos->size() == 2u);
|
|
auto local_fi = folder_infos->by_device(*my_device);
|
|
|
|
auto peer_sha = peer_id.get_sha256();
|
|
auto local_sha = my_id.get_sha256();
|
|
REQUIRE(builder.configure_cluster(peer_sha)
|
|
.add(peer_sha, "1234-5678", 123, 10)
|
|
.add(local_sha, "1234-5678", local_fi->get_index(), -1)
|
|
.finish()
|
|
.apply());
|
|
|
|
auto index = builder.make_index(peer_id.get_sha256(), folder->get_id());
|
|
std::int64_t sequence = 10;
|
|
for (auto &meta : file_metas) {
|
|
auto pr = proto::FileInfo();
|
|
proto::set_name(pr, meta.name);
|
|
proto::set_size(pr, meta.size);
|
|
proto::set_modified_s(pr, meta.modified);
|
|
|
|
if (meta.size) {
|
|
auto bytes_view = utils::bytes_view_t((unsigned char *)&file_metas, meta.size);
|
|
auto hash = utils::sha256_digest(bytes_view).value();
|
|
auto block = proto::BlockInfo();
|
|
proto::set_hash(block, hash);
|
|
proto::set_size(block, meta.size);
|
|
proto::add_blocks(pr, std::move(block));
|
|
}
|
|
|
|
proto::set_sequence(pr, sequence++);
|
|
index.add(pr, peer_device);
|
|
}
|
|
REQUIRE(index.finish().apply());
|
|
|
|
SECTION("iteration anew") {
|
|
auto iterate = [&]() -> names_t {
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
auto names = names_t();
|
|
while (true) {
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
if (!f) {
|
|
break;
|
|
};
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
names.emplace_back(std::string(f->get_name()->get_full_name()));
|
|
}
|
|
return names;
|
|
};
|
|
|
|
auto &pull_order = ((model::folder_data_t *)folder.get())->access<test::to::pull_order>();
|
|
SECTION("defaul order (alphabetic)") {
|
|
auto expected = names_t{"0.txt", "1/a.txt", "1/c.txt", "1/d.txt", "1/e.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("random (aka alphabetic)") {
|
|
pull_order = db::PullOrder::random;
|
|
auto expected = names_t{"0.txt", "1/a.txt", "1/c.txt", "1/d.txt", "1/e.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("alphabetic") {
|
|
pull_order = db::PullOrder::alphabetic;
|
|
auto expected = names_t{"0.txt", "1/a.txt", "1/c.txt", "1/d.txt", "1/e.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("smallest") {
|
|
pull_order = db::PullOrder::smallest;
|
|
auto expected = names_t{"0.txt", "1/a.txt", "1/d.txt", "1/c.txt", "1/e.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("largest") {
|
|
pull_order = db::PullOrder::largest;
|
|
auto expected = names_t{"0.txt", "1/e.txt", "1/c.txt", "1/d.txt", "1/a.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("oldest") {
|
|
pull_order = db::PullOrder::oldest;
|
|
auto expected = names_t{"0.txt", "1/c.txt", "1/d.txt", "1/a.txt", "1/e.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
SECTION("newest") {
|
|
pull_order = db::PullOrder::newest;
|
|
auto expected = names_t{"0.txt", "1/e.txt", "1/a.txt", "1/d.txt", "1/c.txt"};
|
|
CHECK(iterate() == expected);
|
|
}
|
|
}
|
|
SECTION("change iteration order") {
|
|
auto &pull_order = ((model::folder_data_t *)folder.get())->access<test::to::pull_order>();
|
|
pull_order = db::PullOrder::alphabetic;
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
auto names = names_t();
|
|
auto next = [&]() {
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
REQUIRE(fi);
|
|
CHECK(!lf);
|
|
CHECK(action == A::remote_copy);
|
|
names.emplace_back(std::string(f->get_name()->get_full_name()));
|
|
};
|
|
|
|
next();
|
|
next();
|
|
|
|
pull_order = db::PullOrder::newest;
|
|
file_iterator->on_upsert(*folder);
|
|
next();
|
|
next();
|
|
next();
|
|
|
|
auto expected = names_t{"0.txt", "1/a.txt", "1/e.txt", "1/d.txt", "1/c.txt"};
|
|
CHECK(names == expected);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("no file iteration for send-only folder", "[model]") {
|
|
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 peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
|
|
|
|
auto peer_device = device_t::create(peer_id, "peer-device").value();
|
|
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);
|
|
|
|
auto &folders = cluster->get_folders();
|
|
|
|
db::Folder db_folder;
|
|
db::set_id(db_folder, "1234-5678");
|
|
db::set_label(db_folder, "l");
|
|
db::set_path(db_folder, "/my/path");
|
|
db::set_folder_type(db_folder, db::FolderType::send);
|
|
|
|
REQUIRE(builder.upsert_folder(db_folder).apply());
|
|
REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
|
|
auto folder = folders.by_id("1234-5678");
|
|
auto folder_infos = &folder->get_folder_infos();
|
|
REQUIRE(folder_infos->size() == 2u);
|
|
auto local_fi = folder_infos->by_device(*my_device);
|
|
|
|
auto peer_sha = peer_id.get_sha256();
|
|
auto local_sha = my_id.get_sha256();
|
|
REQUIRE(builder.configure_cluster(peer_sha)
|
|
.add(peer_sha, "1234-5678", 123, 10)
|
|
.add(local_sha, "1234-5678", local_fi->get_index(), -1)
|
|
.finish()
|
|
.apply());
|
|
|
|
auto file_iterator = peer_device->create_iterator(*cluster);
|
|
auto pr_fi = proto::FileInfo();
|
|
proto::set_name(pr_fi, "a.txt");
|
|
proto::set_sequence(pr_fi, 10);
|
|
|
|
REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(pr_fi, peer_device).finish().apply());
|
|
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(!f);
|
|
CHECK(!lf);
|
|
|
|
SECTION("back to send & receive") {
|
|
db::set_folder_type(db_folder, db::FolderType::send_and_receive);
|
|
REQUIRE(builder.upsert_folder(db_folder).apply());
|
|
auto [f, fi, lf, action] = file_iterator->next();
|
|
REQUIRE(f);
|
|
CHECK(!lf);
|
|
REQUIRE(action == A::remote_copy);
|
|
CHECK(f->get_name()->get_full_name() == "a.txt");
|
|
}
|
|
}
|
|
|
|
int _init() {
|
|
test::init_logging();
|
|
return 1;
|
|
}
|
|
|
|
static int v = _init();
|