050-file_iterator.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // SPDX-FileCopyrightText: 2019-2024 Ivan Baidakou
  3. #include "test-utils.h"
  4. #include "model/cluster.h"
  5. #include "model/misc/file_iterator.h"
  6. #include "model/misc/sequencer.h"
  7. #include "diff-builder.h"
  8. using namespace syncspirit;
  9. using namespace syncspirit::test;
  10. using namespace syncspirit::model;
  11. struct dummy_sink_t final : model::diff_sink_t {
  12. dummy_sink_t(diff_builder_t &builder_) noexcept : builder{builder_} {}
  13. void push(diff::cluster_diff_ptr_t d) noexcept override { builder.assign(d.get()); }
  14. diff_builder_t &builder;
  15. };
  16. TEST_CASE("file iterator", "[model]") {
  17. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  18. auto my_device = device_t::create(my_id, "my-device").value();
  19. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  20. auto peer_device = device_t::create(peer_id, "peer-device").value();
  21. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  22. auto sequencer = make_sequencer(4);
  23. cluster->get_devices().put(my_device);
  24. cluster->get_devices().put(peer_device);
  25. auto builder = diff_builder_t(*cluster);
  26. auto sink = dummy_sink_t(builder);
  27. auto &blocks_map = cluster->get_blocks();
  28. auto &folders = cluster->get_folders();
  29. REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
  30. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  31. auto folder = folders.by_id("1234-5678");
  32. auto &folder_infos = cluster->get_folders().by_id(folder->get_id())->get_folder_infos();
  33. REQUIRE(folder_infos.size() == 2u);
  34. auto peer_folder = folder_infos.by_device(*peer_device);
  35. auto &peer_files = peer_folder->get_file_infos();
  36. auto my_folder = folder_infos.by_device(*my_device);
  37. auto &my_files = my_folder->get_file_infos();
  38. auto file_iterator = peer_device->create_iterator(*cluster);
  39. file_iterator->activate(sink);
  40. CHECK(!file_iterator->next_need_cloning());
  41. REQUIRE(builder.configure_cluster(peer_id.get_sha256())
  42. .add(peer_id.get_sha256(), folder->get_id(), 123, 10u)
  43. .finish()
  44. .apply());
  45. CHECK(!file_iterator->next_need_cloning());
  46. SECTION("empty file cases") {
  47. SECTION("1 emtpy file") {
  48. auto file = proto::FileInfo();
  49. file.set_name("a.txt");
  50. file.set_sequence(10ul);
  51. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  52. auto f = file_iterator->next_need_cloning();
  53. REQUIRE(f);
  54. CHECK(f->get_name() == "a.txt");
  55. CHECK(!f->is_locked());
  56. REQUIRE(builder.apply());
  57. CHECK(folder->is_synchronizing());
  58. REQUIRE(!file_iterator->next_need_cloning());
  59. REQUIRE(!file_iterator->next_need_sync());
  60. REQUIRE(builder.clone_file(*f).apply());
  61. REQUIRE(!file_iterator->next_need_cloning());
  62. REQUIRE(!file_iterator->next_need_sync());
  63. CHECK(!folder->is_synchronizing());
  64. CHECK(!f->is_locked());
  65. }
  66. SECTION("2 emtpy file") {
  67. auto files = model::file_infos_map_t();
  68. auto file_1 = proto::FileInfo();
  69. file_1.set_name("a.txt");
  70. file_1.set_sequence(10ul);
  71. auto file_2 = proto::FileInfo();
  72. file_2.set_name("b.txt");
  73. file_2.set_sequence(10ul);
  74. builder.make_index(peer_id.get_sha256(), folder->get_id())
  75. .add(file_1, peer_device)
  76. .add(file_2, peer_device)
  77. .finish();
  78. REQUIRE(builder.apply());
  79. SECTION("both files are missing on my side") {
  80. auto f = file_iterator->next_need_cloning();
  81. REQUIRE(f);
  82. CHECK((f->get_name() == "a.txt" || f->get_name() == "b.txt"));
  83. CHECK(!f->is_locked());
  84. files.put(f);
  85. REQUIRE(builder.apply());
  86. CHECK(folder->is_synchronizing());
  87. REQUIRE(builder.clone_file(*f).apply());
  88. f = file_iterator->next_need_cloning();
  89. REQUIRE(f);
  90. CHECK((f->get_name() == "a.txt" || f->get_name() == "b.txt"));
  91. CHECK(!f->is_locked());
  92. files.put(f);
  93. REQUIRE(builder.apply());
  94. CHECK(folder->is_synchronizing());
  95. REQUIRE(builder.clone_file(*f).apply());
  96. REQUIRE(!file_iterator->next_need_cloning());
  97. REQUIRE(!file_iterator->next_need_sync());
  98. CHECK(!folder->is_synchronizing());
  99. CHECK(files.by_name("a.txt"));
  100. CHECK(files.by_name("b.txt"));
  101. }
  102. SECTION("1 file is missing on my side") {
  103. auto peer_file = peer_files.by_name("a.txt");
  104. REQUIRE(peer_file);
  105. REQUIRE(builder.clone_file(*peer_file).apply());
  106. auto f = file_iterator->next_need_cloning();
  107. REQUIRE(f);
  108. CHECK(f->get_name() == "b.txt");
  109. CHECK(!f->is_locked());
  110. REQUIRE(builder.apply());
  111. CHECK(folder->is_synchronizing());
  112. REQUIRE(!file_iterator->next_need_cloning());
  113. REQUIRE(builder.clone_file(*f).apply());
  114. REQUIRE(!file_iterator->next_need_cloning());
  115. REQUIRE(!file_iterator->next_need_sync());
  116. CHECK(!folder->is_synchronizing());
  117. CHECK(!f->is_locked());
  118. }
  119. SECTION("0 files are missing on my side") {
  120. auto peer_file_1 = peer_files.by_name("a.txt");
  121. auto peer_file_2 = peer_files.by_name("b.txt");
  122. REQUIRE(builder.clone_file(*peer_file_1).clone_file(*peer_file_2).apply());
  123. REQUIRE(!file_iterator->next_need_cloning());
  124. }
  125. }
  126. SECTION("invalid file is ignored") {
  127. auto file = proto::FileInfo();
  128. file.set_name("a.txt");
  129. file.set_sequence(10ul);
  130. file.set_invalid(true);
  131. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  132. REQUIRE(!file_iterator->next_need_cloning());
  133. REQUIRE(!file_iterator->next_need_cloning());
  134. CHECK(!folder->is_synchronizing());
  135. }
  136. }
  137. SECTION("non-empty single file case") {
  138. auto b = proto::BlockInfo();
  139. b.set_hash(utils::sha256_digest("12345").value());
  140. b.set_weak_hash(555);
  141. b.set_size(5ul);
  142. auto bi = block_info_t::create(b).value();
  143. auto &blocks_map = cluster->get_blocks();
  144. blocks_map.put(bi);
  145. SECTION("missing locally") {
  146. auto file = proto::FileInfo();
  147. file.set_name("a.txt");
  148. file.set_sequence(10ul);
  149. file.set_size(b.size());
  150. file.set_block_size(b.size());
  151. *file.add_blocks() = b;
  152. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  153. auto f = file_iterator->next_need_cloning();
  154. REQUIRE(f);
  155. CHECK(f->get_name() == "a.txt");
  156. CHECK(!f->is_locked());
  157. REQUIRE(builder.apply());
  158. CHECK(folder->is_synchronizing());
  159. CHECK(f->is_locked());
  160. REQUIRE(!file_iterator->next_need_cloning());
  161. REQUIRE(!file_iterator->next_need_sync());
  162. REQUIRE(builder.clone_file(*f).apply());
  163. CHECK(folder->is_synchronizing());
  164. CHECK(f->is_locked());
  165. REQUIRE(!file_iterator->next_need_cloning());
  166. f = file_iterator->next_need_sync();
  167. CHECK(folder->is_synchronizing());
  168. REQUIRE(f);
  169. REQUIRE(file_iterator->next_need_sync() == f);
  170. file_iterator->commit(f);
  171. CHECK(f->get_name() == "a.txt");
  172. CHECK(f->is_locked());
  173. CHECK(!file_iterator->next_need_sync());
  174. REQUIRE(builder.apply());
  175. auto lf = my_files.by_name(f->get_name());
  176. f->mark_local_available(0);
  177. REQUIRE(builder.finish_file_ack(*lf).apply());
  178. REQUIRE(!file_iterator->next_need_cloning());
  179. REQUIRE(!file_iterator->next_need_sync());
  180. CHECK(!folder->is_synchronizing());
  181. CHECK(!f->is_locked());
  182. }
  183. SECTION("have local, but outdated") {
  184. auto file = proto::FileInfo();
  185. file.set_name("a.txt");
  186. file.set_sequence(10ul);
  187. file.set_size(b.size());
  188. file.set_block_size(b.size());
  189. *file.add_blocks() = b;
  190. auto c_1 = file.mutable_version()->add_counters();
  191. c_1->set_id(1);
  192. c_1->set_value(1);
  193. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  194. my_files.put(my_file);
  195. auto c_2 = file.mutable_version()->add_counters();
  196. c_2->set_id(2);
  197. c_2->set_value(2);
  198. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  199. SECTION("has been scanned") {
  200. my_file->mark_local();
  201. CHECK(!file_iterator->next_need_cloning());
  202. auto f = file_iterator->next_need_sync();
  203. REQUIRE(f);
  204. CHECK(f->get_name() == "a.txt");
  205. CHECK(!f->is_locked());
  206. REQUIRE(file_iterator->next_need_sync() == f);
  207. REQUIRE(builder.apply());
  208. CHECK(folder->is_synchronizing());
  209. CHECK(f->is_locked());
  210. file_iterator->commit(f);
  211. REQUIRE(builder.apply());
  212. CHECK(!folder->is_synchronizing());
  213. CHECK(!f->is_locked());
  214. }
  215. SECTION("has not bee scanned") {
  216. CHECK(!file_iterator->next_need_cloning());
  217. CHECK(!file_iterator->next_need_sync());
  218. }
  219. }
  220. SECTION("have local, local is newer") {
  221. auto file = proto::FileInfo();
  222. file.set_name("a.txt");
  223. file.set_sequence(10ul);
  224. file.set_size(b.size());
  225. file.set_block_size(b.size());
  226. *file.add_blocks() = b;
  227. auto c_1 = file.mutable_version()->add_counters();
  228. c_1->set_id(1);
  229. c_1->set_value(1);
  230. builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish();
  231. auto c_2 = file.mutable_version()->add_counters();
  232. c_2->set_id(2);
  233. c_2->set_value(2);
  234. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  235. my_files.put(my_file);
  236. REQUIRE(builder.apply());
  237. CHECK(!file_iterator->next_need_cloning());
  238. CHECK(!file_iterator->next_need_sync());
  239. }
  240. SECTION("peer file is unreacheable") {
  241. auto file = proto::FileInfo();
  242. file.set_name("a.txt");
  243. file.set_sequence(10ul);
  244. file.set_size(b.size());
  245. file.set_block_size(b.size());
  246. *file.add_blocks() = b;
  247. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  248. auto f = file_iterator->next_need_cloning();
  249. REQUIRE(f);
  250. REQUIRE(builder.clone_file(*f).mark_reacheable(f, false).apply());
  251. file_iterator->commit(f);
  252. REQUIRE(builder.apply());
  253. CHECK(!folder->is_synchronizing());
  254. CHECK(!f->is_locked());
  255. CHECK(!file_iterator->next_need_cloning());
  256. CHECK(!file_iterator->next_need_sync());
  257. }
  258. }
  259. file_iterator->deactivate();
  260. }
  261. #if 0
  262. TEST_CASE("file iterator for 2 folders", "[model]") {
  263. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  264. auto my_device = device_t::create(my_id, "my-device").value();
  265. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  266. auto peer_device = device_t::create(peer_id, "peer-device").value();
  267. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  268. cluster->get_devices().put(my_device);
  269. cluster->get_devices().put(peer_device);
  270. auto file_iterator = file_iterator_ptr_t();
  271. auto next = [&](bool reset = false) -> file_info_ptr_t {
  272. if (reset) {
  273. file_iterator = new file_iterator_t(*cluster, peer_device);
  274. }
  275. auto f = file_iterator->next();
  276. if (f) {
  277. file_iterator->done();
  278. }
  279. return f;
  280. };
  281. auto builder = diff_builder_t(*cluster);
  282. auto &folders = cluster->get_folders();
  283. auto sha256 = peer_id.get_sha256();
  284. REQUIRE(builder.upsert_folder("1234", "/", "my-label-1").upsert_folder("5678", "/", "my-label-2").apply());
  285. REQUIRE(builder.share_folder(sha256, "1234").share_folder(sha256, "5678").apply());
  286. auto folder1 = folders.by_id("1234");
  287. auto folder2 = folders.by_id("5678");
  288. REQUIRE(builder.configure_cluster(sha256)
  289. .add(sha256, "1234", 123, 10u)
  290. .add(sha256, "5678", 1234u, 11)
  291. .finish()
  292. .apply());
  293. auto file1 = proto::FileInfo();
  294. file1.set_name("a.txt");
  295. file1.set_sequence(10ul);
  296. auto file2 = proto::FileInfo();
  297. file2.set_name("b.txt");
  298. file2.set_sequence(11ul);
  299. REQUIRE(builder.make_index(sha256, "1234").add(file1, peer_device).finish().apply());
  300. REQUIRE(builder.make_index(sha256, "5678").add(file2, peer_device).finish().apply());
  301. auto files = std::unordered_set<std::string>{};
  302. auto f = next(true);
  303. REQUIRE(f);
  304. files.emplace(f->get_full_name());
  305. f = next();
  306. REQUIRE(f);
  307. files.emplace(f->get_full_name());
  308. CHECK(files.size() == 2);
  309. CHECK(files.count("my-label-1/a.txt"));
  310. CHECK(files.count("my-label-2/b.txt"));
  311. }
  312. #endif
  313. int _init() {
  314. utils::set_default("trace");
  315. return 1;
  316. }
  317. static int v = _init();