055-resolver.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // SPDX-FileCopyrightText: 2024 Ivan Baidakou
  3. #include "test-utils.h"
  4. #include "diff-builder.h"
  5. #include "model/cluster.h"
  6. #include "model/misc/resolver.h"
  7. using namespace syncspirit;
  8. using namespace syncspirit::test;
  9. using namespace syncspirit::utils;
  10. using namespace syncspirit::model;
  11. using A = advance_action_t;
  12. TEST_CASE("resolver", "[model]") {
  13. test::init_logging();
  14. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  15. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  16. auto peer_2_id =
  17. model::device_id_t::from_string("LYXKCHX-VI3NYZR-ALCJBHF-WMZYSPK-QG6QJA3-MPFYMSO-U56GTUK-NA2MIAW").value();
  18. auto my_device = device_t::create(my_id, "my-device").value();
  19. auto peer_device = device_t::create(peer_id, "peer-device").value();
  20. auto peer_2_device = device_t::create(peer_2_id, "peer-device-2").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. cluster->get_devices().put(peer_2_device);
  26. auto &folders = cluster->get_folders();
  27. auto builder = diff_builder_t(*cluster);
  28. REQUIRE(builder.upsert_folder("1234-5678", "some/path", "my-label").apply());
  29. REQUIRE(builder.share_folder(peer_id.get_sha256(), "1234-5678").apply());
  30. REQUIRE(builder.share_folder(peer_2_id.get_sha256(), "1234-5678").apply());
  31. auto folder = folders.by_id("1234-5678");
  32. auto &folder_infos = folder->get_folder_infos();
  33. auto folder_my = folder_infos.by_device(*my_device);
  34. auto folder_peer = folder_infos.by_device(*peer_device);
  35. auto folder_peer_2 = folder_infos.by_device(*peer_2_device);
  36. auto pr_remote = proto::FileInfo();
  37. pr_remote.set_sequence(2);
  38. pr_remote.set_name("a.txt");
  39. auto *peer_v = pr_remote.mutable_version();
  40. auto *peer_c1 = peer_v->add_counters();
  41. peer_c1->set_id(1);
  42. peer_c1->set_value(2);
  43. SECTION("no local file -> copy remote") {
  44. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  45. folder_peer->add_strict(file_remote);
  46. auto action = resolve(*file_remote);
  47. CHECK(action == A::remote_copy);
  48. }
  49. SECTION("unreacheable -> ignore") {
  50. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  51. file_remote->mark_unreachable(true);
  52. folder_peer->add_strict(file_remote);
  53. auto action = resolve(*file_remote);
  54. CHECK(action == A::ignore);
  55. }
  56. SECTION("invalid -> ignore") {
  57. pr_remote.set_invalid(true);
  58. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  59. folder_peer->add_strict(file_remote);
  60. auto action = resolve(*file_remote);
  61. CHECK(action == A::ignore);
  62. }
  63. SECTION("3rd party has global version -> ignore") {
  64. auto pr_remote_2 = pr_remote;
  65. auto c2 = pr_remote_2.mutable_version()->add_counters();
  66. c2->set_id(2);
  67. c2->set_value(3);
  68. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  69. folder_peer->add_strict(file_remote);
  70. auto file_remote_2 = file_info_t::create(sequencer->next_uuid(), pr_remote_2, folder_peer_2).value();
  71. folder_peer_2->add_strict(file_remote_2);
  72. auto action = resolve(*file_remote);
  73. CHECK(action == A::ignore);
  74. }
  75. SECTION("has outdated local file, which is not scanned yet -> ignore") {
  76. auto pr_local = pr_remote;
  77. pr_local.mutable_version()->mutable_counters(0)->set_value(1);
  78. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  79. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  80. folder_peer->add_strict(file_remote);
  81. folder_my->add_strict(file_local);
  82. auto action = resolve(*file_remote);
  83. CHECK(action == A::ignore);
  84. }
  85. SECTION("has outdated local file, scanned -> copy remote (1)") {
  86. auto pr_local = pr_remote;
  87. pr_local.mutable_version()->mutable_counters(0)->set_value(1);
  88. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  89. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  90. file_local->mark_local();
  91. folder_peer->add_strict(file_remote);
  92. folder_my->add_strict(file_local);
  93. auto action = resolve(*file_remote);
  94. CHECK(action == A::remote_copy);
  95. }
  96. SECTION("has outdated local file, scanned -> copy remote (2)") {
  97. auto pr_local = pr_remote;
  98. auto c2 = pr_remote.mutable_version()->add_counters();
  99. c2->set_id(2);
  100. c2->set_value(3);
  101. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  102. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  103. file_local->mark_local();
  104. folder_peer->add_strict(file_remote);
  105. folder_my->add_strict(file_local);
  106. auto action = resolve(*file_remote);
  107. CHECK(action == A::remote_copy);
  108. }
  109. SECTION("has outdated local file, scanned & remote deleted -> copy remote (3)") {
  110. auto pr_local = pr_remote;
  111. auto c2 = pr_remote.mutable_version()->add_counters();
  112. c2->set_id(2);
  113. c2->set_value(3);
  114. pr_remote.set_deleted(true);
  115. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  116. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  117. file_local->mark_local();
  118. folder_peer->add_strict(file_remote);
  119. folder_my->add_strict(file_local);
  120. auto action = resolve(*file_remote);
  121. CHECK(action == A::remote_copy);
  122. }
  123. SECTION("has newer local file, scanned -> ignore (1)") {
  124. auto pr_local = pr_remote;
  125. pr_local.mutable_version()->mutable_counters(0)->set_value(2);
  126. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  127. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  128. file_local->mark_local();
  129. folder_peer->add_strict(file_remote);
  130. folder_my->add_strict(file_local);
  131. auto action = resolve(*file_remote);
  132. CHECK(action == A::ignore);
  133. }
  134. SECTION("has newer local file, scanned -> ignore (2)") {
  135. auto pr_local = pr_remote;
  136. auto c2 = pr_local.mutable_version()->add_counters();
  137. c2->set_id(2);
  138. c2->set_value(3);
  139. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  140. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  141. file_local->mark_local();
  142. folder_peer->add_strict(file_remote);
  143. folder_my->add_strict(file_local);
  144. auto action = resolve(*file_remote);
  145. CHECK(action == A::ignore);
  146. }
  147. SECTION("local & remote are both deleted-> ignore") {
  148. pr_remote.set_deleted(true);
  149. auto pr_local = pr_remote;
  150. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  151. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  152. file_local->mark_local();
  153. folder_peer->add_strict(file_remote);
  154. folder_my->add_strict(file_local);
  155. auto action = resolve(*file_remote);
  156. CHECK(action == A::ignore);
  157. }
  158. SECTION("conflicts") {
  159. auto pr_local = pr_remote;
  160. SECTION("remote deleted, locally modified -> ignore") {
  161. auto c2_remote = pr_remote.mutable_version()->add_counters();
  162. c2_remote->set_id(2);
  163. c2_remote->set_value(3);
  164. auto c2_local = pr_local.mutable_version()->add_counters();
  165. c2_local->set_id(3);
  166. c2_local->set_value(3);
  167. pr_remote.set_deleted(true);
  168. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  169. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  170. file_local->mark_local();
  171. folder_peer->add_strict(file_remote);
  172. folder_my->add_strict(file_local);
  173. auto action = resolve(*file_remote);
  174. CHECK(action == A::ignore);
  175. }
  176. SECTION("remote deleted, locally modified -> ignore (2)") {
  177. auto c2_remote = pr_remote.mutable_version()->mutable_counters(0);
  178. auto v = c2_remote->value() + 1;
  179. c2_remote->set_value(v);
  180. auto c2_local = pr_local.mutable_version()->add_counters();
  181. c2_local->set_id(3);
  182. c2_local->set_value(v);
  183. pr_remote.set_deleted(true);
  184. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  185. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  186. file_local->mark_local();
  187. folder_peer->add_strict(file_remote);
  188. folder_my->add_strict(file_local);
  189. auto action = resolve(*file_remote);
  190. CHECK(action == A::ignore);
  191. }
  192. SECTION("locally deleted, remotely modified -> resurrect remote") {
  193. auto c2_remote = pr_remote.mutable_version()->add_counters();
  194. c2_remote->set_id(2);
  195. c2_remote->set_value(3);
  196. auto c2_local = pr_local.mutable_version()->add_counters();
  197. c2_local->set_id(3);
  198. c2_local->set_value(1);
  199. pr_local.set_deleted(true);
  200. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  201. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  202. file_local->mark_local();
  203. folder_peer->add_strict(file_remote);
  204. folder_my->add_strict(file_local);
  205. auto action = resolve(*file_remote);
  206. CHECK(action == A::remote_copy);
  207. }
  208. SECTION("locally mod > remote mod -> ignore(local_win)") {
  209. auto c2_remote = pr_remote.mutable_version()->add_counters();
  210. c2_remote->set_id(2);
  211. c2_remote->set_value(3);
  212. pr_remote.set_modified_s(1734680000);
  213. auto c2_local = pr_local.mutable_version()->add_counters();
  214. c2_local->set_id(3);
  215. c2_local->set_value(4);
  216. pr_local.set_modified_s(1734690000);
  217. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  218. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  219. file_local->mark_local();
  220. folder_peer->add_strict(file_remote);
  221. folder_my->add_strict(file_local);
  222. auto action = resolve(*file_remote);
  223. CHECK(action == A::ignore);
  224. }
  225. SECTION("locally mod < remote mod -> remote_win") {
  226. auto c2_remote = pr_remote.mutable_version()->add_counters();
  227. c2_remote->set_id(2);
  228. c2_remote->set_value(4);
  229. pr_remote.set_modified_s(1734680000);
  230. auto c2_local = pr_local.mutable_version()->add_counters();
  231. c2_local->set_id(3);
  232. c2_local->set_value(3);
  233. pr_local.set_modified_s(1734670000);
  234. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  235. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  236. file_local->mark_local();
  237. folder_peer->add_strict(file_remote);
  238. folder_my->add_strict(file_local);
  239. auto action = resolve(*file_remote);
  240. CHECK(action == A::resolve_remote_win);
  241. }
  242. SECTION("locally version == remote version -> local_win(ignore), by device") {
  243. auto c2_remote = pr_remote.mutable_version()->add_counters();
  244. c2_remote->set_id(2);
  245. c2_remote->set_value(4);
  246. pr_remote.set_modified_s(1734680000);
  247. auto c2_local = pr_local.mutable_version()->add_counters();
  248. c2_local->set_id(3);
  249. c2_local->set_value(4);
  250. pr_local.set_modified_s(1734680000);
  251. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  252. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  253. file_local->mark_local();
  254. folder_peer->add_strict(file_remote);
  255. folder_my->add_strict(file_local);
  256. auto action = resolve(*file_remote);
  257. CHECK(action == A::ignore);
  258. }
  259. SECTION("no recursive conflicts are allowed -> ignore") {
  260. auto name = std::string("a.txt..sync-conflict-20060102-150405-XBOWT");
  261. pr_remote.set_name(name);
  262. pr_local.set_name(name);
  263. auto c2_remote = pr_remote.mutable_version()->add_counters();
  264. c2_remote->set_id(2);
  265. c2_remote->set_value(4);
  266. auto c2_local = pr_local.mutable_version()->add_counters();
  267. c2_local->set_id(3);
  268. c2_local->set_value(4);
  269. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  270. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  271. file_local->mark_local();
  272. folder_peer->add_strict(file_remote);
  273. folder_my->add_strict(file_local);
  274. auto action = resolve(*file_remote);
  275. CHECK(action == A::ignore);
  276. }
  277. SECTION("locally version < remote version, already resolved -> ignore") {
  278. auto c2_remote = pr_remote.mutable_version()->add_counters();
  279. c2_remote->set_id(2);
  280. c2_remote->set_value(4);
  281. auto c2_local = pr_local.mutable_version()->add_counters();
  282. c2_local->set_id(3);
  283. c2_local->set_value(3);
  284. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  285. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  286. file_local->mark_local();
  287. folder_my->add_strict(file_local);
  288. folder_peer->add_strict(file_remote);
  289. pr_local.mutable_version()->clear_counters();
  290. auto c2_local_r = pr_local.mutable_version()->add_counters();
  291. (void)c2_local_r;
  292. c2_local->set_id(3);
  293. c2_local->set_value(1);
  294. pr_local.set_name(file_local->make_conflicting_name());
  295. pr_local.set_sequence(folder_my->get_max_sequence() + 1);
  296. auto file_resolved = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  297. file_resolved->mark_local();
  298. REQUIRE(folder_my->add_strict(file_resolved));
  299. auto action = resolve(*file_remote);
  300. CHECK(action == A::ignore);
  301. }
  302. SECTION("locally version > remote version, already resolved -> ignore") {
  303. auto c2_remote = pr_remote.mutable_version()->add_counters();
  304. c2_remote->set_id(2);
  305. c2_remote->set_value(3);
  306. auto c2_local = pr_local.mutable_version()->add_counters();
  307. c2_local->set_id(3);
  308. c2_local->set_value(4);
  309. auto file_remote = file_info_t::create(sequencer->next_uuid(), pr_remote, folder_peer).value();
  310. auto file_local = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  311. file_local->mark_local();
  312. folder_my->add_strict(file_local);
  313. folder_peer->add_strict(file_remote);
  314. pr_local.mutable_version()->clear_counters();
  315. auto c2_local_r = pr_local.mutable_version()->add_counters();
  316. (void)c2_local_r;
  317. c2_local->set_id(3);
  318. c2_local->set_value(1);
  319. pr_local.set_name(file_remote->make_conflicting_name());
  320. pr_local.set_sequence(folder_my->get_max_sequence() + 1);
  321. auto file_resolved = file_info_t::create(sequencer->next_uuid(), pr_local, folder_my).value();
  322. file_resolved->mark_local();
  323. REQUIRE(folder_my->add_strict(file_resolved));
  324. auto action = resolve(*file_remote);
  325. CHECK(action == A::ignore);
  326. }
  327. }
  328. }