results.cpp 106 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #include "catch.hpp"
  19. #include "util/event_loop.hpp"
  20. #include "util/index_helpers.hpp"
  21. #include "util/templated_test_case.hpp"
  22. #include "util/test_file.hpp"
  23. #include "impl/object_accessor_impl.hpp"
  24. #include "impl/realm_coordinator.hpp"
  25. #include "binding_context.hpp"
  26. #include "object_schema.hpp"
  27. #include "property.hpp"
  28. #include "results.hpp"
  29. #include "schema.hpp"
  30. #include <realm/group_shared.hpp>
  31. #include <realm/link_view.hpp>
  32. #include <realm/query_engine.hpp>
  33. #include <realm/query_expression.hpp>
  34. #if REALM_ENABLE_SYNC
  35. #include "sync/sync_manager.hpp"
  36. #include "sync/sync_session.hpp"
  37. #endif
  38. namespace realm {
  39. class TestHelper {
  40. public:
  41. static SharedGroup& get_shared_group(SharedRealm const& shared_realm)
  42. {
  43. return *Realm::Internal::get_shared_group(*shared_realm);
  44. }
  45. };
  46. }
  47. using namespace realm;
  48. using namespace std::string_literals;
  49. namespace {
  50. using AnyDict = std::map<std::string, util::Any>;
  51. using AnyVec = std::vector<util::Any>;
  52. }
  53. struct TestContext : CppContext {
  54. std::map<std::string, AnyDict> defaults;
  55. using CppContext::CppContext;
  56. TestContext(TestContext& parent, realm::Property const& prop)
  57. : CppContext(parent, prop)
  58. , defaults(parent.defaults)
  59. { }
  60. void will_change(Object const&, Property const&) {}
  61. void did_change() {}
  62. std::string print(util::Any) { return "not implemented"; }
  63. bool allow_missing(util::Any) { return false; }
  64. };
  65. TEST_CASE("notifications: async delivery") {
  66. _impl::RealmCoordinator::assert_no_open_realms();
  67. InMemoryTestFile config;
  68. config.cache = false;
  69. config.automatic_change_notifications = false;
  70. auto r = Realm::get_shared_realm(config);
  71. r->update_schema({
  72. {"object", {
  73. {"value", PropertyType::Int}
  74. }},
  75. });
  76. auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
  77. auto table = r->read_group().get_table("class_object");
  78. r->begin_transaction();
  79. table->add_empty_row(10);
  80. for (int i = 0; i < 10; ++i)
  81. table->set_int(0, i, i * 2);
  82. r->commit_transaction();
  83. Results results(r, table->where().greater(0, 0).less(0, 10));
  84. int notification_calls = 0;
  85. auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  86. REQUIRE_FALSE(err);
  87. ++notification_calls;
  88. });
  89. auto make_local_change = [&] {
  90. r->begin_transaction();
  91. table->set_int(0, 0, 4);
  92. r->commit_transaction();
  93. };
  94. auto make_remote_change = [&] {
  95. auto r2 = coordinator->get_realm();
  96. r2->begin_transaction();
  97. r2->read_group().get_table("class_object")->set_int(0, 0, 5);
  98. r2->commit_transaction();
  99. };
  100. SECTION("initial notification") {
  101. SECTION("is delivered on notify()") {
  102. REQUIRE(notification_calls == 0);
  103. advance_and_notify(*r);
  104. REQUIRE(notification_calls == 1);
  105. }
  106. SECTION("is delivered on refresh()") {
  107. coordinator->on_change();
  108. REQUIRE(notification_calls == 0);
  109. r->refresh();
  110. REQUIRE(notification_calls == 1);
  111. }
  112. SECTION("is delivered on begin_transaction()") {
  113. coordinator->on_change();
  114. REQUIRE(notification_calls == 0);
  115. r->begin_transaction();
  116. REQUIRE(notification_calls == 1);
  117. r->cancel_transaction();
  118. }
  119. SECTION("is delivered on notify() even with autorefresh disabled") {
  120. r->set_auto_refresh(false);
  121. REQUIRE(notification_calls == 0);
  122. advance_and_notify(*r);
  123. REQUIRE(notification_calls == 1);
  124. }
  125. SECTION("refresh() blocks due to initial results not being ready") {
  126. REQUIRE(notification_calls == 0);
  127. JoiningThread thread([&] {
  128. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  129. coordinator->on_change();
  130. });
  131. r->refresh();
  132. REQUIRE(notification_calls == 1);
  133. }
  134. SECTION("begin_transaction() blocks due to initial results not being ready") {
  135. REQUIRE(notification_calls == 0);
  136. JoiningThread thread([&] {
  137. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  138. coordinator->on_change();
  139. });
  140. r->begin_transaction();
  141. REQUIRE(notification_calls == 1);
  142. r->cancel_transaction();
  143. }
  144. SECTION("notify() does not block due to initial results not being ready") {
  145. REQUIRE(notification_calls == 0);
  146. r->notify();
  147. REQUIRE(notification_calls == 0);
  148. }
  149. SECTION("is delivered after invalidate()") {
  150. r->invalidate();
  151. SECTION("notify()") {
  152. coordinator->on_change();
  153. REQUIRE_FALSE(r->is_in_read_transaction());
  154. r->notify();
  155. REQUIRE(notification_calls == 1);
  156. }
  157. SECTION("notify() without autorefresh") {
  158. r->set_auto_refresh(false);
  159. coordinator->on_change();
  160. REQUIRE_FALSE(r->is_in_read_transaction());
  161. r->notify();
  162. REQUIRE(notification_calls == 1);
  163. }
  164. SECTION("refresh()") {
  165. coordinator->on_change();
  166. REQUIRE_FALSE(r->is_in_read_transaction());
  167. r->refresh();
  168. REQUIRE(notification_calls == 1);
  169. }
  170. SECTION("begin_transaction()") {
  171. coordinator->on_change();
  172. REQUIRE_FALSE(r->is_in_read_transaction());
  173. r->begin_transaction();
  174. REQUIRE(notification_calls == 1);
  175. r->cancel_transaction();
  176. }
  177. }
  178. SECTION("is delivered by notify() even if there are later versions") {
  179. REQUIRE(notification_calls == 0);
  180. coordinator->on_change();
  181. make_remote_change();
  182. r->notify();
  183. REQUIRE(notification_calls == 1);
  184. }
  185. }
  186. advance_and_notify(*r);
  187. SECTION("notifications for local changes") {
  188. make_local_change();
  189. coordinator->on_change();
  190. REQUIRE(notification_calls == 1);
  191. SECTION("notify()") {
  192. r->notify();
  193. REQUIRE(notification_calls == 2);
  194. }
  195. SECTION("notify() without autorefresh") {
  196. r->set_auto_refresh(false);
  197. r->notify();
  198. REQUIRE(notification_calls == 2);
  199. }
  200. SECTION("refresh()") {
  201. r->refresh();
  202. REQUIRE(notification_calls == 2);
  203. }
  204. SECTION("begin_transaction()") {
  205. r->begin_transaction();
  206. REQUIRE(notification_calls == 2);
  207. r->cancel_transaction();
  208. }
  209. }
  210. SECTION("notifications for remote changes") {
  211. make_remote_change();
  212. coordinator->on_change();
  213. REQUIRE(notification_calls == 1);
  214. SECTION("notify()") {
  215. r->notify();
  216. REQUIRE(notification_calls == 2);
  217. }
  218. SECTION("notify() without autorefresh") {
  219. r->set_auto_refresh(false);
  220. r->notify();
  221. REQUIRE(notification_calls == 1);
  222. r->refresh();
  223. REQUIRE(notification_calls == 2);
  224. }
  225. SECTION("refresh()") {
  226. r->refresh();
  227. REQUIRE(notification_calls == 2);
  228. }
  229. SECTION("begin_transaction()") {
  230. r->begin_transaction();
  231. REQUIRE(notification_calls == 2);
  232. r->cancel_transaction();
  233. }
  234. }
  235. SECTION("notifications are not delivered when the token is destroyed before they are calculated") {
  236. make_remote_change();
  237. REQUIRE(notification_calls == 1);
  238. token = {};
  239. advance_and_notify(*r);
  240. REQUIRE(notification_calls == 1);
  241. }
  242. SECTION("notifications are not delivered when the token is destroyed before they are delivered") {
  243. make_remote_change();
  244. REQUIRE(notification_calls == 1);
  245. coordinator->on_change();
  246. token = {};
  247. r->notify();
  248. REQUIRE(notification_calls == 1);
  249. }
  250. SECTION("notifications are delivered on the next cycle when a new callback is added from within a callback") {
  251. NotificationToken token2, token3;
  252. bool called = false;
  253. token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  254. token2 = {};
  255. token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  256. called = true;
  257. });
  258. });
  259. advance_and_notify(*r);
  260. REQUIRE_FALSE(called);
  261. advance_and_notify(*r);
  262. REQUIRE(called);
  263. }
  264. SECTION("notifications are delivered on the next cycle when a new callback is added from within a callback") {
  265. auto results2 = results;
  266. auto results3 = results;
  267. NotificationToken token2, token3, token4;
  268. bool called = false;
  269. auto check = [&](Results& outer, Results& inner) {
  270. token2 = outer.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  271. token2 = {};
  272. token3 = inner.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  273. called = true;
  274. });
  275. });
  276. advance_and_notify(*r);
  277. REQUIRE_FALSE(called);
  278. advance_and_notify(*r);
  279. REQUIRE(called);
  280. };
  281. SECTION("same Results") {
  282. check(results, results);
  283. }
  284. SECTION("Results which has never had a notifier") {
  285. check(results, results2);
  286. }
  287. SECTION("Results which used to have callbacks but no longer does") {
  288. SECTION("notifier before active") {
  289. token3 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  290. token3 = {};
  291. });
  292. check(results3, results2);
  293. }
  294. SECTION("notifier after active") {
  295. token3 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  296. token3 = {};
  297. });
  298. check(results, results2);
  299. }
  300. }
  301. SECTION("Results which already has callbacks") {
  302. SECTION("notifier before active") {
  303. token4 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
  304. check(results3, results2);
  305. }
  306. SECTION("notifier after active") {
  307. token4 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
  308. check(results, results2);
  309. }
  310. }
  311. }
  312. SECTION("remote changes made before adding a callback from within a callback are not reported") {
  313. NotificationToken token2, token3;
  314. bool called = false;
  315. token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  316. token2 = {};
  317. make_remote_change();
  318. coordinator->on_change();
  319. token3 = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
  320. called = true;
  321. REQUIRE(c.empty());
  322. REQUIRE(table->get_int(0, 0) == 5);
  323. });
  324. });
  325. advance_and_notify(*r);
  326. REQUIRE_FALSE(called);
  327. advance_and_notify(*r);
  328. REQUIRE(called);
  329. }
  330. SECTION("notifications are not delivered when a callback is removed from within a callback") {
  331. NotificationToken token2, token3;
  332. token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  333. token3 = {};
  334. });
  335. token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  336. REQUIRE(false);
  337. });
  338. advance_and_notify(*r);
  339. }
  340. SECTION("removing the current callback does not stop later ones from being called") {
  341. NotificationToken token2, token3;
  342. bool called = false;
  343. token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  344. token2 = {};
  345. });
  346. token3 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  347. called = true;
  348. });
  349. advance_and_notify(*r);
  350. REQUIRE(called);
  351. }
  352. SECTION("the first call of a notification can include changes if it previously ran for a different callback") {
  353. r->begin_transaction();
  354. auto token2 = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
  355. REQUIRE(!c.empty());
  356. });
  357. table->set_int(0, table->add_empty_row(), 5);
  358. r->commit_transaction();
  359. advance_and_notify(*r);
  360. }
  361. SECTION("handling of results not ready") {
  362. make_remote_change();
  363. SECTION("notify() does nothing") {
  364. r->notify();
  365. REQUIRE(notification_calls == 1);
  366. coordinator->on_change();
  367. r->notify();
  368. REQUIRE(notification_calls == 2);
  369. }
  370. SECTION("refresh() blocks") {
  371. REQUIRE(notification_calls == 1);
  372. JoiningThread thread([&] {
  373. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  374. coordinator->on_change();
  375. });
  376. r->refresh();
  377. REQUIRE(notification_calls == 2);
  378. }
  379. SECTION("refresh() advances to the first version with notifiers ready that is at least a recent as the newest at the time it is called") {
  380. JoiningThread thread([&] {
  381. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  382. make_remote_change();
  383. coordinator->on_change();
  384. make_remote_change();
  385. });
  386. // advances to the version after the one it was waiting for, but still
  387. // not the latest
  388. r->refresh();
  389. REQUIRE(notification_calls == 2);
  390. thread.join();
  391. REQUIRE(notification_calls == 2);
  392. // now advances to the latest
  393. coordinator->on_change();
  394. r->refresh();
  395. REQUIRE(notification_calls == 3);
  396. }
  397. SECTION("begin_transaction() blocks") {
  398. REQUIRE(notification_calls == 1);
  399. JoiningThread thread([&] {
  400. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  401. coordinator->on_change();
  402. });
  403. r->begin_transaction();
  404. REQUIRE(notification_calls == 2);
  405. r->cancel_transaction();
  406. }
  407. SECTION("refresh() does not block for results without callbacks") {
  408. token = {};
  409. // this would deadlock if it waits for the notifier to be ready
  410. r->refresh();
  411. }
  412. SECTION("begin_transaction() does not block for results without callbacks") {
  413. token = {};
  414. // this would deadlock if it waits for the notifier to be ready
  415. r->begin_transaction();
  416. r->cancel_transaction();
  417. }
  418. SECTION("begin_transaction() does not block for Results for different Realms") {
  419. // this would deadlock if beginning the write on the secondary Realm
  420. // waited for the primary Realm to be ready
  421. make_remote_change();
  422. // sanity check that the notifications never did run
  423. r->notify();
  424. REQUIRE(notification_calls == 1);
  425. }
  426. }
  427. SECTION("handling of stale results") {
  428. make_remote_change();
  429. coordinator->on_change();
  430. make_remote_change();
  431. SECTION("notify() uses the older version") {
  432. r->notify();
  433. REQUIRE(notification_calls == 2);
  434. coordinator->on_change();
  435. r->notify();
  436. REQUIRE(notification_calls == 3);
  437. r->notify();
  438. REQUIRE(notification_calls == 3);
  439. }
  440. SECTION("refresh() blocks") {
  441. REQUIRE(notification_calls == 1);
  442. JoiningThread thread([&] {
  443. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  444. coordinator->on_change();
  445. });
  446. r->refresh();
  447. REQUIRE(notification_calls == 2);
  448. }
  449. SECTION("begin_transaction() blocks") {
  450. REQUIRE(notification_calls == 1);
  451. JoiningThread thread([&] {
  452. std::this_thread::sleep_for(std::chrono::microseconds(5000));
  453. coordinator->on_change();
  454. });
  455. r->begin_transaction();
  456. REQUIRE(notification_calls == 2);
  457. r->cancel_transaction();
  458. }
  459. }
  460. SECTION("updates are delivered after invalidate()") {
  461. r->invalidate();
  462. make_remote_change();
  463. SECTION("notify()") {
  464. coordinator->on_change();
  465. REQUIRE_FALSE(r->is_in_read_transaction());
  466. r->notify();
  467. REQUIRE(notification_calls == 2);
  468. }
  469. SECTION("notify() without autorefresh") {
  470. r->set_auto_refresh(false);
  471. coordinator->on_change();
  472. REQUIRE_FALSE(r->is_in_read_transaction());
  473. r->notify();
  474. REQUIRE(notification_calls == 1);
  475. r->refresh();
  476. REQUIRE(notification_calls == 2);
  477. }
  478. SECTION("refresh()") {
  479. coordinator->on_change();
  480. REQUIRE_FALSE(r->is_in_read_transaction());
  481. r->refresh();
  482. REQUIRE(notification_calls == 2);
  483. }
  484. SECTION("begin_transaction()") {
  485. coordinator->on_change();
  486. REQUIRE_FALSE(r->is_in_read_transaction());
  487. r->begin_transaction();
  488. REQUIRE(notification_calls == 2);
  489. r->cancel_transaction();
  490. }
  491. }
  492. SECTION("refresh() from within changes_available() do not interfere with notification delivery") {
  493. struct Context : BindingContext {
  494. Realm& realm;
  495. Context(Realm& realm) : realm(realm) { }
  496. void changes_available() override
  497. {
  498. REQUIRE(realm.refresh());
  499. }
  500. };
  501. make_remote_change();
  502. coordinator->on_change();
  503. r->set_auto_refresh(false);
  504. REQUIRE(notification_calls == 1);
  505. r->notify();
  506. REQUIRE(notification_calls == 1);
  507. r->m_binding_context.reset(new Context(*r));
  508. r->notify();
  509. REQUIRE(notification_calls == 2);
  510. }
  511. SECTION("refresh() from within a notification is a no-op") {
  512. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  513. REQUIRE_FALSE(err);
  514. REQUIRE_FALSE(r->refresh()); // would deadlock if it actually tried to refresh
  515. });
  516. advance_and_notify(*r);
  517. make_remote_change(); // 1
  518. coordinator->on_change();
  519. make_remote_change(); // 2
  520. r->notify(); // advances to version from 1
  521. coordinator->on_change();
  522. REQUIRE(r->refresh()); // advances to version from 2
  523. REQUIRE_FALSE(r->refresh()); // does not advance since it's now up-to-date
  524. }
  525. SECTION("begin_transaction() from within a notification does not send notifications immediately") {
  526. bool first = true;
  527. auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  528. REQUIRE_FALSE(err);
  529. if (first)
  530. first = false;
  531. else {
  532. // would deadlock if it tried to send notifications as they aren't ready yet
  533. r->begin_transaction();
  534. r->cancel_transaction();
  535. }
  536. });
  537. advance_and_notify(*r);
  538. make_remote_change(); // 1
  539. coordinator->on_change();
  540. make_remote_change(); // 2
  541. r->notify(); // advances to version from 1
  542. REQUIRE(notification_calls == 2);
  543. coordinator->on_change();
  544. REQUIRE_FALSE(r->refresh()); // we made the commit locally, so no advancing here
  545. REQUIRE(notification_calls == 3);
  546. }
  547. SECTION("begin_transaction() from within a notification does not break delivering additional notifications") {
  548. size_t calls = 0;
  549. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  550. REQUIRE_FALSE(err);
  551. if (++calls == 1)
  552. return;
  553. // force the read version to advance by beginning a transaction
  554. r->begin_transaction();
  555. r->cancel_transaction();
  556. });
  557. auto results2 = results;
  558. size_t calls2 = 0;
  559. auto token2 = results2.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  560. REQUIRE_FALSE(err);
  561. if (++calls2 == 1)
  562. return;
  563. REQUIRE_INDICES(c.insertions, 0);
  564. });
  565. advance_and_notify(*r);
  566. REQUIRE(calls == 1);
  567. REQUIRE(calls2 == 1);
  568. make_remote_change(); // 1
  569. coordinator->on_change();
  570. make_remote_change(); // 2
  571. r->notify(); // advances to version from 1
  572. REQUIRE(calls == 2);
  573. REQUIRE(calls2 == 2);
  574. }
  575. SECTION("begin_transaction() from within did_change() does not break delivering collection notification") {
  576. struct Context : BindingContext {
  577. Realm& realm;
  578. Context(Realm& realm) : realm(realm) { }
  579. void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
  580. {
  581. if (!realm.is_in_transaction()) {
  582. // advances to version from 2 (and recursively calls this, hence the check above)
  583. realm.begin_transaction();
  584. realm.cancel_transaction();
  585. }
  586. }
  587. };
  588. r->m_binding_context.reset(new Context(*r));
  589. make_remote_change(); // 1
  590. coordinator->on_change();
  591. make_remote_change(); // 2
  592. r->notify(); // advances to version from 1
  593. }
  594. SECTION("is_in_transaction() is reported correctly within a notification from begin_transaction() and changes can be made") {
  595. bool first = true;
  596. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  597. REQUIRE_FALSE(err);
  598. if (first) {
  599. REQUIRE_FALSE(r->is_in_transaction());
  600. first = false;
  601. }
  602. else {
  603. REQUIRE(r->is_in_transaction());
  604. table->set_int(0, 0, 100);
  605. }
  606. });
  607. advance_and_notify(*r);
  608. make_remote_change();
  609. coordinator->on_change();
  610. r->begin_transaction();
  611. REQUIRE(table->get_int(0, 0) == 100);
  612. r->cancel_transaction();
  613. REQUIRE(table->get_int(0, 0) != 100);
  614. }
  615. SECTION("invalidate() from within notification is a no-op") {
  616. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  617. REQUIRE_FALSE(err);
  618. r->invalidate();
  619. REQUIRE(r->is_in_read_transaction());
  620. });
  621. advance_and_notify(*r);
  622. REQUIRE(r->is_in_read_transaction());
  623. make_remote_change();
  624. coordinator->on_change();
  625. r->begin_transaction();
  626. REQUIRE(r->is_in_transaction());
  627. r->cancel_transaction();
  628. }
  629. SECTION("cancel_transaction() from within notification ends the write transaction started by begin_transaction()") {
  630. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  631. REQUIRE_FALSE(err);
  632. if (r->is_in_transaction())
  633. r->cancel_transaction();
  634. });
  635. advance_and_notify(*r);
  636. make_remote_change();
  637. coordinator->on_change();
  638. r->begin_transaction();
  639. REQUIRE_FALSE(r->is_in_transaction());
  640. }
  641. }
  642. TEST_CASE("notifications: skip") {
  643. _impl::RealmCoordinator::assert_no_open_realms();
  644. InMemoryTestFile config;
  645. config.cache = false;
  646. config.automatic_change_notifications = false;
  647. auto r = Realm::get_shared_realm(config);
  648. r->update_schema({
  649. {"object", {
  650. {"value", PropertyType::Int}
  651. }},
  652. });
  653. auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
  654. auto table = r->read_group().get_table("class_object");
  655. r->begin_transaction();
  656. table->add_empty_row(10);
  657. for (int i = 0; i < 10; ++i)
  658. table->set_int(0, i, i * 2);
  659. r->commit_transaction();
  660. Results results(r, table->where());
  661. auto add_callback = [](Results& results, int& calls, CollectionChangeSet& changes) {
  662. return results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  663. REQUIRE_FALSE(err);
  664. ++calls;
  665. changes = std::move(c);
  666. });
  667. };
  668. auto make_local_change = [&](auto& token) {
  669. r->begin_transaction();
  670. table->add_empty_row();
  671. token.suppress_next();
  672. r->commit_transaction();
  673. };
  674. auto make_remote_change = [&] {
  675. auto r2 = coordinator->get_realm();
  676. r2->begin_transaction();
  677. r2->read_group().get_table("class_object")->add_empty_row();
  678. r2->commit_transaction();
  679. };
  680. int calls1 = 0;
  681. CollectionChangeSet changes1;
  682. auto token1 = add_callback(results, calls1, changes1);
  683. SECTION("no notification is sent when only callback is skipped") {
  684. advance_and_notify(*r);
  685. REQUIRE(calls1 == 1);
  686. make_local_change(token1);
  687. advance_and_notify(*r);
  688. REQUIRE(calls1 == 1);
  689. REQUIRE(changes1.empty());
  690. }
  691. SECTION("unskipped tokens for the same Results are still delivered") {
  692. int calls2 = 0;
  693. CollectionChangeSet changes2;
  694. auto token2 = add_callback(results, calls2, changes2);
  695. advance_and_notify(*r);
  696. REQUIRE(calls1 == 1);
  697. REQUIRE(calls2 == 1);
  698. make_local_change(token1);
  699. advance_and_notify(*r);
  700. REQUIRE(calls1 == 1);
  701. REQUIRE(changes1.empty());
  702. REQUIRE(calls2 == 2);
  703. REQUIRE_INDICES(changes2.insertions, 10);
  704. }
  705. SECTION("unskipped tokens for different Results are still delivered") {
  706. Results results2(r, table->where());
  707. int calls2 = 0;
  708. CollectionChangeSet changes2;
  709. auto token2 = add_callback(results2, calls2, changes2);
  710. advance_and_notify(*r);
  711. REQUIRE(calls1 == 1);
  712. REQUIRE(calls2 == 1);
  713. make_local_change(token1);
  714. advance_and_notify(*r);
  715. REQUIRE(calls1 == 1);
  716. REQUIRE(changes1.empty());
  717. REQUIRE(calls2 == 2);
  718. REQUIRE_INDICES(changes2.insertions, 10);
  719. }
  720. SECTION("additional commits which occur before calculation are merged in") {
  721. int calls2 = 0;
  722. CollectionChangeSet changes2;
  723. auto token2 = add_callback(results, calls2, changes2);
  724. advance_and_notify(*r);
  725. REQUIRE(calls1 == 1);
  726. REQUIRE(calls2 == 1);
  727. make_local_change(token1);
  728. make_remote_change();
  729. advance_and_notify(*r);
  730. REQUIRE(calls1 == 2);
  731. REQUIRE_INDICES(changes1.insertions, 11);
  732. REQUIRE(calls2 == 2);
  733. REQUIRE_INDICES(changes2.insertions, 10, 11);
  734. }
  735. SECTION("additional commits which occur before delivery are merged in") {
  736. int calls2 = 0;
  737. CollectionChangeSet changes2;
  738. auto token2 = add_callback(results, calls2, changes2);
  739. advance_and_notify(*r);
  740. REQUIRE(calls1 == 1);
  741. REQUIRE(calls2 == 1);
  742. make_local_change(token1);
  743. coordinator->on_change();
  744. make_remote_change();
  745. advance_and_notify(*r);
  746. REQUIRE(calls1 == 2);
  747. REQUIRE_INDICES(changes1.insertions, 11);
  748. REQUIRE(calls2 == 2);
  749. REQUIRE_INDICES(changes2.insertions, 10, 11);
  750. }
  751. SECTION("skipping must be done from within a write transaction") {
  752. REQUIRE_THROWS(token1.suppress_next());
  753. }
  754. SECTION("skipping must be done from the Realm's thread") {
  755. advance_and_notify(*r);
  756. r->begin_transaction();
  757. std::thread([&] {
  758. REQUIRE_THROWS(token1.suppress_next());
  759. }).join();
  760. r->cancel_transaction();
  761. }
  762. SECTION("new notifiers do not interfere with skipping") {
  763. advance_and_notify(*r);
  764. REQUIRE(calls1 == 1);
  765. CollectionChangeSet changes;
  766. // new notifier at a version before the skipped one
  767. auto r2 = coordinator->get_realm();
  768. Results results2(r2, r2->read_group().get_table("class_object")->where());
  769. int calls2 = 0;
  770. auto token2 = add_callback(results2, calls2, changes);
  771. make_local_change(token1);
  772. // new notifier at the skipped version
  773. auto r3 = coordinator->get_realm();
  774. Results results3(r3, r3->read_group().get_table("class_object")->where());
  775. int calls3 = 0;
  776. auto token3 = add_callback(results3, calls3, changes);
  777. make_remote_change();
  778. // new notifier at version after the skipped one
  779. auto r4 = coordinator->get_realm();
  780. Results results4(r4, r4->read_group().get_table("class_object")->where());
  781. int calls4 = 0;
  782. auto token4 = add_callback(results4, calls4, changes);
  783. coordinator->on_change();
  784. r->notify();
  785. r2->notify();
  786. r3->notify();
  787. r4->notify();
  788. REQUIRE(calls1 == 2);
  789. REQUIRE(calls2 == 1);
  790. REQUIRE(calls3 == 1);
  791. REQUIRE(calls4 == 1);
  792. }
  793. SECTION("skipping only effects the current transaction even if no notification would occur anyway") {
  794. advance_and_notify(*r);
  795. REQUIRE(calls1 == 1);
  796. // would not produce a notification even if it wasn't skipped because no changes were made
  797. r->begin_transaction();
  798. token1.suppress_next();
  799. r->commit_transaction();
  800. advance_and_notify(*r);
  801. REQUIRE(calls1 == 1);
  802. // should now produce a notification
  803. r->begin_transaction();
  804. table->add_empty_row();
  805. r->commit_transaction();
  806. advance_and_notify(*r);
  807. REQUIRE(calls1 == 2);
  808. }
  809. SECTION("removing skipped notifier before it gets the chance to run") {
  810. advance_and_notify(*r);
  811. REQUIRE(calls1 == 1);
  812. // Set the skip version
  813. make_local_change(token1);
  814. // Advance the file to a version after the skip version
  815. make_remote_change();
  816. REQUIRE(calls1 == 1);
  817. // Remove the skipped notifier and add an entirely new notifier, so that
  818. // notifications need to run but the skip logic shouldn't be used
  819. token1 = {};
  820. results = {};
  821. Results results2(r, table->where());
  822. auto token2 = add_callback(results2, calls1, changes1);
  823. advance_and_notify(*r);
  824. REQUIRE(calls1 == 2);
  825. }
  826. }
  827. #if REALM_PLATFORM_APPLE
  828. TEST_CASE("notifications: async error handling") {
  829. _impl::RealmCoordinator::assert_no_open_realms();
  830. InMemoryTestFile config;
  831. config.cache = false;
  832. config.automatic_change_notifications = false;
  833. auto r = Realm::get_shared_realm(config);
  834. r->update_schema({
  835. {"object", {
  836. {"value", PropertyType::Int},
  837. }},
  838. });
  839. auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
  840. Results results(r, *r->read_group().get_table("class_object"));
  841. auto r2 = Realm::get_shared_realm(config);
  842. class OpenFileLimiter {
  843. public:
  844. OpenFileLimiter()
  845. {
  846. // Set the max open files to zero so that opening new files will fail
  847. getrlimit(RLIMIT_NOFILE, &m_old);
  848. rlimit rl = m_old;
  849. rl.rlim_cur = 0;
  850. setrlimit(RLIMIT_NOFILE, &rl);
  851. }
  852. ~OpenFileLimiter()
  853. {
  854. setrlimit(RLIMIT_NOFILE, &m_old);
  855. }
  856. private:
  857. rlimit m_old;
  858. };
  859. SECTION("error when opening the advancer SG") {
  860. OpenFileLimiter limiter;
  861. bool called = false;
  862. auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  863. REQUIRE(err);
  864. REQUIRE_FALSE(called);
  865. called = true;
  866. });
  867. REQUIRE(!called);
  868. SECTION("error is delivered on notify() without changes") {
  869. coordinator->on_change();
  870. REQUIRE(!called);
  871. r->notify();
  872. REQUIRE(called);
  873. }
  874. SECTION("error is delivered on notify() with changes") {
  875. r2->begin_transaction(); r2->commit_transaction();
  876. REQUIRE(!called);
  877. coordinator->on_change();
  878. REQUIRE(!called);
  879. r->notify();
  880. REQUIRE(called);
  881. }
  882. SECTION("error is delivered on refresh() without changes") {
  883. coordinator->on_change();
  884. REQUIRE(!called);
  885. r->refresh();
  886. REQUIRE(called);
  887. }
  888. SECTION("error is delivered on refresh() with changes") {
  889. r2->begin_transaction(); r2->commit_transaction();
  890. REQUIRE(!called);
  891. coordinator->on_change();
  892. REQUIRE(!called);
  893. r->refresh();
  894. REQUIRE(called);
  895. }
  896. SECTION("error is delivered on begin_transaction() without changes") {
  897. coordinator->on_change();
  898. REQUIRE(!called);
  899. r->begin_transaction();
  900. REQUIRE(called);
  901. r->cancel_transaction();
  902. }
  903. SECTION("error is delivered on begin_transaction() with changes") {
  904. r2->begin_transaction(); r2->commit_transaction();
  905. REQUIRE(!called);
  906. coordinator->on_change();
  907. REQUIRE(!called);
  908. r->begin_transaction();
  909. REQUIRE(called);
  910. r->cancel_transaction();
  911. }
  912. SECTION("adding another callback sends the error to only the newly added one") {
  913. advance_and_notify(*r);
  914. REQUIRE(called);
  915. bool called2 = false;
  916. auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  917. REQUIRE(err);
  918. REQUIRE_FALSE(called2);
  919. called2 = true;
  920. });
  921. advance_and_notify(*r);
  922. REQUIRE(called2);
  923. }
  924. SECTION("destroying a token from before the error does not remove newly added callbacks") {
  925. advance_and_notify(*r);
  926. bool called = false;
  927. auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  928. REQUIRE(err);
  929. REQUIRE_FALSE(called);
  930. called = true;
  931. });
  932. token = {};
  933. advance_and_notify(*r);
  934. REQUIRE(called);
  935. }
  936. SECTION("adding another callback from within an error callback defers delivery") {
  937. NotificationToken token2;
  938. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  939. token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  940. REQUIRE(err);
  941. REQUIRE_FALSE(called);
  942. called = true;
  943. });
  944. });
  945. advance_and_notify(*r);
  946. REQUIRE(!called);
  947. advance_and_notify(*r);
  948. REQUIRE(called);
  949. }
  950. SECTION("adding a callback to a different collection from within the error callback defers delivery") {
  951. auto results2 = results;
  952. NotificationToken token2;
  953. token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
  954. token2 = results2.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  955. REQUIRE(err);
  956. REQUIRE_FALSE(called);
  957. called = true;
  958. });
  959. });
  960. advance_and_notify(*r);
  961. REQUIRE(!called);
  962. advance_and_notify(*r);
  963. REQUIRE(called);
  964. }
  965. }
  966. SECTION("error when opening the executor SG") {
  967. SECTION("error is delivered asynchronously") {
  968. bool called = false;
  969. auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  970. REQUIRE(err);
  971. called = true;
  972. });
  973. OpenFileLimiter limiter;
  974. REQUIRE(!called);
  975. coordinator->on_change();
  976. REQUIRE(!called);
  977. r->notify();
  978. REQUIRE(called);
  979. }
  980. SECTION("adding another callback only sends the error to the new one") {
  981. bool called = false;
  982. auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  983. REQUIRE(err);
  984. REQUIRE_FALSE(called);
  985. called = true;
  986. });
  987. OpenFileLimiter limiter;
  988. advance_and_notify(*r);
  989. bool called2 = false;
  990. auto token2 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  991. REQUIRE(err);
  992. REQUIRE_FALSE(called2);
  993. called2 = true;
  994. });
  995. advance_and_notify(*r);
  996. REQUIRE(called2);
  997. }
  998. }
  999. }
  1000. #endif
  1001. #if REALM_ENABLE_SYNC
  1002. TEST_CASE("notifications: sync") {
  1003. _impl::RealmCoordinator::assert_no_open_realms();
  1004. SyncServer server(false);
  1005. SyncTestFile config(server);
  1006. config.cache = false;
  1007. config.schema = Schema{
  1008. {"object", {
  1009. {"value", PropertyType::Int},
  1010. }},
  1011. };
  1012. SECTION("sync progress commits do not distrupt notifications") {
  1013. auto r = Realm::get_shared_realm(config);
  1014. auto wait_realm = Realm::get_shared_realm(config);
  1015. Results results(r, *r->read_group().get_table("class_object"));
  1016. Results wait_results(wait_realm, *wait_realm->read_group().get_table("class_object"));
  1017. auto token1 = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
  1018. auto token2 = wait_results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) { });
  1019. // Add an object to the Realm so that notifications are needed
  1020. {
  1021. auto write_realm = Realm::get_shared_realm(config);
  1022. write_realm->begin_transaction();
  1023. sync::create_object(write_realm->read_group(), *write_realm->read_group().get_table("class_object"));
  1024. write_realm->commit_transaction();
  1025. }
  1026. // Wait for the notifications to become ready for the new version
  1027. wait_realm->refresh();
  1028. // Start the server and wait for the Realm to be uploaded so that sync
  1029. // makes some writes to the Realm and bumps the version
  1030. server.start();
  1031. wait_for_upload(*r);
  1032. // Make sure that the notifications still get delivered rather than
  1033. // waiting forever due to that we don't get a commit notification from
  1034. // the commits sync makes to store the upload progress
  1035. r->refresh();
  1036. }
  1037. }
  1038. #endif
  1039. TEST_CASE("notifications: results") {
  1040. _impl::RealmCoordinator::assert_no_open_realms();
  1041. InMemoryTestFile config;
  1042. config.cache = false;
  1043. config.automatic_change_notifications = false;
  1044. auto r = Realm::get_shared_realm(config);
  1045. r->update_schema({
  1046. {"object", {
  1047. {"value", PropertyType::Int},
  1048. {"link", PropertyType::Object|PropertyType::Nullable, "linked to object"}
  1049. }},
  1050. {"other object", {
  1051. {"value", PropertyType::Int}
  1052. }},
  1053. {"linking object", {
  1054. {"link", PropertyType::Object|PropertyType::Nullable, "object"}
  1055. }},
  1056. {"linked to object", {
  1057. {"value", PropertyType::Int}
  1058. }}
  1059. });
  1060. auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
  1061. auto table = r->read_group().get_table("class_object");
  1062. r->begin_transaction();
  1063. r->read_group().get_table("class_linked to object")->add_empty_row(10);
  1064. table->add_empty_row(10);
  1065. for (int i = 0; i < 10; ++i) {
  1066. table->set_int(0, i, i * 2);
  1067. table->set_link(1, i, i);
  1068. }
  1069. r->commit_transaction();
  1070. auto r2 = coordinator->get_realm();
  1071. auto r2_table = r2->read_group().get_table("class_object");
  1072. Results results(r, table->where().greater(0, 0).less(0, 10));
  1073. SECTION("unsorted notifications") {
  1074. int notification_calls = 0;
  1075. CollectionChangeSet change;
  1076. auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  1077. REQUIRE_FALSE(err);
  1078. change = c;
  1079. ++notification_calls;
  1080. });
  1081. advance_and_notify(*r);
  1082. auto write = [&](auto&& f) {
  1083. r->begin_transaction();
  1084. f();
  1085. r->commit_transaction();
  1086. advance_and_notify(*r);
  1087. };
  1088. SECTION("modifications to unrelated tables do not send notifications") {
  1089. write([&] {
  1090. r->read_group().get_table("class_other object")->add_empty_row();
  1091. });
  1092. REQUIRE(notification_calls == 1);
  1093. }
  1094. SECTION("irrelevant modifications to linked tables do not send notifications") {
  1095. write([&] {
  1096. r->read_group().get_table("class_linked to object")->add_empty_row();
  1097. });
  1098. REQUIRE(notification_calls == 1);
  1099. }
  1100. SECTION("irrelevant modifications to linking tables do not send notifications") {
  1101. write([&] {
  1102. r->read_group().get_table("class_linking object")->add_empty_row();
  1103. });
  1104. REQUIRE(notification_calls == 1);
  1105. }
  1106. SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
  1107. write([&] {
  1108. table->set_int(0, 6, 13);
  1109. });
  1110. REQUIRE(notification_calls == 1);
  1111. }
  1112. SECTION("deleting non-matching rows does not send a notification") {
  1113. write([&] {
  1114. table->move_last_over(0);
  1115. table->move_last_over(6);
  1116. });
  1117. REQUIRE(notification_calls == 1);
  1118. }
  1119. SECTION("swapping adjacent matching and non-matching rows does not send notifications") {
  1120. write([&] {
  1121. table->swap_rows(0, 1);
  1122. });
  1123. REQUIRE(notification_calls == 1);
  1124. }
  1125. SECTION("swapping non-adjacent matching and non-matching rows send a single insert/delete pair") {
  1126. write([&] {
  1127. table->swap_rows(0, 2);
  1128. });
  1129. REQUIRE(notification_calls == 2);
  1130. REQUIRE_INDICES(change.deletions, 1);
  1131. REQUIRE_INDICES(change.insertions, 0);
  1132. }
  1133. SECTION("swapping matching rows sends insert/delete pairs") {
  1134. write([&] {
  1135. table->swap_rows(1, 4);
  1136. });
  1137. REQUIRE(notification_calls == 2);
  1138. REQUIRE_INDICES(change.deletions, 0, 3);
  1139. REQUIRE_INDICES(change.insertions, 0, 3);
  1140. write([&] {
  1141. table->swap_rows(1, 2);
  1142. table->swap_rows(2, 3);
  1143. table->swap_rows(3, 4);
  1144. });
  1145. REQUIRE(notification_calls == 3);
  1146. REQUIRE_INDICES(change.deletions, 1, 2, 3);
  1147. REQUIRE_INDICES(change.insertions, 0, 1, 2);
  1148. }
  1149. SECTION("swap does not inhibit move collapsing after removals") {
  1150. write([&] {
  1151. table->swap_rows(2, 3);
  1152. table->set_int(0, 3, 100);
  1153. });
  1154. REQUIRE(notification_calls == 2);
  1155. REQUIRE_INDICES(change.deletions, 1);
  1156. REQUIRE(change.insertions.empty());
  1157. }
  1158. SECTION("modifying a matching row and leaving it matching marks that row as modified") {
  1159. write([&] {
  1160. table->set_int(0, 1, 3);
  1161. });
  1162. REQUIRE(notification_calls == 2);
  1163. REQUIRE_INDICES(change.modifications, 0);
  1164. REQUIRE_INDICES(change.modifications_new, 0);
  1165. }
  1166. SECTION("modifying a matching row to no longer match marks that row as deleted") {
  1167. write([&] {
  1168. table->set_int(0, 2, 0);
  1169. });
  1170. REQUIRE(notification_calls == 2);
  1171. REQUIRE_INDICES(change.deletions, 1);
  1172. }
  1173. SECTION("modifying a non-matching row to match marks that row as inserted, but not modified") {
  1174. write([&] {
  1175. table->set_int(0, 7, 3);
  1176. });
  1177. REQUIRE(notification_calls == 2);
  1178. REQUIRE_INDICES(change.insertions, 4);
  1179. REQUIRE(change.modifications.empty());
  1180. REQUIRE(change.modifications_new.empty());
  1181. }
  1182. SECTION("deleting a matching row marks that row as deleted") {
  1183. write([&] {
  1184. table->move_last_over(3);
  1185. });
  1186. REQUIRE(notification_calls == 2);
  1187. REQUIRE_INDICES(change.deletions, 2);
  1188. }
  1189. SECTION("moving a matching row via deletion marks that row as moved") {
  1190. write([&] {
  1191. table->where().greater_equal(0, 10).find_all().clear(RemoveMode::unordered);
  1192. table->move_last_over(0);
  1193. });
  1194. REQUIRE(notification_calls == 2);
  1195. REQUIRE_MOVES(change, {3, 0});
  1196. }
  1197. SECTION("moving a matching row via subsumption marks that row as modified") {
  1198. write([&] {
  1199. table->where().greater_equal(0, 10).find_all().clear(RemoveMode::unordered);
  1200. table->move_last_over(0);
  1201. });
  1202. REQUIRE(notification_calls == 2);
  1203. REQUIRE_MOVES(change, {3, 0});
  1204. }
  1205. SECTION("modifications from multiple transactions are collapsed") {
  1206. r2->begin_transaction();
  1207. r2_table->set_int(0, 0, 6);
  1208. r2->commit_transaction();
  1209. coordinator->on_change();
  1210. r2->begin_transaction();
  1211. r2_table->set_int(0, 1, 0);
  1212. r2->commit_transaction();
  1213. REQUIRE(notification_calls == 1);
  1214. coordinator->on_change();
  1215. r->notify();
  1216. REQUIRE(notification_calls == 2);
  1217. }
  1218. SECTION("inserting a row then modifying it in a second transaction does not report it as modified") {
  1219. r2->begin_transaction();
  1220. size_t ndx = r2_table->add_empty_row();
  1221. r2_table->set_int(0, ndx, 6);
  1222. r2->commit_transaction();
  1223. coordinator->on_change();
  1224. r2->begin_transaction();
  1225. r2_table->set_int(0, ndx, 7);
  1226. r2->commit_transaction();
  1227. advance_and_notify(*r);
  1228. REQUIRE(notification_calls == 2);
  1229. REQUIRE_INDICES(change.insertions, 4);
  1230. REQUIRE(change.modifications.empty());
  1231. REQUIRE(change.modifications_new.empty());
  1232. }
  1233. SECTION("modification indices are pre-insert/delete") {
  1234. r->begin_transaction();
  1235. table->set_int(0, 2, 0);
  1236. table->set_int(0, 3, 6);
  1237. r->commit_transaction();
  1238. advance_and_notify(*r);
  1239. REQUIRE(notification_calls == 2);
  1240. REQUIRE_INDICES(change.deletions, 1);
  1241. REQUIRE_INDICES(change.modifications, 2);
  1242. REQUIRE_INDICES(change.modifications_new, 1);
  1243. }
  1244. SECTION("notifications are not delivered when collapsing transactions results in no net change") {
  1245. r2->begin_transaction();
  1246. size_t ndx = r2_table->add_empty_row();
  1247. r2_table->set_int(0, ndx, 5);
  1248. r2->commit_transaction();
  1249. coordinator->on_change();
  1250. r2->begin_transaction();
  1251. r2_table->move_last_over(ndx);
  1252. r2->commit_transaction();
  1253. REQUIRE(notification_calls == 1);
  1254. coordinator->on_change();
  1255. r->notify();
  1256. REQUIRE(notification_calls == 1);
  1257. }
  1258. SECTION("inserting a non-matching row at the beginning does not produce a notification") {
  1259. write([&] {
  1260. table->insert_empty_row(1);
  1261. });
  1262. REQUIRE(notification_calls == 1);
  1263. }
  1264. SECTION("inserting a matching row at the beginning marks just it as inserted") {
  1265. write([&] {
  1266. table->insert_empty_row(0);
  1267. table->set_int(0, 0, 5);
  1268. });
  1269. REQUIRE(notification_calls == 2);
  1270. REQUIRE_INDICES(change.insertions, 0);
  1271. }
  1272. }
  1273. SECTION("before/after change callback") {
  1274. struct Callback {
  1275. size_t before_calls = 0;
  1276. size_t after_calls = 0;
  1277. CollectionChangeSet before_change;
  1278. CollectionChangeSet after_change;
  1279. std::function<void(void)> on_before = []{};
  1280. std::function<void(void)> on_after = []{};
  1281. void before(CollectionChangeSet c) {
  1282. before_change = c;
  1283. ++before_calls;
  1284. on_before();
  1285. }
  1286. void after(CollectionChangeSet c) {
  1287. after_change = c;
  1288. ++after_calls;
  1289. on_after();
  1290. }
  1291. void error(std::exception_ptr) {
  1292. FAIL("error() should not be called");
  1293. }
  1294. } callback;
  1295. auto token = results.add_notification_callback(&callback);
  1296. advance_and_notify(*r);
  1297. SECTION("only after() is called for initial results") {
  1298. REQUIRE(callback.before_calls == 0);
  1299. REQUIRE(callback.after_calls == 1);
  1300. REQUIRE(callback.after_change.empty());
  1301. }
  1302. auto write = [&](auto&& func) {
  1303. r2->begin_transaction();
  1304. func(*r2_table);
  1305. r2->commit_transaction();
  1306. advance_and_notify(*r);
  1307. };
  1308. SECTION("both are called after a write") {
  1309. write([&](auto&& t) {
  1310. t.set_int(0, t.add_empty_row(), 5);
  1311. });
  1312. REQUIRE(callback.before_calls == 1);
  1313. REQUIRE(callback.after_calls == 2);
  1314. REQUIRE_INDICES(callback.before_change.insertions, 4);
  1315. REQUIRE_INDICES(callback.after_change.insertions, 4);
  1316. }
  1317. SECTION("deleted objects are usable in before()") {
  1318. callback.on_before = [&] {
  1319. REQUIRE(results.size() == 4);
  1320. REQUIRE_INDICES(callback.before_change.deletions, 0);
  1321. REQUIRE(results.get(0).is_attached());
  1322. REQUIRE(results.get(0).get_int(0) == 2);
  1323. };
  1324. write([&](auto&& t) {
  1325. t.move_last_over(results.get(0).get_index());
  1326. });
  1327. REQUIRE(callback.before_calls == 1);
  1328. REQUIRE(callback.after_calls == 2);
  1329. }
  1330. SECTION("inserted objects are usable in after()") {
  1331. callback.on_after = [&] {
  1332. REQUIRE(results.size() == 5);
  1333. REQUIRE_INDICES(callback.after_change.insertions, 4);
  1334. REQUIRE(results.last()->get_int(0) == 5);
  1335. };
  1336. write([&](auto&& t) {
  1337. t.set_int(0, t.add_empty_row(), 5);
  1338. });
  1339. REQUIRE(callback.before_calls == 1);
  1340. REQUIRE(callback.after_calls == 2);
  1341. }
  1342. }
  1343. SECTION("sorted notifications") {
  1344. // Sort in descending order
  1345. results = results.sort({*table, {{0}}, {false}});
  1346. int notification_calls = 0;
  1347. CollectionChangeSet change;
  1348. auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  1349. REQUIRE_FALSE(err);
  1350. change = c;
  1351. ++notification_calls;
  1352. });
  1353. advance_and_notify(*r);
  1354. auto write = [&](auto&& f) {
  1355. r->begin_transaction();
  1356. f();
  1357. r->commit_transaction();
  1358. advance_and_notify(*r);
  1359. };
  1360. SECTION("swapping rows does not send notifications") {
  1361. write([&] {
  1362. table->swap_rows(2, 3);
  1363. });
  1364. REQUIRE(notification_calls == 1);
  1365. }
  1366. SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
  1367. write([&] {
  1368. table->set_int(0, 6, 13);
  1369. });
  1370. REQUIRE(notification_calls == 1);
  1371. }
  1372. SECTION("deleting non-matching rows does not send a notification") {
  1373. write([&] {
  1374. table->move_last_over(0);
  1375. table->move_last_over(6);
  1376. });
  1377. REQUIRE(notification_calls == 1);
  1378. }
  1379. SECTION("modifying a matching row and leaving it matching marks that row as modified") {
  1380. write([&] {
  1381. table->set_int(0, 1, 3);
  1382. });
  1383. REQUIRE(notification_calls == 2);
  1384. REQUIRE_INDICES(change.modifications, 3);
  1385. REQUIRE_INDICES(change.modifications_new, 3);
  1386. }
  1387. SECTION("swapping leaves modified rows marked as modified") {
  1388. write([&] {
  1389. table->set_int(0, 1, 3);
  1390. table->swap_rows(1, 2);
  1391. });
  1392. REQUIRE(notification_calls == 2);
  1393. REQUIRE_INDICES(change.modifications, 3);
  1394. REQUIRE_INDICES(change.modifications_new, 3);
  1395. write([&] {
  1396. table->swap_rows(3, 1);
  1397. table->set_int(0, 1, 7);
  1398. });
  1399. REQUIRE(notification_calls == 3);
  1400. REQUIRE_INDICES(change.modifications, 1);
  1401. REQUIRE_INDICES(change.modifications_new, 1);
  1402. }
  1403. SECTION("modifying a matching row to no longer match marks that row as deleted") {
  1404. write([&] {
  1405. table->set_int(0, 2, 0);
  1406. });
  1407. REQUIRE(notification_calls == 2);
  1408. REQUIRE_INDICES(change.deletions, 2);
  1409. }
  1410. SECTION("modifying a non-matching row to match marks that row as inserted") {
  1411. write([&] {
  1412. table->set_int(0, 7, 3);
  1413. });
  1414. REQUIRE(notification_calls == 2);
  1415. REQUIRE_INDICES(change.insertions, 3);
  1416. }
  1417. SECTION("deleting a matching row marks that row as deleted") {
  1418. write([&] {
  1419. table->move_last_over(3);
  1420. });
  1421. REQUIRE(notification_calls == 2);
  1422. REQUIRE_INDICES(change.deletions, 1);
  1423. }
  1424. SECTION("moving a matching row via deletion does not send a notification") {
  1425. write([&] {
  1426. table->where().greater_equal(0, 10).find_all().clear(RemoveMode::unordered);
  1427. table->move_last_over(0);
  1428. });
  1429. REQUIRE(notification_calls == 1);
  1430. }
  1431. SECTION("modifying a matching row to change its position sends insert+delete") {
  1432. write([&] {
  1433. table->set_int(0, 2, 9);
  1434. });
  1435. REQUIRE(notification_calls == 2);
  1436. REQUIRE_INDICES(change.deletions, 2);
  1437. REQUIRE_INDICES(change.insertions, 0);
  1438. }
  1439. SECTION("modifications from multiple transactions are collapsed") {
  1440. r2->begin_transaction();
  1441. r2_table->set_int(0, 0, 5);
  1442. r2->commit_transaction();
  1443. r2->begin_transaction();
  1444. r2_table->set_int(0, 1, 0);
  1445. r2->commit_transaction();
  1446. REQUIRE(notification_calls == 1);
  1447. advance_and_notify(*r);
  1448. REQUIRE(notification_calls == 2);
  1449. }
  1450. SECTION("moving a matching row by deleting all other rows") {
  1451. r->begin_transaction();
  1452. table->clear();
  1453. table->add_empty_row(2);
  1454. table->set_int(0, 0, 15);
  1455. table->set_int(0, 1, 5);
  1456. r->commit_transaction();
  1457. advance_and_notify(*r);
  1458. write([&] {
  1459. table->move_last_over(0);
  1460. table->add_empty_row();
  1461. table->set_int(0, 1, 3);
  1462. });
  1463. REQUIRE(notification_calls == 3);
  1464. REQUIRE(change.deletions.empty());
  1465. REQUIRE_INDICES(change.insertions, 1);
  1466. }
  1467. }
  1468. SECTION("distinct notifications") {
  1469. results = results.distinct(SortDescriptor(*table, {{0}}));
  1470. int notification_calls = 0;
  1471. CollectionChangeSet change;
  1472. auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  1473. REQUIRE_FALSE(err);
  1474. change = c;
  1475. ++notification_calls;
  1476. });
  1477. advance_and_notify(*r);
  1478. auto write = [&](auto&& f) {
  1479. r->begin_transaction();
  1480. f();
  1481. r->commit_transaction();
  1482. advance_and_notify(*r);
  1483. };
  1484. SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
  1485. write([&] {
  1486. table->set_int(0, 6, 13);
  1487. });
  1488. REQUIRE(notification_calls == 1);
  1489. }
  1490. SECTION("deleting non-matching rows does not send a notification") {
  1491. write([&] {
  1492. table->move_last_over(0);
  1493. table->move_last_over(6);
  1494. });
  1495. REQUIRE(notification_calls == 1);
  1496. }
  1497. SECTION("modifying a matching row and leaving it matching marks that row as modified") {
  1498. write([&] {
  1499. table->set_int(0, 1, 3);
  1500. });
  1501. REQUIRE(notification_calls == 2);
  1502. REQUIRE_INDICES(change.modifications, 0);
  1503. REQUIRE_INDICES(change.modifications_new, 0);
  1504. }
  1505. SECTION("modifying a non-matching row which is after the distinct results in the table to be a same value \
  1506. in the distinct results doesn't send notification.") {
  1507. write([&] {
  1508. table->set_int(0, 6, 2);
  1509. });
  1510. REQUIRE(notification_calls == 1);
  1511. }
  1512. SECTION("modifying a non-matching row which is before the distinct results in the table to be a same value \
  1513. in the distinct results send insert + delete.") {
  1514. write([&] {
  1515. table->set_int(0, 0, 2);
  1516. });
  1517. REQUIRE(notification_calls == 2);
  1518. REQUIRE_INDICES(change.deletions, 0);
  1519. REQUIRE_INDICES(change.insertions, 0);
  1520. }
  1521. SECTION("modifying a matching row to duplicated value in distinct results marks that row as deleted") {
  1522. write([&] {
  1523. table->set_int(0, 2, 2);
  1524. });
  1525. REQUIRE(notification_calls == 2);
  1526. REQUIRE_INDICES(change.deletions, 1);
  1527. }
  1528. SECTION("modifying a non-matching row to match and different value marks that row as inserted") {
  1529. write([&] {
  1530. table->set_int(0, 0, 1);
  1531. });
  1532. REQUIRE(notification_calls == 2);
  1533. REQUIRE_INDICES(change.insertions, 0);
  1534. }
  1535. }
  1536. SECTION("schema changes") {
  1537. CollectionChangeSet change;
  1538. auto token = results.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  1539. REQUIRE_FALSE(err);
  1540. change = c;
  1541. });
  1542. advance_and_notify(*r);
  1543. auto write = [&](auto&& f) {
  1544. r->begin_transaction();
  1545. f();
  1546. r->commit_transaction();
  1547. advance_and_notify(*r);
  1548. };
  1549. SECTION("insert table before observed table") {
  1550. write([&] {
  1551. size_t row = table->add_empty_row();
  1552. table->set_int(0, row, 5);
  1553. r->read_group().insert_table(0, "new table");
  1554. table->insert_empty_row(0);
  1555. table->set_int(0, 0, 5);
  1556. });
  1557. REQUIRE_INDICES(change.insertions, 0, 5);
  1558. }
  1559. auto linked_table = table->get_link_target(1);
  1560. SECTION("insert new column before link column") {
  1561. write([&] {
  1562. linked_table->set_int(0, 1, 5);
  1563. table->insert_column(0, type_Int, "new col");
  1564. linked_table->set_int(0, 2, 5);
  1565. });
  1566. REQUIRE_INDICES(change.modifications, 0, 1);
  1567. }
  1568. SECTION("insert table before link target") {
  1569. write([&] {
  1570. linked_table->set_int(0, 1, 5);
  1571. r->read_group().insert_table(0, "new table");
  1572. linked_table->set_int(0, 2, 5);
  1573. });
  1574. REQUIRE_INDICES(change.modifications, 0, 1);
  1575. }
  1576. }
  1577. }
  1578. TEST_CASE("results: notifications after move") {
  1579. InMemoryTestFile config;
  1580. config.cache = false;
  1581. config.automatic_change_notifications = false;
  1582. auto r = Realm::get_shared_realm(config);
  1583. r->update_schema({
  1584. {"object", {
  1585. {"value", PropertyType::Int},
  1586. }},
  1587. });
  1588. auto table = r->read_group().get_table("class_object");
  1589. auto results = std::make_unique<Results>(r, *table);
  1590. int notification_calls = 0;
  1591. auto token = results->add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  1592. REQUIRE_FALSE(err);
  1593. ++notification_calls;
  1594. });
  1595. advance_and_notify(*r);
  1596. auto write = [&](auto&& f) {
  1597. r->begin_transaction();
  1598. f();
  1599. r->commit_transaction();
  1600. advance_and_notify(*r);
  1601. };
  1602. SECTION("notifications continue to work after Results is moved (move-constructor)") {
  1603. Results r(std::move(*results));
  1604. results.reset();
  1605. write([&] {
  1606. table->set_int(0, table->add_empty_row(), 1);
  1607. });
  1608. REQUIRE(notification_calls == 2);
  1609. }
  1610. SECTION("notifications continue to work after Results is moved (move-assignment)") {
  1611. Results r;
  1612. r = std::move(*results);
  1613. results.reset();
  1614. write([&] {
  1615. table->set_int(0, table->add_empty_row(), 1);
  1616. });
  1617. REQUIRE(notification_calls == 2);
  1618. }
  1619. }
  1620. TEST_CASE("results: notifier with no callbacks") {
  1621. _impl::RealmCoordinator::assert_no_open_realms();
  1622. InMemoryTestFile config;
  1623. config.cache = false;
  1624. config.automatic_change_notifications = false;
  1625. auto coordinator = _impl::RealmCoordinator::get_coordinator(config.path);
  1626. auto r = coordinator->get_realm(std::move(config));
  1627. r->update_schema({
  1628. {"object", {
  1629. {"value", PropertyType::Int},
  1630. }},
  1631. });
  1632. auto table = r->read_group().get_table("class_object");
  1633. Results results(r, table->where());
  1634. results.last(); // force evaluation and creation of TableView
  1635. SECTION("refresh() does not block due to implicit notifier") {
  1636. // Create and then immediately remove a callback because
  1637. // `automatic_change_notifications = false` makes Results not implicitly
  1638. // create a notifier
  1639. results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) {});
  1640. auto r2 = coordinator->get_realm();
  1641. r2->begin_transaction();
  1642. r2->read_group().get_table("class_object")->add_empty_row();
  1643. r2->commit_transaction();
  1644. r->refresh(); // would deadlock if there was a callback
  1645. }
  1646. SECTION("refresh() does not attempt to deliver stale results") {
  1647. results.add_notification_callback([](CollectionChangeSet const&, std::exception_ptr) {});
  1648. // Create version 1
  1649. r->begin_transaction();
  1650. table->add_empty_row();
  1651. r->commit_transaction();
  1652. r->begin_transaction();
  1653. // Run async query for version 1
  1654. coordinator->on_change();
  1655. // Create version 2 without ever letting 1 be delivered
  1656. table->add_empty_row();
  1657. r->commit_transaction();
  1658. // Give it a chance to deliver the async query results (and fail, becuse
  1659. // they're for version 1 and the realm is at 2)
  1660. r->refresh();
  1661. }
  1662. SECTION("should not pin the source version even after the Realm has been closed") {
  1663. auto r2 = coordinator->get_realm();
  1664. REQUIRE(r != r2);
  1665. r->close();
  1666. auto& shared_group = TestHelper::get_shared_group(r2);
  1667. // There's always at least 2 live versions because the previous version
  1668. // isn't clean up until the *next* commit
  1669. REQUIRE(shared_group.get_number_of_versions() == 2);
  1670. auto table = r2->read_group().get_table("class_object");
  1671. r2->begin_transaction();
  1672. table->add_empty_row();
  1673. r2->commit_transaction();
  1674. r2->begin_transaction();
  1675. table->add_empty_row();
  1676. r2->commit_transaction();
  1677. // Would now be 3 if the closed Realm is still pinning the version it was at
  1678. REQUIRE(shared_group.get_number_of_versions() == 2);
  1679. }
  1680. }
  1681. TEST_CASE("results: error messages") {
  1682. InMemoryTestFile config;
  1683. config.schema = Schema{
  1684. {"object", {
  1685. {"value", PropertyType::String},
  1686. }},
  1687. };
  1688. auto r = Realm::get_shared_realm(config);
  1689. auto table = r->read_group().get_table("class_object");
  1690. Results results(r, *table);
  1691. r->begin_transaction();
  1692. table->add_empty_row();
  1693. r->commit_transaction();
  1694. SECTION("out of bounds access") {
  1695. REQUIRE_THROWS_WITH(results.get(5), "Requested index 5 greater than max 0");
  1696. }
  1697. SECTION("unsupported aggregate operation") {
  1698. REQUIRE_THROWS_WITH(results.sum(0), "Cannot sum property 'value': operation not supported for 'string' properties");
  1699. }
  1700. }
  1701. TEST_CASE("results: snapshots") {
  1702. InMemoryTestFile config;
  1703. config.cache = false;
  1704. config.automatic_change_notifications = false;
  1705. config.schema = Schema{
  1706. {"object", {
  1707. {"value", PropertyType::Int},
  1708. {"array", PropertyType::Array|PropertyType::Object, "linked to object"}
  1709. }},
  1710. {"linked to object", {
  1711. {"value", PropertyType::Int}
  1712. }}
  1713. };
  1714. auto r = Realm::get_shared_realm(config);
  1715. auto write = [&](auto&& f) {
  1716. r->begin_transaction();
  1717. f();
  1718. r->commit_transaction();
  1719. advance_and_notify(*r);
  1720. };
  1721. SECTION("snapshot of empty Results") {
  1722. Results results;
  1723. auto snapshot = results.snapshot();
  1724. REQUIRE(snapshot.size() == 0);
  1725. }
  1726. SECTION("snapshot of Results based on Table") {
  1727. auto table = r->read_group().get_table("class_object");
  1728. Results results(r, *table);
  1729. {
  1730. // A newly-added row should not appear in the snapshot.
  1731. auto snapshot = results.snapshot();
  1732. REQUIRE(results.size() == 0);
  1733. REQUIRE(snapshot.size() == 0);
  1734. write([=]{
  1735. table->add_empty_row();
  1736. });
  1737. REQUIRE(results.size() == 1);
  1738. REQUIRE(snapshot.size() == 0);
  1739. }
  1740. {
  1741. // Removing a row present in the snapshot should not affect the size of the snapshot,
  1742. // but will result in the snapshot returning a detached row accessor.
  1743. auto snapshot = results.snapshot();
  1744. REQUIRE(results.size() == 1);
  1745. REQUIRE(snapshot.size() == 1);
  1746. write([=]{
  1747. table->move_last_over(0);
  1748. });
  1749. REQUIRE(results.size() == 0);
  1750. REQUIRE(snapshot.size() == 1);
  1751. REQUIRE(!snapshot.get(0).is_attached());
  1752. // Adding a row at the same index that was formerly present in the snapshot shouldn't
  1753. // affect the state of the snapshot.
  1754. write([=]{
  1755. table->add_empty_row();
  1756. });
  1757. REQUIRE(snapshot.size() == 1);
  1758. REQUIRE(!snapshot.get(0).is_attached());
  1759. }
  1760. }
  1761. SECTION("snapshot of Results based on LinkView") {
  1762. auto object = r->read_group().get_table("class_object");
  1763. auto linked_to = r->read_group().get_table("class_linked to object");
  1764. write([=]{
  1765. object->add_empty_row();
  1766. });
  1767. LinkViewRef lv = object->get_linklist(1, 0);
  1768. Results results(r, lv);
  1769. {
  1770. // A newly-added row should not appear in the snapshot.
  1771. auto snapshot = results.snapshot();
  1772. REQUIRE(results.size() == 0);
  1773. REQUIRE(snapshot.size() == 0);
  1774. write([=]{
  1775. lv->add(linked_to->add_empty_row());
  1776. });
  1777. REQUIRE(results.size() == 1);
  1778. REQUIRE(snapshot.size() == 0);
  1779. }
  1780. {
  1781. // Removing a row from the link list should not affect the snapshot.
  1782. auto snapshot = results.snapshot();
  1783. REQUIRE(results.size() == 1);
  1784. REQUIRE(snapshot.size() == 1);
  1785. write([=]{
  1786. lv->remove(0);
  1787. });
  1788. REQUIRE(results.size() == 0);
  1789. REQUIRE(snapshot.size() == 1);
  1790. REQUIRE(snapshot.get(0).is_attached());
  1791. // Removing a row present in the snapshot from its table should result in the snapshot
  1792. // returning a detached row accessor.
  1793. write([=]{
  1794. linked_to->remove(0);
  1795. });
  1796. REQUIRE(snapshot.size() == 1);
  1797. REQUIRE(!snapshot.get(0).is_attached());
  1798. // Adding a new row to the link list shouldn't affect the state of the snapshot.
  1799. write([=]{
  1800. lv->add(linked_to->add_empty_row());
  1801. });
  1802. REQUIRE(snapshot.size() == 1);
  1803. REQUIRE(!snapshot.get(0).is_attached());
  1804. }
  1805. }
  1806. SECTION("snapshot of Results based on Query") {
  1807. auto table = r->read_group().get_table("class_object");
  1808. Query q = table->column<Int>(0) > 0;
  1809. Results results(r, std::move(q));
  1810. {
  1811. // A newly-added row should not appear in the snapshot.
  1812. auto snapshot = results.snapshot();
  1813. REQUIRE(results.size() == 0);
  1814. REQUIRE(snapshot.size() == 0);
  1815. write([=]{
  1816. table->set_int(0, table->add_empty_row(), 1);
  1817. });
  1818. REQUIRE(results.size() == 1);
  1819. REQUIRE(snapshot.size() == 0);
  1820. }
  1821. {
  1822. // Updating a row to no longer match the query criteria should not affect the snapshot.
  1823. auto snapshot = results.snapshot();
  1824. REQUIRE(results.size() == 1);
  1825. REQUIRE(snapshot.size() == 1);
  1826. write([=]{
  1827. table->set_int(0, 0, 0);
  1828. });
  1829. REQUIRE(results.size() == 0);
  1830. REQUIRE(snapshot.size() == 1);
  1831. REQUIRE(snapshot.get(0).is_attached());
  1832. // Removing a row present in the snapshot from its table should result in the snapshot
  1833. // returning a detached row accessor.
  1834. write([=]{
  1835. table->remove(0);
  1836. });
  1837. REQUIRE(snapshot.size() == 1);
  1838. REQUIRE(!snapshot.get(0).is_attached());
  1839. // Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
  1840. write([=]{
  1841. table->set_int(0, table->add_empty_row(), 1);
  1842. });
  1843. REQUIRE(snapshot.size() == 1);
  1844. REQUIRE(!snapshot.get(0).is_attached());
  1845. }
  1846. }
  1847. SECTION("snapshot of Results based on TableView from query") {
  1848. auto table = r->read_group().get_table("class_object");
  1849. Query q = table->column<Int>(0) > 0;
  1850. Results results(r, q.find_all());
  1851. {
  1852. // A newly-added row should not appear in the snapshot.
  1853. auto snapshot = results.snapshot();
  1854. REQUIRE(results.size() == 0);
  1855. REQUIRE(snapshot.size() == 0);
  1856. write([=]{
  1857. table->set_int(0, table->add_empty_row(), 1);
  1858. });
  1859. REQUIRE(results.size() == 1);
  1860. REQUIRE(snapshot.size() == 0);
  1861. }
  1862. {
  1863. // Updating a row to no longer match the query criteria should not affect the snapshot.
  1864. auto snapshot = results.snapshot();
  1865. REQUIRE(results.size() == 1);
  1866. REQUIRE(snapshot.size() == 1);
  1867. write([=]{
  1868. table->set_int(0, 0, 0);
  1869. });
  1870. REQUIRE(results.size() == 0);
  1871. REQUIRE(snapshot.size() == 1);
  1872. REQUIRE(snapshot.get(0).is_attached());
  1873. // Removing a row present in the snapshot from its table should result in the snapshot
  1874. // returning a detached row accessor.
  1875. write([=]{
  1876. table->remove(0);
  1877. });
  1878. REQUIRE(snapshot.size() == 1);
  1879. REQUIRE(!snapshot.get(0).is_attached());
  1880. // Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
  1881. write([=]{
  1882. table->set_int(0, table->add_empty_row(), 1);
  1883. });
  1884. REQUIRE(snapshot.size() == 1);
  1885. REQUIRE(!snapshot.get(0).is_attached());
  1886. }
  1887. }
  1888. SECTION("snapshot of Results based on TableView from backlinks") {
  1889. auto object = r->read_group().get_table("class_object");
  1890. auto linked_to = r->read_group().get_table("class_linked to object");
  1891. write([=]{
  1892. linked_to->add_empty_row();
  1893. });
  1894. TableView backlinks = linked_to->get_backlink_view(0, object.get(), 1);
  1895. Results results(r, std::move(backlinks));
  1896. auto lv = object->get_linklist(1, object->add_empty_row());
  1897. {
  1898. // A newly-added row should not appear in the snapshot.
  1899. auto snapshot = results.snapshot();
  1900. REQUIRE(results.size() == 0);
  1901. REQUIRE(snapshot.size() == 0);
  1902. write([=]{
  1903. lv->add(0);
  1904. });
  1905. REQUIRE(results.size() == 1);
  1906. REQUIRE(snapshot.size() == 0);
  1907. }
  1908. {
  1909. // Removing the link should not affect the snapshot.
  1910. auto snapshot = results.snapshot();
  1911. REQUIRE(results.size() == 1);
  1912. REQUIRE(snapshot.size() == 1);
  1913. write([=]{
  1914. lv->remove(0);
  1915. });
  1916. REQUIRE(results.size() == 0);
  1917. REQUIRE(snapshot.size() == 1);
  1918. REQUIRE(snapshot.get(0).is_attached());
  1919. // Removing a row present in the snapshot from its table should result in the snapshot
  1920. // returning a detached row accessor.
  1921. write([=]{
  1922. object->remove(0);
  1923. });
  1924. REQUIRE(snapshot.size() == 1);
  1925. REQUIRE(!snapshot.get(0).is_attached());
  1926. // Adding a new link shouldn't affect the state of the snapshot.
  1927. write([=]{
  1928. object->add_empty_row();
  1929. auto lv = object->get_linklist(1, object->add_empty_row());
  1930. lv->add(0);
  1931. });
  1932. REQUIRE(snapshot.size() == 1);
  1933. REQUIRE(!snapshot.get(0).is_attached());
  1934. }
  1935. }
  1936. SECTION("snapshot of Results with notification callback registered") {
  1937. auto table = r->read_group().get_table("class_object");
  1938. Query q = table->column<Int>(0) > 0;
  1939. Results results(r, q.find_all());
  1940. auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
  1941. REQUIRE_FALSE(err);
  1942. });
  1943. advance_and_notify(*r);
  1944. SECTION("snapshot of lvalue") {
  1945. auto snapshot = results.snapshot();
  1946. write([=] {
  1947. table->set_int(0, table->add_empty_row(), 1);
  1948. });
  1949. REQUIRE(snapshot.size() == 0);
  1950. }
  1951. SECTION("snapshot of rvalue") {
  1952. auto snapshot = std::move(results).snapshot();
  1953. write([=] {
  1954. table->set_int(0, table->add_empty_row(), 1);
  1955. });
  1956. REQUIRE(snapshot.size() == 0);
  1957. }
  1958. }
  1959. SECTION("adding notification callback to snapshot throws") {
  1960. auto table = r->read_group().get_table("class_object");
  1961. Query q = table->column<Int>(0) > 0;
  1962. Results results(r, q.find_all());
  1963. auto snapshot = results.snapshot();
  1964. CHECK_THROWS(snapshot.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {}));
  1965. }
  1966. SECTION("accessors should return none for detached row") {
  1967. auto table = r->read_group().get_table("class_object");
  1968. write([=] {
  1969. table->add_empty_row();
  1970. });
  1971. Results results(r, *table);
  1972. auto snapshot = results.snapshot();
  1973. write([=] {;
  1974. table->clear();
  1975. });
  1976. REQUIRE_FALSE(snapshot.get(0).is_attached());
  1977. REQUIRE_FALSE(snapshot.first()->is_attached());
  1978. REQUIRE_FALSE(snapshot.last()->is_attached());
  1979. }
  1980. }
  1981. TEST_CASE("results: distinct") {
  1982. const int N = 10;
  1983. InMemoryTestFile config;
  1984. config.cache = false;
  1985. config.automatic_change_notifications = false;
  1986. auto r = Realm::get_shared_realm(config);
  1987. r->update_schema({
  1988. {"object", {
  1989. {"num1", PropertyType::Int},
  1990. {"string", PropertyType::String},
  1991. {"num2", PropertyType::Int},
  1992. {"num3", PropertyType::Int}
  1993. }},
  1994. });
  1995. auto table = r->read_group().get_table("class_object");
  1996. r->begin_transaction();
  1997. table->add_empty_row(N);
  1998. for (int i = 0; i < N; ++i) {
  1999. table->set_int(0, i, i % 3);
  2000. table->set_string(1, i, util::format("Foo_%1", i % 3).c_str());
  2001. table->set_int(2, i, N - i);
  2002. table->set_int(3, i, i % 2);
  2003. }
  2004. // table:
  2005. // 0, Foo_0, 10, 0
  2006. // 1, Foo_1, 9, 1
  2007. // 2, Foo_2, 8, 0
  2008. // 0, Foo_0, 7, 1
  2009. // 1, Foo_1, 6, 0
  2010. // 2, Foo_2, 5, 1
  2011. // 0, Foo_0, 4, 0
  2012. // 1, Foo_1, 3, 1
  2013. // 2, Foo_2, 2, 0
  2014. // 0, Foo_0, 1, 1
  2015. r->commit_transaction();
  2016. Results results(r, table->where());
  2017. SECTION("Single integer property") {
  2018. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2019. // unique:
  2020. // 0, Foo_0, 10
  2021. // 1, Foo_1, 9
  2022. // 2, Foo_2, 8
  2023. REQUIRE(unique.size() == 3);
  2024. REQUIRE(unique.get(0).get_int(2) == 10);
  2025. REQUIRE(unique.get(1).get_int(2) == 9);
  2026. REQUIRE(unique.get(2).get_int(2) == 8);
  2027. }
  2028. SECTION("Single integer via apply_ordering") {
  2029. DescriptorOrdering ordering;
  2030. ordering.append_sort(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2031. ordering.append_distinct(DistinctDescriptor(results.get_tableview().get_parent(), {{0}}));
  2032. Results unique = results.apply_ordering(std::move(ordering));
  2033. // unique:
  2034. // 0, Foo_0, 10
  2035. // 1, Foo_1, 9
  2036. // 2, Foo_2, 8
  2037. REQUIRE(unique.size() == 3);
  2038. REQUIRE(unique.get(0).get_int(2) == 10);
  2039. REQUIRE(unique.get(1).get_int(2) == 9);
  2040. REQUIRE(unique.get(2).get_int(2) == 8);
  2041. }
  2042. SECTION("Single string property") {
  2043. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{1}}));
  2044. // unique:
  2045. // 0, Foo_0, 10
  2046. // 1, Foo_1, 9
  2047. // 2, Foo_2, 8
  2048. REQUIRE(unique.size() == 3);
  2049. REQUIRE(unique.get(0).get_int(2) == 10);
  2050. REQUIRE(unique.get(1).get_int(2) == 9);
  2051. REQUIRE(unique.get(2).get_int(2) == 8);
  2052. }
  2053. SECTION("Two integer properties combined") {
  2054. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{0}, {2}}));
  2055. // unique is the same as the table
  2056. REQUIRE(unique.size() == N);
  2057. for (int i = 0; i < N; ++i) {
  2058. REQUIRE(unique.get(i).get_string(1) == StringData(util::format("Foo_%1", i % 3).c_str()));
  2059. }
  2060. }
  2061. SECTION("String and integer combined") {
  2062. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{2}, {1}}));
  2063. // unique is the same as the table
  2064. REQUIRE(unique.size() == N);
  2065. for (int i = 0; i < N; ++i) {
  2066. REQUIRE(unique.get(i).get_string(1) == StringData(util::format("Foo_%1", i % 3).c_str()));
  2067. }
  2068. }
  2069. // This section and next section demonstrate that sort().distinct() != distinct().sort()
  2070. SECTION("Order after sort and distinct") {
  2071. Results reverse = results.sort(SortDescriptor(results.get_tableview().get_parent(), {{2}}, {true}));
  2072. // reverse:
  2073. // 0, Foo_0, 1
  2074. // ...
  2075. // 0, Foo_0, 10
  2076. REQUIRE(reverse.first()->get_int(2) == 1);
  2077. REQUIRE(reverse.last()->get_int(2) == 10);
  2078. // distinct() will be applied to the table, after sorting
  2079. Results unique = reverse.distinct(SortDescriptor(reverse.get_tableview().get_parent(), {{0}}));
  2080. // unique:
  2081. // 0, Foo_0, 1
  2082. // 2, Foo_2, 2
  2083. // 1, Foo_1, 3
  2084. REQUIRE(unique.size() == 3);
  2085. REQUIRE(unique.get(0).get_int(2) == 1);
  2086. REQUIRE(unique.get(1).get_int(2) == 2);
  2087. REQUIRE(unique.get(2).get_int(2) == 3);
  2088. }
  2089. SECTION("Order after distinct and sort") {
  2090. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2091. // unique:
  2092. // 0, Foo_0, 10
  2093. // 1, Foo_1, 9
  2094. // 2, Foo_2, 8
  2095. REQUIRE(unique.size() == 3);
  2096. REQUIRE(unique.first()->get_int(2) == 10);
  2097. REQUIRE(unique.last()->get_int(2) == 8);
  2098. // sort() is only applied to unique
  2099. Results reverse = unique.sort(SortDescriptor(unique.get_tableview().get_parent(), {{2}}, {true}));
  2100. // reversed:
  2101. // 2, Foo_2, 8
  2102. // 1, Foo_1, 9
  2103. // 0, Foo_0, 10
  2104. REQUIRE(reverse.size() == 3);
  2105. REQUIRE(reverse.get(0).get_int(2) == 8);
  2106. REQUIRE(reverse.get(1).get_int(2) == 9);
  2107. REQUIRE(reverse.get(2).get_int(2) == 10);
  2108. }
  2109. SECTION("Chaining distinct") {
  2110. Results first = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2111. REQUIRE(first.size() == 3);
  2112. // distinct() will not discard the previous applied distinct() calls
  2113. Results second = first.distinct(SortDescriptor(first.get_tableview().get_parent(), {{3}}));
  2114. REQUIRE(second.size() == 2);
  2115. }
  2116. SECTION("Chaining sort") {
  2117. using cols_0_3 = std::pair<size_t, size_t>;
  2118. Results first = results.sort(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2119. Results second = first.sort(SortDescriptor(first.get_tableview().get_parent(), {{3}}));
  2120. REQUIRE(second.size() == 10);
  2121. // results are ordered first by the last sorted column
  2122. // if any duplicates exist in that column, they are resolved by sorting the
  2123. // previously sorted column. Eg. sort(a).sort(b) == sort(b, a)
  2124. std::vector<cols_0_3> results
  2125. = {{0, 0}, {0, 0}, {1, 0}, {2, 0}, {2, 0}, {0, 1}, {0, 1}, {1, 1}, {1, 1}, {2, 1}};
  2126. for (size_t i = 0; i < results.size(); ++i) {
  2127. REQUIRE(second.get(i).get_int(0) == results[i].first);
  2128. REQUIRE(second.get(i).get_int(3) == results[i].second);
  2129. }
  2130. }
  2131. SECTION("Distinct is carried over to new queries") {
  2132. Results unique = results.distinct(SortDescriptor(results.get_tableview().get_parent(), {{0}}));
  2133. // unique:
  2134. // 0, Foo_0, 10
  2135. // 1, Foo_1, 9
  2136. // 2, Foo_2, 8
  2137. REQUIRE(unique.size() == 3);
  2138. Results filtered = unique.filter(Query(table->where().less(0, 2)));
  2139. // filtered:
  2140. // 0, Foo_0, 10
  2141. // 1, Foo_1, 9
  2142. REQUIRE(filtered.size() == 2);
  2143. REQUIRE(filtered.get(0).get_int(2) == 10);
  2144. REQUIRE(filtered.get(1).get_int(2) == 9);
  2145. }
  2146. SECTION("Distinct will not forget previous query") {
  2147. Results filtered = results.filter(Query(table->where().greater(2, 5)));
  2148. // filtered:
  2149. // 0, Foo_0, 10
  2150. // 1, Foo_1, 9
  2151. // 2, Foo_2, 8
  2152. // 0, Foo_0, 7
  2153. // 1, Foo_1, 6
  2154. REQUIRE(filtered.size() == 5);
  2155. Results unique = filtered.distinct(SortDescriptor(filtered.get_tableview().get_parent(), {{0}}));
  2156. // unique:
  2157. // 0, Foo_0, 10
  2158. // 1, Foo_1, 9
  2159. // 2, Foo_2, 8
  2160. REQUIRE(unique.size() == 3);
  2161. REQUIRE(unique.get(0).get_int(2) == 10);
  2162. REQUIRE(unique.get(1).get_int(2) == 9);
  2163. REQUIRE(unique.get(2).get_int(2) == 8);
  2164. Results further_filtered = unique.filter(Query(table->where().equal(2, 9)));
  2165. // further_filtered:
  2166. // 1, Foo_1, 9
  2167. REQUIRE(further_filtered.size() == 1);
  2168. REQUIRE(further_filtered.get(0).get_int(2) == 9);
  2169. }
  2170. }
  2171. TEST_CASE("results: sort") {
  2172. InMemoryTestFile config;
  2173. config.cache = false;
  2174. config.schema = Schema{
  2175. {"object", {
  2176. {"value", PropertyType::Int},
  2177. {"bool", PropertyType::Bool},
  2178. {"data prop", PropertyType::Data},
  2179. {"link", PropertyType::Object|PropertyType::Nullable, "object 2"},
  2180. {"array", PropertyType::Object|PropertyType::Array, "object 2"},
  2181. }},
  2182. {"object 2", {
  2183. {"value", PropertyType::Int},
  2184. {"link", PropertyType::Object|PropertyType::Nullable, "object"},
  2185. }},
  2186. };
  2187. auto realm = Realm::get_shared_realm(config);
  2188. auto table = realm->read_group().get_table("class_object");
  2189. auto table2 = realm->read_group().get_table("class_object 2");
  2190. Results r(realm, *table);
  2191. SECTION("invalid keypaths") {
  2192. SECTION("empty property name") {
  2193. REQUIRE_THROWS_WITH(r.sort({{"", true}}), "Cannot sort on key path '': missing property name.");
  2194. REQUIRE_THROWS_WITH(r.sort({{".", true}}), "Cannot sort on key path '.': missing property name.");
  2195. REQUIRE_THROWS_WITH(r.sort({{"link.", true}}), "Cannot sort on key path 'link.': missing property name.");
  2196. REQUIRE_THROWS_WITH(r.sort({{".value", true}}), "Cannot sort on key path '.value': missing property name.");
  2197. REQUIRE_THROWS_WITH(r.sort({{"link..value", true}}), "Cannot sort on key path 'link..value': missing property name.");
  2198. }
  2199. SECTION("bad property name") {
  2200. REQUIRE_THROWS_WITH(r.sort({{"not a property", true}}),
  2201. "Cannot sort on key path 'not a property': property 'object.not a property' does not exist.");
  2202. REQUIRE_THROWS_WITH(r.sort({{"link.not a property", true}}),
  2203. "Cannot sort on key path 'link.not a property': property 'object 2.not a property' does not exist.");
  2204. }
  2205. SECTION("subscript primitive") {
  2206. REQUIRE_THROWS_WITH(r.sort({{"value.link", true}}),
  2207. "Cannot sort on key path 'value.link': property 'object.value' of type 'int' may only be the final property in the key path.");
  2208. }
  2209. SECTION("end in link") {
  2210. REQUIRE_THROWS_WITH(r.sort({{"link", true}}),
  2211. "Cannot sort on key path 'link': property 'object.link' of type 'object' cannot be the final property in the key path.");
  2212. REQUIRE_THROWS_WITH(r.sort({{"link.link", true}}),
  2213. "Cannot sort on key path 'link.link': property 'object 2.link' of type 'object' cannot be the final property in the key path.");
  2214. }
  2215. SECTION("sort involving bad property types") {
  2216. REQUIRE_THROWS_WITH(r.sort({{"array", true}}),
  2217. "Cannot sort on key path 'array': property 'object.array' is of unsupported type 'array'.");
  2218. REQUIRE_THROWS_WITH(r.sort({{"array.value", true}}),
  2219. "Cannot sort on key path 'array.value': property 'object.array' is of unsupported type 'array'.");
  2220. REQUIRE_THROWS_WITH(r.sort({{"link.link.array.value", true}}),
  2221. "Cannot sort on key path 'link.link.array.value': property 'object.array' is of unsupported type 'array'.");
  2222. REQUIRE_THROWS_WITH(r.sort({{"data prop", true}}),
  2223. "Cannot sort on key path 'data prop': property 'object.data prop' is of unsupported type 'data'.");
  2224. }
  2225. }
  2226. realm->begin_transaction();
  2227. table->add_empty_row(4);
  2228. table2->add_empty_row(4);
  2229. for (int i = 0; i < 4; ++i) {
  2230. table->set_int(0, i, (i + 2) % 4);
  2231. table->set_bool(1, i, i % 2);
  2232. table->set_link(3, i, 3 - i);
  2233. table2->set_int(0, i, (i + 1) % 4);
  2234. table2->set_link(1, i, i);
  2235. }
  2236. realm->commit_transaction();
  2237. /*
  2238. | index | value | bool | link.value | link.link.value |
  2239. |-------|-------|------|------------|-----------------|
  2240. | 0 | 2 | 0 | 0 | 1 |
  2241. | 1 | 3 | 1 | 3 | 0 |
  2242. | 2 | 0 | 0 | 2 | 3 |
  2243. | 3 | 1 | 1 | 1 | 2 |
  2244. */
  2245. #define REQUIRE_ORDER(sort, ...) do { \
  2246. std::vector<size_t> expected = {__VA_ARGS__}; \
  2247. auto results = sort; \
  2248. REQUIRE(results.size() == expected.size()); \
  2249. for (size_t i = 0; i < expected.size(); ++i) \
  2250. REQUIRE(results.get(i).get_index() == expected[i]); \
  2251. } while (0)
  2252. SECTION("sort on single property") {
  2253. REQUIRE_ORDER((r.sort({{"value", true}})),
  2254. 2, 3, 0, 1);
  2255. REQUIRE_ORDER((r.sort({{"value", false}})),
  2256. 1, 0, 3, 2);
  2257. }
  2258. SECTION("sort on two properties") {
  2259. REQUIRE_ORDER((r.sort({{"bool", true}, {"value", true}})),
  2260. 2, 0, 3, 1);
  2261. REQUIRE_ORDER((r.sort({{"bool", false}, {"value", true}})),
  2262. 3, 1, 2, 0);
  2263. REQUIRE_ORDER((r.sort({{"bool", true}, {"value", false}})),
  2264. 0, 2, 1, 3);
  2265. REQUIRE_ORDER((r.sort({{"bool", false}, {"value", false}})),
  2266. 1, 3, 0, 2);
  2267. }
  2268. SECTION("sort over link") {
  2269. REQUIRE_ORDER((r.sort({{"link.value", true}})),
  2270. 0, 3, 2, 1);
  2271. REQUIRE_ORDER((r.sort({{"link.value", false}})),
  2272. 1, 2, 3, 0);
  2273. }
  2274. SECTION("sort over two links") {
  2275. REQUIRE_ORDER((r.sort({{"link.link.value", true}})),
  2276. 1, 0, 3, 2);
  2277. REQUIRE_ORDER((r.sort({{"link.link.value", false}})),
  2278. 2, 3, 0, 1);
  2279. }
  2280. }
  2281. struct ResultsFromTable {
  2282. static Results call(std::shared_ptr<Realm> r, Table* table) {
  2283. return Results(std::move(r), *table);
  2284. }
  2285. };
  2286. struct ResultsFromQuery {
  2287. static Results call(std::shared_ptr<Realm> r, Table* table) {
  2288. return Results(std::move(r), table->where());
  2289. }
  2290. };
  2291. struct ResultsFromTableView {
  2292. static Results call(std::shared_ptr<Realm> r, Table* table) {
  2293. return Results(std::move(r), table->where().find_all());
  2294. }
  2295. };
  2296. struct ResultsFromLinkView {
  2297. static Results call(std::shared_ptr<Realm> r, Table* table) {
  2298. r->begin_transaction();
  2299. auto link_table = r->read_group().get_table("class_linking_object");
  2300. link_table->add_empty_row(1);
  2301. auto link_view = link_table->get_linklist(0, 0);
  2302. for (size_t i = 0; i < table->size(); ++i)
  2303. link_view->add(i);
  2304. r->commit_transaction();
  2305. return Results(r, link_view);
  2306. }
  2307. };
  2308. TEMPLATE_TEST_CASE("results: aggregate", ResultsFromTable, ResultsFromQuery, ResultsFromTableView, ResultsFromLinkView) {
  2309. InMemoryTestFile config;
  2310. config.cache = false;
  2311. config.automatic_change_notifications = false;
  2312. auto r = Realm::get_shared_realm(config);
  2313. r->update_schema({
  2314. {"object", {
  2315. {"int", PropertyType::Int|PropertyType::Nullable},
  2316. {"float", PropertyType::Float|PropertyType::Nullable},
  2317. {"double", PropertyType::Double|PropertyType::Nullable},
  2318. {"date", PropertyType::Date|PropertyType::Nullable},
  2319. }},
  2320. {"linking_object", {
  2321. {"link", PropertyType::Array|PropertyType::Object, "object"}
  2322. }},
  2323. });
  2324. auto table = r->read_group().get_table("class_object");
  2325. SECTION("one row with null values") {
  2326. r->begin_transaction();
  2327. table->add_empty_row(3);
  2328. table->set_int(0, 1, 0);
  2329. table->set_float(1, 1, 0.f);
  2330. table->set_double(2, 1, 0.0);
  2331. table->set_timestamp(3, 1, Timestamp(0, 0));
  2332. table->set_int(0, 2, 2);
  2333. table->set_float(1, 2, 2.f);
  2334. table->set_double(2, 2, 2.0);
  2335. table->set_timestamp(3, 2, Timestamp(2, 0));
  2336. // table:
  2337. // null, null, null, null,
  2338. // 0, 0, 0, (0, 0)
  2339. // 2, 2, 2, (2, 0)
  2340. r->commit_transaction();
  2341. Results results = TestType::call(r, table.get());
  2342. SECTION("max") {
  2343. REQUIRE(results.max(0)->get_int() == 2);
  2344. REQUIRE(results.max(1)->get_float() == 2.f);
  2345. REQUIRE(results.max(2)->get_double() == 2.0);
  2346. REQUIRE(results.max(3)->get_timestamp() == Timestamp(2, 0));
  2347. }
  2348. SECTION("min") {
  2349. REQUIRE(results.min(0)->get_int() == 0);
  2350. REQUIRE(results.min(1)->get_float() == 0.f);
  2351. REQUIRE(results.min(2)->get_double() == 0.0);
  2352. REQUIRE(results.min(3)->get_timestamp() == Timestamp(0, 0));
  2353. }
  2354. SECTION("average") {
  2355. REQUIRE(results.average(0) == 1.0);
  2356. REQUIRE(results.average(1) == 1.0);
  2357. REQUIRE(results.average(2) == 1.0);
  2358. REQUIRE_THROWS_AS(results.average(3), Results::UnsupportedColumnTypeException);
  2359. }
  2360. SECTION("sum") {
  2361. REQUIRE(results.sum(0)->get_int() == 2);
  2362. REQUIRE(results.sum(1)->get_double() == 2.0);
  2363. REQUIRE(results.sum(2)->get_double() == 2.0);
  2364. REQUIRE_THROWS_AS(results.sum(3), Results::UnsupportedColumnTypeException);
  2365. }
  2366. }
  2367. SECTION("rows with all null values") {
  2368. r->begin_transaction();
  2369. table->add_empty_row(3);
  2370. // table:
  2371. // null, null, null, null, null
  2372. // null, null, null, null, null
  2373. // null, null, null, null, null
  2374. r->commit_transaction();
  2375. Results results = TestType::call(r, table.get());
  2376. SECTION("max") {
  2377. REQUIRE(!results.max(0));
  2378. REQUIRE(!results.max(1));
  2379. REQUIRE(!results.max(2));
  2380. REQUIRE(!results.max(3));
  2381. }
  2382. SECTION("min") {
  2383. REQUIRE(!results.min(0));
  2384. REQUIRE(!results.min(1));
  2385. REQUIRE(!results.min(2));
  2386. REQUIRE(!results.min(3));
  2387. }
  2388. SECTION("average") {
  2389. REQUIRE(!results.average(0));
  2390. REQUIRE(!results.average(1));
  2391. REQUIRE(!results.average(2));
  2392. REQUIRE_THROWS_AS(results.average(3), Results::UnsupportedColumnTypeException);
  2393. }
  2394. SECTION("sum") {
  2395. REQUIRE(results.sum(0)->get_int() == 0);
  2396. REQUIRE(results.sum(1)->get_double() == 0.0);
  2397. REQUIRE(results.sum(2)->get_double() == 0.0);
  2398. REQUIRE_THROWS_AS(results.sum(3), Results::UnsupportedColumnTypeException);
  2399. }
  2400. }
  2401. SECTION("empty") {
  2402. Results results = TestType::call(r, table.get());
  2403. SECTION("max") {
  2404. REQUIRE(!results.max(0));
  2405. REQUIRE(!results.max(1));
  2406. REQUIRE(!results.max(2));
  2407. REQUIRE(!results.max(3));
  2408. }
  2409. SECTION("min") {
  2410. REQUIRE(!results.min(0));
  2411. REQUIRE(!results.min(1));
  2412. REQUIRE(!results.min(2));
  2413. REQUIRE(!results.min(3));
  2414. }
  2415. SECTION("average") {
  2416. REQUIRE(!results.average(0));
  2417. REQUIRE(!results.average(1));
  2418. REQUIRE(!results.average(2));
  2419. REQUIRE_THROWS_AS(results.average(3), Results::UnsupportedColumnTypeException);
  2420. }
  2421. SECTION("sum") {
  2422. REQUIRE(results.sum(0)->get_int() == 0);
  2423. REQUIRE(results.sum(1)->get_double() == 0.0);
  2424. REQUIRE(results.sum(2)->get_double() == 0.0);
  2425. REQUIRE_THROWS_AS(results.sum(3), Results::UnsupportedColumnTypeException);
  2426. }
  2427. }
  2428. }
  2429. TEST_CASE("results: set property value on all objects", "[batch_updates]") {
  2430. InMemoryTestFile config;
  2431. config.automatic_change_notifications = false;
  2432. config.cache = false;
  2433. config.schema = Schema{
  2434. {"AllTypes", {
  2435. {"pk", PropertyType::Int, Property::IsPrimary{true}},
  2436. {"bool", PropertyType::Bool},
  2437. {"int", PropertyType::Int},
  2438. {"float", PropertyType::Float},
  2439. {"double", PropertyType::Double},
  2440. {"string", PropertyType::String},
  2441. {"data", PropertyType::Data},
  2442. {"date", PropertyType::Date},
  2443. {"object", PropertyType::Object|PropertyType::Nullable, "AllTypes"},
  2444. {"list", PropertyType::Array|PropertyType::Object, "AllTypes"},
  2445. {"bool array", PropertyType::Array|PropertyType::Bool},
  2446. {"int array", PropertyType::Array|PropertyType::Int},
  2447. {"float array", PropertyType::Array|PropertyType::Float},
  2448. {"double array", PropertyType::Array|PropertyType::Double},
  2449. {"string array", PropertyType::Array|PropertyType::String},
  2450. {"data array", PropertyType::Array|PropertyType::Data},
  2451. {"date array", PropertyType::Array|PropertyType::Date},
  2452. {"object array", PropertyType::Array|PropertyType::Object, "AllTypes"},
  2453. }, {
  2454. {"parents", PropertyType::LinkingObjects|PropertyType::Array, "AllTypes", "object"},
  2455. }}
  2456. };
  2457. config.schema_version = 0;
  2458. auto realm = Realm::get_shared_realm(config);
  2459. auto table = realm->read_group().get_table("class_AllTypes");
  2460. realm->begin_transaction();
  2461. table->add_empty_row(2);
  2462. realm->commit_transaction();
  2463. Results r(realm, *table);
  2464. TestContext ctx(realm);
  2465. SECTION("non-existing property name") {
  2466. realm->begin_transaction();
  2467. REQUIRE_THROWS_AS(r.set_property_value(ctx, "i dont exist", util::Any(false)), Results::InvalidPropertyException);
  2468. realm->cancel_transaction();
  2469. }
  2470. SECTION("readonly property") {
  2471. realm->begin_transaction();
  2472. REQUIRE_THROWS_AS(r.set_property_value(ctx, "parents", util::Any(false)), ReadOnlyPropertyException);
  2473. realm->cancel_transaction();
  2474. }
  2475. SECTION("primarykey property") {
  2476. realm->begin_transaction();
  2477. REQUIRE_THROWS_AS(r.set_property_value(ctx, "pk", util::Any(1)), std::logic_error);
  2478. realm->cancel_transaction();
  2479. }
  2480. SECTION("set property values removes object from Results") {
  2481. realm->begin_transaction();
  2482. Results results(realm, table->where().equal(2,0));
  2483. CHECK(results.size() == 2);
  2484. r.set_property_value(ctx, "int", util::Any(INT64_C(42)));
  2485. CHECK(results.size() == 0);
  2486. realm->cancel_transaction();
  2487. }
  2488. SECTION("set property value") {
  2489. realm->begin_transaction();
  2490. r.set_property_value<util::Any>(ctx, "bool", util::Any(true));
  2491. for (size_t i = 0; i < r.size(); i++) {
  2492. CHECK(r.get(i).get_bool(1) == true);
  2493. }
  2494. r.set_property_value(ctx, "int", util::Any(INT64_C(42)));
  2495. for (size_t i = 0; i < r.size(); i++) {
  2496. CHECK(r.get(i).get_int(2) == 42);
  2497. }
  2498. r.set_property_value(ctx, "float", util::Any(1.23f));
  2499. for (size_t i = 0; i < r.size(); i++) {
  2500. CHECK(r.get(i).get_float(3) == 1.23f);
  2501. }
  2502. r.set_property_value(ctx, "double", util::Any(1.234));
  2503. for (size_t i = 0; i < r.size(); i++) {
  2504. CHECK(r.get(i).get_double(4) == 1.234);
  2505. }
  2506. r.set_property_value(ctx, "string", util::Any(std::string("abc")));
  2507. for (size_t i = 0; i < r.size(); i++) {
  2508. CHECK(r.get(i).get_string(5) == "abc");
  2509. }
  2510. r.set_property_value(ctx, "data", util::Any(std::string("abc")));
  2511. for (size_t i = 0; i < r.size(); i++) {
  2512. CHECK(r.get(i).get_binary(6) == BinaryData("abc", 3));
  2513. }
  2514. util::Any timestamp = Timestamp(1, 2);
  2515. r.set_property_value(ctx, "date", timestamp);
  2516. for (size_t i = 0; i < r.size(); i++) {
  2517. CHECK(r.get(i).get_timestamp(7) == any_cast<Timestamp>(timestamp));
  2518. }
  2519. size_t object_ndx = table->add_empty_row();
  2520. Object linked_obj(realm, "AllTypes", object_ndx);
  2521. r.set_property_value(ctx, "object", util::Any(linked_obj));
  2522. for (size_t i = 0; i < r.size(); i++) {
  2523. CHECK(r.get(i).get_link(8) == object_ndx);
  2524. }
  2525. size_t list_object_ndx = table->add_empty_row();
  2526. Object list_object(realm, "AllTypes", list_object_ndx);
  2527. r.set_property_value(ctx, "list", util::Any(AnyVector{list_object, list_object}));
  2528. for (size_t i = 0; i < r.size(); i++) {
  2529. auto list = r.get(i).get_linklist(9);
  2530. CHECK(list->size() == 2);
  2531. CHECK(list->get(0).get_index() == list_object_ndx);
  2532. CHECK(list->get(1).get_index() == list_object_ndx);
  2533. }
  2534. auto check_array = [&](size_t col, auto... values) {
  2535. size_t rows = r.size();
  2536. for (size_t i = 0; i < rows; ++i) {
  2537. RowExpr row = r.get(i);
  2538. auto table = row.get_subtable(col);
  2539. size_t j = 0;
  2540. for (auto& value : {values...}) {
  2541. CAPTURE(j);
  2542. REQUIRE(j < row.get_subtable_size(col));
  2543. REQUIRE(value == table->get<typename std::decay<decltype(value)>::type>(0, j));
  2544. ++j;
  2545. }
  2546. }
  2547. };
  2548. r.set_property_value(ctx, "bool array", util::Any(AnyVec{true, false}));
  2549. check_array(10, true, false);
  2550. r.set_property_value(ctx, "int array", util::Any(AnyVec{INT64_C(5), INT64_C(6)}));
  2551. check_array(11, INT64_C(5), INT64_C(6));
  2552. r.set_property_value(ctx, "float array", util::Any(AnyVec{1.1f, 2.2f}));
  2553. check_array(12, 1.1f, 2.2f);
  2554. r.set_property_value(ctx, "double array", util::Any(AnyVec{3.3, 4.4}));
  2555. check_array(13, 3.3, 4.4);
  2556. r.set_property_value(ctx, "string array", util::Any(AnyVec{"a"s, "b"s, "c"s}));
  2557. check_array(14, StringData("a"), StringData("b"), StringData("c"));
  2558. r.set_property_value(ctx, "data array", util::Any(AnyVec{"d"s, "e"s, "f"s}));
  2559. check_array(15, BinaryData("d",1), BinaryData("e",1), BinaryData("f",1));
  2560. r.set_property_value(ctx, "date array", util::Any(AnyVec{Timestamp(10,20), Timestamp(20,30), Timestamp(30,40)}));
  2561. check_array(16, Timestamp(10,20), Timestamp(20,30), Timestamp(30,40));
  2562. }
  2563. }
  2564. TEST_CASE("results: limit", "[limit]") {
  2565. InMemoryTestFile config;
  2566. config.cache = false;
  2567. config.automatic_change_notifications = false;
  2568. config.schema = Schema{
  2569. {"object", {
  2570. {"value", PropertyType::Int},
  2571. }},
  2572. };
  2573. auto realm = Realm::get_shared_realm(config);
  2574. auto table = realm->read_group().get_table("class_object");
  2575. realm->begin_transaction();
  2576. table->add_empty_row(8);
  2577. for (int i = 0; i < 8; ++i) {
  2578. table->set_int(0, i, (i + 2) % 4);
  2579. }
  2580. realm->commit_transaction();
  2581. Results r(realm, *table);
  2582. SECTION("unsorted") {
  2583. REQUIRE(r.limit(0).size() == 0);
  2584. REQUIRE_ORDER(r.limit(1), 0);
  2585. REQUIRE_ORDER(r.limit(2), 0, 1);
  2586. REQUIRE_ORDER(r.limit(8), 0, 1, 2, 3, 4, 5, 6, 7);
  2587. REQUIRE_ORDER(r.limit(100), 0, 1, 2, 3, 4, 5, 6, 7);
  2588. }
  2589. SECTION("sorted") {
  2590. auto sorted = r.sort({{"value", true}});
  2591. REQUIRE(sorted.limit(0).size() == 0);
  2592. REQUIRE_ORDER(sorted.limit(1), 2);
  2593. REQUIRE_ORDER(sorted.limit(2), 2, 6);
  2594. REQUIRE_ORDER(sorted.limit(8), 2, 6, 3, 7, 0, 4, 1, 5);
  2595. REQUIRE_ORDER(sorted.limit(100), 2, 6, 3, 7, 0, 4, 1, 5);
  2596. }
  2597. SECTION("sort after limit") {
  2598. REQUIRE(r.limit(0).sort({{"value", true}}).size() == 0);
  2599. REQUIRE_ORDER(r.limit(1).sort({{"value", true}}), 0);
  2600. REQUIRE_ORDER(r.limit(3).sort({{"value", true}}), 2, 0, 1);
  2601. REQUIRE_ORDER(r.limit(8).sort({{"value", true}}), 2, 6, 3, 7, 0, 4, 1, 5);
  2602. REQUIRE_ORDER(r.limit(100).sort({{"value", true}}), 2, 6, 3, 7, 0, 4, 1, 5);
  2603. }
  2604. SECTION("distinct") {
  2605. auto sorted = r.distinct({"value"});
  2606. REQUIRE(sorted.limit(0).size() == 0);
  2607. REQUIRE_ORDER(sorted.limit(1), 0);
  2608. REQUIRE_ORDER(sorted.limit(2), 0, 1);
  2609. REQUIRE_ORDER(sorted.limit(8), 0, 1, 2, 3);
  2610. sorted = r.sort({{"value", true}}).distinct({"value"});
  2611. REQUIRE(sorted.limit(0).size() == 0);
  2612. REQUIRE_ORDER(sorted.limit(1), 2);
  2613. REQUIRE_ORDER(sorted.limit(2), 2, 3);
  2614. REQUIRE_ORDER(sorted.limit(8), 2, 3, 0, 1);
  2615. }
  2616. SECTION("notifications on results using all descriptor types") {
  2617. r = r.distinct({"value"}).sort({{"value", false}}).limit(2);
  2618. int notification_calls = 0;
  2619. auto token = r.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  2620. REQUIRE_FALSE(err);
  2621. if (notification_calls == 0) {
  2622. REQUIRE(c.empty());
  2623. REQUIRE(r.size() == 2);
  2624. REQUIRE(r.get(0).get_int(0) == 3);
  2625. REQUIRE(r.get(1).get_int(0) == 2);
  2626. } else if (notification_calls == 1) {
  2627. REQUIRE(!c.empty());
  2628. REQUIRE_INDICES(c.insertions, 0);
  2629. REQUIRE_INDICES(c.deletions, 1);
  2630. REQUIRE(c.moves.size() == 0);
  2631. REQUIRE(c.modifications.count() == 0);
  2632. REQUIRE(r.size() == 2);
  2633. REQUIRE(r.get(0).get_int(0) == 5);
  2634. REQUIRE(r.get(1).get_int(0) == 3);
  2635. }
  2636. ++notification_calls;
  2637. });
  2638. advance_and_notify(*realm);
  2639. REQUIRE(notification_calls == 1);
  2640. realm->begin_transaction();
  2641. table->add_empty_row(1);
  2642. table->set_int(0, 8, 5);
  2643. realm->commit_transaction();
  2644. advance_and_notify(*realm);
  2645. REQUIRE(notification_calls == 2);
  2646. }
  2647. SECTION("notifications on only limited results") {
  2648. r = r.limit(2);
  2649. int notification_calls = 0;
  2650. auto token = r.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr err) {
  2651. REQUIRE_FALSE(err);
  2652. if (notification_calls == 0) {
  2653. REQUIRE(c.empty());
  2654. REQUIRE(r.size() == 2);
  2655. } else if (notification_calls == 1) {
  2656. REQUIRE(!c.empty());
  2657. REQUIRE(c.insertions.count() == 0);
  2658. REQUIRE(c.deletions.count() == 0);
  2659. REQUIRE(c.modifications.count() == 1);
  2660. REQUIRE_INDICES(c.modifications, 1);
  2661. REQUIRE(r.size() == 2);
  2662. }
  2663. ++notification_calls;
  2664. });
  2665. advance_and_notify(*realm);
  2666. REQUIRE(notification_calls == 1);
  2667. realm->begin_transaction();
  2668. table->set_int(0, 1, 5);
  2669. realm->commit_transaction();
  2670. advance_and_notify(*realm);
  2671. REQUIRE(notification_calls == 2);
  2672. }
  2673. SECTION("does not support further filtering") {
  2674. auto limited = r.limit(0);
  2675. REQUIRE_THROWS_AS(limited.filter(table->where()), Results::UnimplementedOperationException);
  2676. }
  2677. }