operators_tests.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include <catch.hpp>
  8. #include <rpl/rpl.h>
  9. #include <string>
  10. using namespace rpl;
  11. class OnDestructor {
  12. public:
  13. OnDestructor(std::function<void()> callback)
  14. : _callback(std::move(callback)) {
  15. }
  16. ~OnDestructor() {
  17. if (_callback) {
  18. _callback();
  19. }
  20. }
  21. private:
  22. std::function<void()> _callback;
  23. };
  24. class InvokeCounter {
  25. public:
  26. InvokeCounter(
  27. const std::shared_ptr<int> &copyCounter,
  28. const std::shared_ptr<int> &moveCounter)
  29. : _copyCounter(copyCounter)
  30. , _moveCounter(moveCounter) {
  31. }
  32. InvokeCounter(const InvokeCounter &other)
  33. : _copyCounter(other._copyCounter)
  34. , _moveCounter(other._moveCounter) {
  35. if (_copyCounter) {
  36. ++*_copyCounter;
  37. }
  38. }
  39. InvokeCounter(InvokeCounter &&other)
  40. : _copyCounter(details::take(other._copyCounter))
  41. , _moveCounter(details::take(other._moveCounter)) {
  42. if (_moveCounter) {
  43. ++*_moveCounter;
  44. }
  45. }
  46. InvokeCounter &operator=(const InvokeCounter &other) {
  47. _copyCounter = other._copyCounter;
  48. _moveCounter = other._moveCounter;
  49. if (_copyCounter) {
  50. ++*_copyCounter;
  51. }
  52. return *this;
  53. }
  54. InvokeCounter &operator=(InvokeCounter &&other) {
  55. _copyCounter = details::take(other._copyCounter);
  56. _moveCounter = details::take(other._moveCounter);
  57. if (_moveCounter) {
  58. ++*_moveCounter;
  59. }
  60. return *this;
  61. }
  62. private:
  63. std::shared_ptr<int> _copyCounter;
  64. std::shared_ptr<int> _moveCounter;
  65. };
  66. TEST_CASE("basic operators tests", "[rpl::operators]") {
  67. SECTION("single test") {
  68. auto sum = std::make_shared<int>(0);
  69. auto doneGenerated = std::make_shared<bool>(false);
  70. auto destroyed = std::make_shared<bool>(false);
  71. auto copyCount = std::make_shared<int>(0);
  72. auto moveCount = std::make_shared<int>(0);
  73. {
  74. InvokeCounter counter(copyCount, moveCount);
  75. auto destroyCalled = std::make_shared<OnDestructor>([=] {
  76. *destroyed = true;
  77. });
  78. rpl::lifetime lifetime;
  79. single(std::move(counter))
  80. | start_with_next_error_done([=](InvokeCounter&&) {
  81. (void)destroyCalled;
  82. ++*sum;
  83. }, [=](no_error) {
  84. (void)destroyCalled;
  85. }, [=] {
  86. (void)destroyCalled;
  87. *doneGenerated = true;
  88. }, lifetime);
  89. }
  90. REQUIRE(*sum == 1);
  91. REQUIRE(*doneGenerated);
  92. REQUIRE(*destroyed);
  93. REQUIRE(*copyCount == 0);
  94. }
  95. SECTION("then test") {
  96. auto sum = std::make_shared<int>(0);
  97. auto doneGenerated = std::make_shared<bool>(false);
  98. auto destroyed = std::make_shared<bool>(false);
  99. auto copyCount = std::make_shared<int>(0);
  100. auto moveCount = std::make_shared<int>(0);
  101. {
  102. auto testing = complete<InvokeCounter>() | type_erased();
  103. for (auto i = 0; i != 5; ++i) {
  104. InvokeCounter counter(copyCount, moveCount);
  105. testing = std::move(testing)
  106. | then(single(std::move(counter)));
  107. }
  108. auto destroyCalled = std::make_shared<OnDestructor>([=] {
  109. *destroyed = true;
  110. });
  111. rpl::lifetime lifetime;
  112. std::move(testing)
  113. | then(complete<InvokeCounter>())
  114. | start_with_next_error_done([=](InvokeCounter&&) {
  115. (void)destroyCalled;
  116. ++*sum;
  117. }, [=](no_error) {
  118. (void)destroyCalled;
  119. }, [=] {
  120. (void)destroyCalled;
  121. *doneGenerated = true;
  122. }, lifetime);
  123. }
  124. REQUIRE(*sum == 5);
  125. REQUIRE(*doneGenerated);
  126. REQUIRE(*destroyed);
  127. REQUIRE(*copyCount == 0);
  128. }
  129. SECTION("map test") {
  130. auto sum = std::make_shared<std::string>("");
  131. {
  132. rpl::lifetime lifetime;
  133. single(1)
  134. | then(single(2))
  135. | then(single(3))
  136. | then(single(4))
  137. | then(single(5))
  138. | map([](int value) {
  139. return std::to_string(value);
  140. })
  141. | start_with_next([=](std::string &&value) {
  142. *sum += std::move(value) + ' ';
  143. }, lifetime);
  144. }
  145. REQUIRE(*sum == "1 2 3 4 5 ");
  146. }
  147. SECTION("deferred test") {
  148. auto launched = std::make_shared<int>(0);
  149. auto checked = std::make_shared<int>(0);
  150. {
  151. rpl::lifetime lifetime;
  152. auto make_next = [=] {
  153. return deferred([=] {
  154. return single(++*launched);
  155. });
  156. };
  157. make_next()
  158. | then(make_next())
  159. | then(make_next())
  160. | then(make_next())
  161. | then(make_next())
  162. | start_with_next([=](int value) {
  163. REQUIRE(++*checked == *launched);
  164. REQUIRE(*checked == value);
  165. }, lifetime);
  166. REQUIRE(*launched == 5);
  167. }
  168. }
  169. SECTION("filter test") {
  170. auto sum = std::make_shared<std::string>("");
  171. {
  172. rpl::lifetime lifetime;
  173. single(1)
  174. | then(single(1))
  175. | then(single(2))
  176. | then(single(2))
  177. | then(single(3))
  178. | filter([](int value) { return value != 2; })
  179. | map([](int value) {
  180. return std::to_string(value);
  181. })
  182. | start_with_next([=](std::string &&value) {
  183. *sum += std::move(value) + ' ';
  184. }, lifetime);
  185. }
  186. REQUIRE(*sum == "1 1 3 ");
  187. }
  188. SECTION("filter tuple test") {
  189. auto sum = std::make_shared<std::string>("");
  190. {
  191. auto lifetime = single(std::make_tuple(1, 2))
  192. | then(single(std::make_tuple(1, 2)))
  193. | then(single(std::make_tuple(2, 3)))
  194. | then(single(std::make_tuple(2, 3)))
  195. | then(single(std::make_tuple(3, 4)))
  196. | filter([](auto first, auto second) { return first != 2; })
  197. | map([](auto first, auto second) {
  198. return std::to_string(second);
  199. })
  200. | start_with_next([=](std::string &&value) {
  201. *sum += std::move(value) + ' ';
  202. });
  203. }
  204. REQUIRE(*sum == "2 2 4 ");
  205. }
  206. SECTION("distinct_until_changed test") {
  207. auto sum = std::make_shared<std::string>("");
  208. {
  209. rpl::lifetime lifetime;
  210. single(1)
  211. | then(single(1))
  212. | then(single(2))
  213. | then(single(2))
  214. | then(single(3))
  215. | distinct_until_changed()
  216. | map([](int value) {
  217. return std::to_string(value);
  218. })
  219. | start_with_next([=](std::string &&value) {
  220. *sum += std::move(value) + ' ';
  221. }, lifetime);
  222. }
  223. REQUIRE(*sum == "1 2 3 ");
  224. }
  225. SECTION("flatten_latest test") {
  226. auto sum = std::make_shared<std::string>("");
  227. {
  228. rpl::lifetime lifetime;
  229. {
  230. event_stream<int> stream;
  231. single(single(1) | then(single(2)))
  232. | then(single(single(3) | then(single(4))))
  233. | then(single(single(5) | then(stream.events())))
  234. | flatten_latest()
  235. | map([](int value) {
  236. return std::to_string(value);
  237. })
  238. | start_with_next_done([=](std::string &&value) {
  239. *sum += std::move(value) + ' ';
  240. }, [=] {
  241. *sum += "done ";
  242. }, lifetime);
  243. stream.fire(6);
  244. }
  245. single(single(1))
  246. | then(single(single(2) | then(single(3))))
  247. | then(single(single(4) | then(single(5)) | then(single(6))))
  248. | flatten_latest()
  249. | map([](int value) {
  250. return std::to_string(value);
  251. })
  252. | start_with_next([=](std::string &&value) {
  253. *sum += std::move(value) + ' ';
  254. }, lifetime);
  255. }
  256. REQUIRE(*sum == "1 2 3 4 5 6 done 1 2 3 4 5 6 ");
  257. }
  258. SECTION("combine vector test") {
  259. auto sum = std::make_shared<std::string>("");
  260. {
  261. rpl::lifetime lifetime;
  262. event_stream<bool> a;
  263. event_stream<bool> b;
  264. event_stream<bool> c;
  265. std::vector<producer<bool>> v;
  266. v.push_back(a.events());
  267. v.push_back(b.events());
  268. v.push_back(c.events());
  269. combine(std::move(v), [](const auto &values) {
  270. return values[0] && values[1] && !values[2];
  271. })
  272. | start_with_next([=](bool value) {
  273. *sum += std::to_string(value ? 1 : 0);
  274. }, lifetime);
  275. a.fire(true);
  276. b.fire(true);
  277. c.fire(false);
  278. a.fire(false);
  279. b.fire(true);
  280. a.fire(true);
  281. c.fire(true);
  282. }
  283. REQUIRE(*sum == "10010");
  284. }
  285. SECTION("combine test") {
  286. auto sum = std::make_shared<std::string>("");
  287. {
  288. rpl::lifetime lifetime;
  289. event_stream<int> a;
  290. event_stream<short> b;
  291. event_stream<char> c;
  292. combine(
  293. a.events(),
  294. b.events(),
  295. c.events(),
  296. [](long a, long b, long c) {
  297. return a;
  298. })
  299. | start_with_next([=](int value) {
  300. *sum += std::to_string(value);
  301. }, lifetime);
  302. combine(
  303. a.events(),
  304. b.events(),
  305. c.events(),
  306. [](auto &&value) {
  307. return std::get<1>(value);
  308. })
  309. | start_with_next([=](int value) {
  310. *sum += std::to_string(value);
  311. }, lifetime);
  312. combine(a.events(), b.events(), c.events())
  313. | map([](auto &&value) {
  314. return std::make_tuple(
  315. std::to_string(std::get<0>(value)),
  316. std::to_string(std::get<1>(value)),
  317. std::to_string(std::get<2>(value)));
  318. })
  319. | start_with_next([=](auto &&value) {
  320. *sum += std::get<0>(value) + ' '
  321. + std::get<1>(value) + ' '
  322. + std::get<2>(value) + ' ';
  323. }, lifetime);
  324. a.fire(1);
  325. b.fire(2);
  326. c.fire(3);
  327. a.fire(4);
  328. b.fire(5);
  329. c.fire(6);
  330. }
  331. REQUIRE(*sum == "121 2 3 424 2 3 454 5 3 454 5 6 ");
  332. }
  333. SECTION("mappers test") {
  334. auto sum = std::make_shared<std::string>("");
  335. {
  336. rpl::lifetime lifetime;
  337. event_stream<int> a;
  338. event_stream<short> b;
  339. event_stream<char> c;
  340. using namespace mappers;
  341. // MSVC BUG + REGRESSION rpl::mappers::tuple :(
  342. //combine(
  343. // a.events(),
  344. // b.events(),
  345. // tuple(_1, _1 + _2)
  346. //) | rpl::start_with_next([=](int a, int a_plus_b) {
  347. // *sum += std::to_string(a * a_plus_b);
  348. //}, lifetime);
  349. combine(
  350. a.events(),
  351. b.events(),
  352. c.events(),
  353. _1 + _2 + _3 + 10)
  354. | start_with_next([=](int value) {
  355. *sum += std::to_string(value);
  356. }, lifetime);
  357. a.fire(1);
  358. b.fire(2);
  359. c.fire(3);
  360. a.fire(4);
  361. b.fire(5);
  362. c.fire(6);
  363. }
  364. REQUIRE(*sum == "16192225");
  365. // MSVC BUG + REGRESSION rpl::mappers::tuple :(
  366. //REQUIRE(*sum == "3162419362225");
  367. }
  368. SECTION("after_next test") {
  369. auto sum = std::make_shared<std::string>("");
  370. {
  371. rpl::lifetime lifetime;
  372. ints(3)
  373. | after_next([=](int value) {
  374. *sum += std::to_string(-value-1);
  375. })
  376. | start_with_next([=](int value) {
  377. *sum += std::to_string(value);
  378. }, lifetime);
  379. }
  380. REQUIRE(*sum == "0-11-22-3");
  381. }
  382. SECTION("combine_previous test") {
  383. auto sum = std::make_shared<std::string>("");
  384. {
  385. rpl::lifetime lifetime;
  386. event_stream<int> a;
  387. a.events(
  388. ) | combine_previous(
  389. ) | start_with_next([=](int previous, int next) {
  390. *sum += std::to_string(previous) + ' ';
  391. *sum += std::to_string(next) + ' ';
  392. }, lifetime);
  393. a.events(
  394. ) | combine_previous(
  395. 5
  396. ) | start_with_next([=](int previous, int next) {
  397. *sum += std::to_string(10 + previous) + ' ';
  398. *sum += std::to_string(next) + ' ';
  399. }, lifetime);
  400. a.fire(1);
  401. a.fire(2);
  402. a.fire(3);
  403. a.fire(4);
  404. }
  405. REQUIRE(*sum == "15 1 1 2 11 2 2 3 12 3 3 4 13 4 ");
  406. }
  407. SECTION("take test") {
  408. auto sum = std::make_shared<std::string>("");
  409. {
  410. rpl::lifetime lifetime;
  411. ints(10) | take(3)
  412. | start_with_next_done([=](int value) {
  413. *sum += std::to_string(value);
  414. }, [=] {
  415. *sum += "done";
  416. }, lifetime);
  417. }
  418. {
  419. rpl::lifetime lifetime;
  420. ints(3) | take(3)
  421. | start_with_next_done([=](int value) {
  422. *sum += std::to_string(value);
  423. }, [=] {
  424. *sum += "done";
  425. }, lifetime);
  426. }
  427. {
  428. rpl::lifetime lifetime;
  429. ints(3) | take(10)
  430. | start_with_next_done([=](int value) {
  431. *sum += std::to_string(value);
  432. }, [=] {
  433. *sum += "done";
  434. }, lifetime);
  435. }
  436. REQUIRE(*sum == "012done012done012done");
  437. }
  438. SECTION("skip test") {
  439. auto sum = std::make_shared<std::string>("");
  440. {
  441. rpl::lifetime lifetime;
  442. ints(10) | skip(5)
  443. | start_with_next_done([=](int value) {
  444. *sum += std::to_string(value);
  445. }, [=] {
  446. *sum += "done";
  447. }, lifetime);
  448. }
  449. {
  450. rpl::lifetime lifetime;
  451. ints(3) | skip(3)
  452. | start_with_next_done([=](int value) {
  453. *sum += std::to_string(value);
  454. }, [=] {
  455. *sum += "done";
  456. }, lifetime);
  457. }
  458. {
  459. rpl::lifetime lifetime;
  460. ints(3) | skip(10)
  461. | start_with_next_done([=](int value) {
  462. *sum += std::to_string(value);
  463. }, [=] {
  464. *sum += "done";
  465. }, lifetime);
  466. }
  467. REQUIRE(*sum == "56789donedonedone");
  468. }
  469. }