050-file_iterator.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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. #include <set>
  9. using namespace syncspirit;
  10. using namespace syncspirit::test;
  11. using namespace syncspirit::model;
  12. using R = file_iterator_t::result_t;
  13. using A = advance_action_t;
  14. TEST_CASE("file iterator, single folder", "[model]") {
  15. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  16. auto my_device = device_t::create(my_id, "my-device").value();
  17. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  18. auto peer_device = device_t::create(peer_id, "peer-device").value();
  19. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  20. auto sequencer = make_sequencer(4);
  21. cluster->get_devices().put(my_device);
  22. cluster->get_devices().put(peer_device);
  23. auto builder = diff_builder_t(*cluster);
  24. auto &blocks_map = cluster->get_blocks();
  25. auto &folders = cluster->get_folders();
  26. REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
  27. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  28. auto folder = folders.by_id("1234-5678");
  29. auto &folder_infos = cluster->get_folders().by_id(folder->get_id())->get_folder_infos();
  30. REQUIRE(folder_infos.size() == 2u);
  31. auto peer_folder = folder_infos.by_device(*peer_device);
  32. auto &peer_files = peer_folder->get_file_infos();
  33. auto my_folder = folder_infos.by_device(*my_device);
  34. auto &my_files = my_folder->get_file_infos();
  35. auto file_iterator = peer_device->create_iterator(*cluster);
  36. SECTION("emtpy folders (1)") { CHECK(file_iterator->next() == R{nullptr, A::ignore}); }
  37. REQUIRE(builder.configure_cluster(peer_id.get_sha256())
  38. .add(peer_id.get_sha256(), folder->get_id(), 123, 10u)
  39. .finish()
  40. .apply());
  41. SECTION("emtpy folders (2)") { CHECK(file_iterator->next() == R{nullptr, A::ignore}); }
  42. SECTION("cloning (empty files)") {
  43. SECTION("1 file") {
  44. SECTION("no local file") {
  45. auto file = proto::FileInfo();
  46. file.set_name("a.txt");
  47. file.set_sequence(10ul);
  48. auto ec =
  49. builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply();
  50. REQUIRE(ec);
  51. auto [f, action] = file_iterator->next();
  52. REQUIRE(f);
  53. CHECK(f->get_name() == "a.txt");
  54. CHECK(!f->is_locked());
  55. CHECK(action == A::remote_copy);
  56. REQUIRE(builder.apply());
  57. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  58. REQUIRE(builder.remote_copy(*f).apply());
  59. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  60. CHECK(!f->is_locked());
  61. }
  62. SECTION("invalid file is ignored") {
  63. auto file = proto::FileInfo();
  64. file.set_name("a.txt");
  65. file.set_sequence(10ul);
  66. file.set_invalid(true);
  67. REQUIRE(builder.apply());
  68. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  69. }
  70. SECTION("version checks") {
  71. auto file = proto::FileInfo();
  72. file.set_name("a.txt");
  73. file.set_sequence(10ul);
  74. auto c_1 = file.mutable_version()->add_counters();
  75. c_1->set_id(1);
  76. c_1->set_value(5);
  77. builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish();
  78. SECTION("my version < peer version") {
  79. c_1->set_value(3);
  80. REQUIRE(builder.apply());
  81. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  82. my_file->mark_local();
  83. my_files.put(my_file);
  84. auto [f, action] = file_iterator->next();
  85. REQUIRE(f);
  86. CHECK(action == A::remote_copy);
  87. CHECK(f->get_folder_info()->get_device() == peer_device.get());
  88. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  89. }
  90. SECTION("my version < peer version, but not scanned yet") {
  91. REQUIRE(builder.apply());
  92. c_1->set_value(3);
  93. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  94. my_files.put(my_file);
  95. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  96. }
  97. SECTION("my version > peer version") {
  98. REQUIRE(builder.apply());
  99. c_1->set_value(10);
  100. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  101. my_file->mark_local();
  102. my_files.put(my_file);
  103. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  104. }
  105. SECTION("my version == peer version") {
  106. REQUIRE(builder.apply());
  107. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  108. my_file->mark_local();
  109. my_files.put(my_file);
  110. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  111. }
  112. }
  113. }
  114. SECTION("2+ files") {
  115. auto files = model::file_infos_map_t();
  116. auto file_1 = proto::FileInfo();
  117. file_1.set_name("a.txt");
  118. file_1.set_sequence(10ul);
  119. auto file_2 = proto::FileInfo();
  120. file_2.set_name("b.txt");
  121. file_2.set_sequence(11ul);
  122. builder.make_index(peer_id.get_sha256(), folder->get_id())
  123. .add(file_1, peer_device)
  124. .add(file_2, peer_device)
  125. .finish();
  126. REQUIRE(builder.apply());
  127. SECTION("both files are missing on my side") {
  128. auto [f, action] = file_iterator->next();
  129. REQUIRE(f);
  130. CHECK(action == A::remote_copy);
  131. CHECK((f->get_name() == "a.txt" || f->get_name() == "b.txt"));
  132. CHECK(!f->is_locked());
  133. files.put(f);
  134. REQUIRE(builder.apply());
  135. REQUIRE(builder.remote_copy(*f).apply());
  136. std::tie(f, action) = file_iterator->next();
  137. REQUIRE(f);
  138. CHECK((f->get_name() == "a.txt" || f->get_name() == "b.txt"));
  139. CHECK(!f->is_locked());
  140. CHECK(action == A::remote_copy);
  141. files.put(f);
  142. REQUIRE(builder.apply());
  143. REQUIRE(builder.remote_copy(*f).apply());
  144. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  145. CHECK(files.by_name("a.txt"));
  146. CHECK(files.by_name("b.txt"));
  147. }
  148. SECTION("1 file is missing on my side") {
  149. auto peer_file = peer_files.by_name("a.txt");
  150. REQUIRE(peer_file);
  151. REQUIRE(builder.remote_copy(*peer_file).apply());
  152. auto [f, action] = file_iterator->next();
  153. REQUIRE(f);
  154. CHECK(action == A::remote_copy);
  155. CHECK(f->get_name() == "b.txt");
  156. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  157. REQUIRE(builder.apply());
  158. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  159. REQUIRE(builder.remote_copy(*f).apply());
  160. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  161. CHECK(!f->is_locked());
  162. }
  163. SECTION("0 files are missing on my side") {
  164. auto peer_file_1 = peer_files.by_name("a.txt");
  165. auto peer_file_2 = peer_files.by_name("b.txt");
  166. REQUIRE(builder.remote_copy(*peer_file_1).remote_copy(*peer_file_2).apply());
  167. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  168. }
  169. SECTION("new file in new peer update") {
  170. auto [f_1, action_1] = file_iterator->next();
  171. REQUIRE(f_1);
  172. CHECK(action_1 == A::remote_copy);
  173. CHECK((f_1->get_name() == "a.txt" || f_1->get_name() == "b.txt"));
  174. CHECK(!f_1->is_locked());
  175. files.put(f_1);
  176. auto [f_2, action_2] = file_iterator->next();
  177. REQUIRE(f_2);
  178. CHECK(action_2 == A::remote_copy);
  179. CHECK((f_2->get_name() == "a.txt" || f_2->get_name() == "b.txt"));
  180. CHECK(!f_2->is_locked());
  181. files.put(f_2);
  182. CHECK(files.by_name("a.txt"));
  183. CHECK(files.by_name("b.txt"));
  184. auto file_3 = proto::FileInfo();
  185. file_3.set_name("c.txt");
  186. file_3.set_sequence(12ul);
  187. auto ec = builder.make_index(peer_id.get_sha256(), folder->get_id())
  188. .add(file_3, peer_device)
  189. .finish()
  190. .apply();
  191. auto [f_3, action_3] = file_iterator->next();
  192. REQUIRE(f_3);
  193. CHECK(action_3 == A::remote_copy);
  194. CHECK(f_3->get_name() == "c.txt");
  195. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  196. }
  197. }
  198. }
  199. SECTION("synchronizing non-empty files") {
  200. auto b1 = proto::BlockInfo();
  201. b1.set_hash(utils::sha256_digest("12345").value());
  202. b1.set_weak_hash(555);
  203. b1.set_size(5ul);
  204. auto b2 = proto::BlockInfo();
  205. b1.set_hash(utils::sha256_digest("67890").value());
  206. b1.set_weak_hash(123);
  207. b1.set_size(5ul);
  208. auto &blocks_map = cluster->get_blocks();
  209. blocks_map.put(block_info_t::create(b1).value());
  210. blocks_map.put(block_info_t::create(b2).value());
  211. SECTION("missing locally") {
  212. auto file = proto::FileInfo();
  213. file.set_name("a.txt");
  214. file.set_sequence(10ul);
  215. file.set_size(b1.size());
  216. file.set_block_size(b1.size());
  217. *file.add_blocks() = b1;
  218. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  219. auto [f, action] = file_iterator->next();
  220. REQUIRE(f);
  221. CHECK(action == A::remote_copy);
  222. CHECK(f->get_name() == "a.txt");
  223. CHECK(!f->is_locked());
  224. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  225. }
  226. SECTION("have local, but outdated") {
  227. auto file = proto::FileInfo();
  228. file.set_name("a.txt");
  229. file.set_sequence(10ul);
  230. file.set_size(b1.size());
  231. file.set_block_size(b1.size());
  232. *file.add_blocks() = b1;
  233. auto c_1 = file.mutable_version()->add_counters();
  234. c_1->set_id(1);
  235. c_1->set_value(1);
  236. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  237. my_files.put(my_file);
  238. auto c_2 = file.mutable_version()->add_counters();
  239. c_2->set_id(2);
  240. c_2->set_value(2);
  241. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  242. SECTION("has been scanned") {
  243. my_file->mark_local();
  244. auto [f, action] = file_iterator->next();
  245. REQUIRE(f);
  246. CHECK(action == A::remote_copy);
  247. CHECK(f->get_name() == "a.txt");
  248. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  249. REQUIRE(builder.apply());
  250. }
  251. SECTION("has not bee scanned") { CHECK(file_iterator->next() == R{nullptr, A::ignore}); }
  252. }
  253. SECTION("have local, local is newer") {
  254. auto file = proto::FileInfo();
  255. file.set_name("a.txt");
  256. file.set_sequence(10ul);
  257. file.set_size(b1.size());
  258. file.set_block_size(b1.size());
  259. *file.add_blocks() = b1;
  260. auto c_1 = file.mutable_version()->add_counters();
  261. c_1->set_id(1);
  262. c_1->set_value(1);
  263. builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish();
  264. auto c_2 = file.mutable_version()->add_counters();
  265. c_2->set_id(2);
  266. c_2->set_value(2);
  267. auto my_file = file_info_t::create(sequencer->next_uuid(), file, my_folder).value();
  268. my_files.put(my_file);
  269. REQUIRE(builder.apply());
  270. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  271. }
  272. SECTION("peer file is unreacheable") {
  273. auto file = proto::FileInfo();
  274. file.set_name("a.txt");
  275. file.set_sequence(10ul);
  276. file.set_size(b1.size());
  277. file.set_block_size(b1.size());
  278. *file.add_blocks() = b1;
  279. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  280. auto f = peer_files.by_name(file.name());
  281. REQUIRE(builder.remote_copy(*f).mark_reacheable(f, false).apply());
  282. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  283. }
  284. }
  285. }
  286. TEST_CASE("file iterator for 2 folders", "[model]") {
  287. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  288. auto my_device = device_t::create(my_id, "my-device").value();
  289. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  290. auto peer_device = device_t::create(peer_id, "peer-device").value();
  291. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  292. auto sequencer = make_sequencer(4);
  293. cluster->get_devices().put(my_device);
  294. cluster->get_devices().put(peer_device);
  295. auto builder = diff_builder_t(*cluster);
  296. auto &blocks_map = cluster->get_blocks();
  297. auto &folders = cluster->get_folders();
  298. REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
  299. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  300. auto folder = folders.by_id("1234-5678");
  301. auto &folder_infos = cluster->get_folders().by_id(folder->get_id())->get_folder_infos();
  302. REQUIRE(folder_infos.size() == 2u);
  303. auto peer_folder = folder_infos.by_device(*peer_device);
  304. auto &peer_files = peer_folder->get_file_infos();
  305. auto my_folder = folder_infos.by_device(*my_device);
  306. auto &my_files = my_folder->get_file_infos();
  307. auto file_iterator = peer_device->create_iterator(*cluster);
  308. auto sha256 = peer_id.get_sha256();
  309. REQUIRE(builder.upsert_folder("1234", "/", "my-label-1").upsert_folder("5678", "/", "my-label-2").apply());
  310. REQUIRE(builder.share_folder(sha256, "1234").share_folder(sha256, "5678").apply());
  311. auto folder1 = folders.by_id("1234");
  312. auto folder2 = folders.by_id("5678");
  313. REQUIRE(builder.configure_cluster(sha256)
  314. .add(sha256, "1234", 123, 10u)
  315. .add(sha256, "5678", 1234u, 11)
  316. .finish()
  317. .apply());
  318. auto file1 = proto::FileInfo();
  319. file1.set_name("a.txt");
  320. file1.set_sequence(10ul);
  321. auto file2 = proto::FileInfo();
  322. file2.set_name("b.txt");
  323. file2.set_sequence(11ul);
  324. using set_t = std::set<std::string_view>;
  325. SECTION("cloning") {
  326. REQUIRE(builder.make_index(sha256, "1234").add(file1, peer_device).add(file2, peer_device).finish().apply());
  327. auto [f1, action1] = file_iterator->next();
  328. auto [f2, action2] = file_iterator->next();
  329. REQUIRE(f1);
  330. REQUIRE(f2);
  331. CHECK(action1 == A::remote_copy);
  332. CHECK(action2 == A::remote_copy);
  333. auto files = set_t{};
  334. files.emplace(f1->get_name());
  335. files.emplace(f2->get_name());
  336. CHECK((files == set_t{"a.txt", "b.txt"}));
  337. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  338. }
  339. SECTION("syncing") {
  340. auto b1 = proto::BlockInfo();
  341. b1.set_hash(utils::sha256_digest("12345").value());
  342. b1.set_weak_hash(555);
  343. b1.set_size(5ul);
  344. auto b2 = proto::BlockInfo();
  345. b2.set_hash(utils::sha256_digest("67890").value());
  346. b2.set_weak_hash(123);
  347. b2.set_size(5ul);
  348. auto &blocks_map = cluster->get_blocks();
  349. blocks_map.put(block_info_t::create(b1).value());
  350. blocks_map.put(block_info_t::create(b2).value());
  351. *file1.add_blocks() = b1;
  352. file1.set_size(b1.size());
  353. *file2.add_blocks() = b2;
  354. file2.set_size(b2.size());
  355. using set_t = std::set<std::string_view>;
  356. REQUIRE(builder.make_index(sha256, "1234").add(file1, peer_device).add(file2, peer_device).finish().apply());
  357. auto files = set_t{};
  358. auto [f1, action1] = file_iterator->next();
  359. REQUIRE(f1);
  360. CHECK(action1 == A::remote_copy);
  361. files.emplace(f1->get_name());
  362. auto [f2, action2] = file_iterator->next();
  363. REQUIRE(f2);
  364. CHECK(action2 == A::remote_copy);
  365. files.emplace(f2->get_name());
  366. files.emplace(f1->get_name());
  367. files.emplace(f2->get_name());
  368. CHECK((files == set_t{"a.txt", "b.txt"}));
  369. }
  370. }
  371. TEST_CASE("file iterator, create, share, iterae, unshare, share, iterate", "[model]") {
  372. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  373. auto my_device = device_t::create(my_id, "my-device").value();
  374. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  375. auto peer_device = device_t::create(peer_id, "peer-device").value();
  376. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  377. auto sequencer = make_sequencer(4);
  378. cluster->get_devices().put(my_device);
  379. cluster->get_devices().put(peer_device);
  380. auto builder = diff_builder_t(*cluster);
  381. auto &folders = cluster->get_folders();
  382. REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
  383. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  384. auto folder = folders.by_id("1234-5678");
  385. auto folder_infos = &folder->get_folder_infos();
  386. REQUIRE(folder_infos->size() == 2u);
  387. auto file_iterator = peer_device->create_iterator(*cluster);
  388. auto file = proto::FileInfo();
  389. file.set_name("a.txt");
  390. file.set_sequence(10ul);
  391. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  392. auto [f, action] = file_iterator->next();
  393. REQUIRE(f);
  394. CHECK(f->get_name() == "a.txt");
  395. CHECK(!f->is_locked());
  396. CHECK(action == A::remote_copy);
  397. REQUIRE(builder.apply());
  398. CHECK(file_iterator->next() == R{nullptr, A::ignore});
  399. REQUIRE(builder.remove_folder(*folder).apply());
  400. REQUIRE(builder.upsert_folder("1234-5678", "/my/path").apply());
  401. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  402. folder = folders.by_id("1234-5678");
  403. folder_infos = &folder->get_folder_infos();
  404. REQUIRE(folder_infos->size() == 2u);
  405. REQUIRE(builder.make_index(peer_id.get_sha256(), folder->get_id()).add(file, peer_device).finish().apply());
  406. std::tie(f, action) = file_iterator->next();
  407. REQUIRE(f);
  408. CHECK(action == A::remote_copy);
  409. CHECK(f->get_name() == "a.txt");
  410. }
  411. int _init() {
  412. utils::set_default("trace");
  413. return 1;
  414. }
  415. static int v = _init();