Changeset View
Changeset View
Standalone View
Standalone View
tests/units/akonadi/akonadilivequeryintegratortest.cpp
Show All 31 Lines | |||||
32 | #include "akonadi/akonaditagfetchjobinterface.h" | 32 | #include "akonadi/akonaditagfetchjobinterface.h" | ||
33 | 33 | | |||
34 | #include "akonadi/akonadilivequeryintegrator.h" | 34 | #include "akonadi/akonadilivequeryintegrator.h" | ||
35 | #include "akonadi/akonadiserializer.h" | 35 | #include "akonadi/akonadiserializer.h" | ||
36 | #include "akonadi/akonadistorage.h" | 36 | #include "akonadi/akonadistorage.h" | ||
37 | 37 | | |||
38 | #include "testlib/akonadifakedata.h" | 38 | #include "testlib/akonadifakedata.h" | ||
39 | #include "testlib/gencollection.h" | 39 | #include "testlib/gencollection.h" | ||
40 | #include "testlib/gennote.h" | | |||
41 | #include "testlib/gentag.h" | 40 | #include "testlib/gentag.h" | ||
42 | #include "testlib/gentodo.h" | 41 | #include "testlib/gentodo.h" | ||
43 | #include "testlib/testhelpers.h" | 42 | #include "testlib/testhelpers.h" | ||
44 | 43 | | |||
45 | #include "utils/jobhandler.h" | 44 | #include "utils/jobhandler.h" | ||
46 | 45 | | |||
47 | using namespace Testlib; | 46 | using namespace Testlib; | ||
48 | 47 | | |||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Line(s) | 135 | { | |||
137 | // GIVEN | 136 | // GIVEN | ||
138 | AkonadiFakeData data; | 137 | AkonadiFakeData data; | ||
139 | 138 | | |||
140 | // One top level collection | 139 | // One top level collection | ||
141 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | 140 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | ||
142 | 141 | | |||
143 | // Three artifacts in the collection, one not matching the predicate | 142 | // Three artifacts in the collection, one not matching the predicate | ||
144 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | 143 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | ||
145 | data.createItem(GenNote().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | 144 | data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | ||
146 | data.createItem(GenNote().withId(44).withParent(42).withTitle(QStringLiteral("44-ex"))); | 145 | data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44-ex"))); | ||
147 | 146 | | |||
148 | // Couple of projects in the collection which should not appear or create trouble | 147 | // Couple of projects in the collection which should not appear or create trouble | ||
149 | data.createItem(GenTodo().withId(40).withParent(42).asProject().withTitle(QStringLiteral("40"))); | 148 | data.createItem(GenTodo().withId(40).withParent(42).asProject().withTitle(QStringLiteral("40"))); | ||
150 | data.createItem(GenTodo().withId(41).withParent(42).asProject().withTitle(QStringLiteral("41-in"))); | 149 | data.createItem(GenTodo().withId(41).withParent(42).asProject().withTitle(QStringLiteral("41-in"))); | ||
151 | 150 | | |||
152 | auto integrator = createIntegrator(data); | 151 | auto integrator = createIntegrator(data); | ||
153 | auto storage = createStorage(data); | 152 | auto storage = createStorage(data); | ||
154 | 153 | | |||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | |||||
199 | 198 | | |||
200 | // THEN | 199 | // THEN | ||
201 | QCOMPARE(result->data().size(), 2); | 200 | QCOMPARE(result->data().size(), 2); | ||
202 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | 201 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | ||
203 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | 202 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | ||
204 | 203 | | |||
205 | // Reacts to change (which adds) | 204 | // Reacts to change (which adds) | ||
206 | // WHEN | 205 | // WHEN | ||
207 | data.modifyItem(GenNote(data.item(44)).withTitle(QStringLiteral("44-in"))); | 206 | data.modifyItem(GenTodo(data.item(44)).withTitle(QStringLiteral("44-in"))); | ||
208 | 207 | | |||
209 | // THEN | 208 | // THEN | ||
210 | QCOMPARE(result->data().size(), 3); | 209 | QCOMPARE(result->data().size(), 3); | ||
211 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | 210 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | ||
212 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | 211 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | ||
213 | QCOMPARE(result->data().at(2)->title(), QStringLiteral("44-in")); | 212 | QCOMPARE(result->data().at(2)->title(), QStringLiteral("44-in")); | ||
214 | 213 | | |||
215 | // Reacts to change (which removes) | 214 | // Reacts to change (which removes) | ||
216 | // WHEN | 215 | // WHEN | ||
217 | data.modifyItem(GenNote(data.item(44)).withTitle(QStringLiteral("44-ex"))); | 216 | data.modifyItem(GenTodo(data.item(44)).withTitle(QStringLiteral("44-ex"))); | ||
218 | 217 | | |||
219 | // THEN | 218 | // THEN | ||
220 | QCOMPARE(result->data().size(), 2); | 219 | QCOMPARE(result->data().size(), 2); | ||
221 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | 220 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | ||
222 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | 221 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | ||
223 | 222 | | |||
224 | // Don't keep a reference on any result | 223 | // Don't keep a reference on any result | ||
225 | result.clear(); | 224 | result.clear(); | ||
Show All 20 Lines | |||||
246 | void shouldMoveArtifactsBetweenQueries() | 245 | void shouldMoveArtifactsBetweenQueries() | ||
247 | { | 246 | { | ||
248 | // GIVEN | 247 | // GIVEN | ||
249 | AkonadiFakeData data; | 248 | AkonadiFakeData data; | ||
250 | 249 | | |||
251 | // One top level collection | 250 | // One top level collection | ||
252 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | 251 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | ||
253 | 252 | | |||
254 | // One task and one note which show in one query and not the other | 253 | // Two tasks: one which shows in one query and not the other | ||
255 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | 254 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | ||
256 | data.createItem(GenNote().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | 255 | data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | ||
257 | 256 | | |||
258 | // Couple of projects in the collection which should not appear or create trouble | 257 | // Couple of projects in the collection which should not appear or create trouble | ||
259 | data.createItem(GenTodo().withId(39).withParent(42).asProject().withTitle(QStringLiteral("39"))); | 258 | data.createItem(GenTodo().withId(39).withParent(42).asProject().withTitle(QStringLiteral("39"))); | ||
260 | data.createItem(GenTodo().withId(40).withParent(42).asProject().withTitle(QStringLiteral("40-ex"))); | 259 | data.createItem(GenTodo().withId(40).withParent(42).asProject().withTitle(QStringLiteral("40-ex"))); | ||
261 | data.createItem(GenTodo().withId(41).withParent(42).asProject().withTitle(QStringLiteral("41-in"))); | 260 | data.createItem(GenTodo().withId(41).withParent(42).asProject().withTitle(QStringLiteral("41-in"))); | ||
262 | 261 | | |||
263 | auto integrator = createIntegrator(data); | 262 | auto integrator = createIntegrator(data); | ||
264 | auto storage = createStorage(data); | 263 | auto storage = createStorage(data); | ||
Show All 16 Lines | |||||
281 | 280 | | |||
282 | TestHelpers::waitForEmptyJobQueue(); | 281 | TestHelpers::waitForEmptyJobQueue(); | ||
283 | 282 | | |||
284 | QCOMPARE(inResult->data().size(), 2); | 283 | QCOMPARE(inResult->data().size(), 2); | ||
285 | QCOMPARE(exResult->data().size(), 0); | 284 | QCOMPARE(exResult->data().size(), 0); | ||
286 | 285 | | |||
287 | // WHEN | 286 | // WHEN | ||
288 | data.modifyItem(GenTodo(data.item(42)).withTitle(QStringLiteral("42-ex"))); | 287 | data.modifyItem(GenTodo(data.item(42)).withTitle(QStringLiteral("42-ex"))); | ||
289 | data.modifyItem(GenNote(data.item(43)).withTitle(QStringLiteral("43-ex"))); | 288 | data.modifyItem(GenTodo(data.item(43)).withTitle(QStringLiteral("43-ex"))); | ||
290 | 289 | | |||
291 | // THEN | 290 | // THEN | ||
292 | QCOMPARE(inResult->data().size(), 0); | 291 | QCOMPARE(inResult->data().size(), 0); | ||
293 | QCOMPARE(exResult->data().size(), 2); | 292 | QCOMPARE(exResult->data().size(), 2); | ||
294 | } | 293 | } | ||
295 | 294 | | |||
296 | void shouldReactToCollectionSelectionChangesForArtifactQueries() | 295 | void shouldReactToCollectionSelectionChangesForArtifactQueries() | ||
297 | { | 296 | { | ||
▲ Show 20 Lines • Show All 351 Lines • ▼ Show 20 Line(s) | 614 | { | |||
649 | // THEN | 648 | // THEN | ||
650 | QCOMPARE(inResult->data().size(), 0); | 649 | QCOMPARE(inResult->data().size(), 0); | ||
651 | QCOMPARE(exResult->data().size(), 1); | 650 | QCOMPARE(exResult->data().size(), 1); | ||
652 | } | 651 | } | ||
653 | 652 | | |||
654 | 653 | | |||
655 | 654 | | |||
656 | 655 | | |||
657 | void shouldBindNoteQueries() | | |||
658 | { | | |||
659 | // GIVEN | | |||
660 | AkonadiFakeData data; | | |||
661 | | ||||
662 | // One top level collection | | |||
663 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | | |||
664 | | ||||
665 | // Three notes in the collection, one not matching the predicate | | |||
666 | data.createItem(GenNote().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | | |||
667 | data.createItem(GenNote().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | | |||
668 | data.createItem(GenNote().withId(44).withParent(42).withTitle(QStringLiteral("44-ex"))); | | |||
669 | | ||||
670 | // Couple of tasks in the collection which should not appear or create trouble | | |||
671 | data.createItem(GenTodo().withId(40).withParent(42).withTitle(QStringLiteral("40"))); | | |||
672 | data.createItem(GenTodo().withId(41).withParent(42).withTitle(QStringLiteral("41-in"))); | | |||
673 | | ||||
674 | auto integrator = createIntegrator(data); | | |||
675 | auto storage = createStorage(data); | | |||
676 | | ||||
677 | auto query = Domain::LiveQueryOutput<Domain::Note::Ptr>::Ptr(); | | |||
678 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | | |||
679 | auto predicate = [] (const Akonadi::Item &item) { | | |||
680 | return titleFromItem(item).endsWith(QLatin1String("-in")); | | |||
681 | }; | | |||
682 | | ||||
683 | // Initial listing | | |||
684 | // WHEN | | |||
685 | integrator->bind("note1", query, fetch, predicate); | | |||
686 | auto result = query->result(); | | |||
687 | result->data(); | | |||
688 | integrator->bind("note2", query, fetch, predicate); | | |||
689 | result = query->result(); // Should not cause any problem or wrong data | | |||
690 | | ||||
691 | // THEN | | |||
692 | QVERIFY(result->data().isEmpty()); | | |||
693 | TestHelpers::waitForEmptyJobQueue(); | | |||
694 | | ||||
695 | QCOMPARE(result->data().size(), 2); | | |||
696 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-in")); | | |||
697 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
698 | | ||||
699 | // Reacts to add | | |||
700 | // WHEN | | |||
701 | data.createItem(GenNote().withId(45).withParent(42).withTitle(QStringLiteral("45-in"))); | | |||
702 | | ||||
703 | // THEN | | |||
704 | QCOMPARE(result->data().size(), 3); | | |||
705 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-in")); | | |||
706 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
707 | QCOMPARE(result->data().at(2)->title(), QStringLiteral("45-in")); | | |||
708 | | ||||
709 | // Reacts to remove | | |||
710 | // WHEN | | |||
711 | data.removeItem(Akonadi::Item(45)); | | |||
712 | | ||||
713 | // THEN | | |||
714 | QCOMPARE(result->data().size(), 2); | | |||
715 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-in")); | | |||
716 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
717 | | ||||
718 | // Reacts to change | | |||
719 | // WHEN | | |||
720 | data.modifyItem(GenNote(data.item(42)).withTitle(QStringLiteral("42-bis-in"))); | | |||
721 | | ||||
722 | // THEN | | |||
723 | QCOMPARE(result->data().size(), 2); | | |||
724 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | | |||
725 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
726 | | ||||
727 | // Reacts to change (which adds) | | |||
728 | // WHEN | | |||
729 | data.modifyItem(GenNote(data.item(44)).withTitle(QStringLiteral("44-in"))); | | |||
730 | | ||||
731 | // THEN | | |||
732 | QCOMPARE(result->data().size(), 3); | | |||
733 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | | |||
734 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
735 | QCOMPARE(result->data().at(2)->title(), QStringLiteral("44-in")); | | |||
736 | | ||||
737 | // Reacts to change (which removes) | | |||
738 | // WHEN | | |||
739 | data.modifyItem(GenNote(data.item(44)).withTitle(QStringLiteral("44-ex"))); | | |||
740 | | ||||
741 | // THEN | | |||
742 | QCOMPARE(result->data().size(), 2); | | |||
743 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42-bis-in")); | | |||
744 | QCOMPARE(result->data().at(1)->title(), QStringLiteral("43-in")); | | |||
745 | | ||||
746 | // Don't keep a reference on any result | | |||
747 | result.clear(); | | |||
748 | | ||||
749 | // The bug we're trying to hit here is the following: | | |||
750 | // - when bind is called the first time a provider is created internally | | |||
751 | // - result is deleted at the end of the loop, no one holds the provider with | | |||
752 | // a strong reference anymore so it is deleted as well | | |||
753 | // - when bind is called the second time, there's a risk of a dangling | | |||
754 | // pointer if the recycling of providers is wrongly implemented which can lead | | |||
755 | // to a crash, if it is properly done no crash will occur | | |||
756 | for (int i = 0; i < 2; i++) { | | |||
757 | // WHEN * 2 | | |||
758 | integrator->bind("noteN", query, fetch, predicate); | | |||
759 | auto result = query->result(); | | |||
760 | | ||||
761 | // THEN * 2 | | |||
762 | QVERIFY(result->data().isEmpty()); | | |||
763 | TestHelpers::waitForEmptyJobQueue(); | | |||
764 | QVERIFY(!result->data().isEmpty()); | | |||
765 | } | | |||
766 | } | | |||
767 | | ||||
768 | void shouldMoveNotesBetweenQueries() | | |||
769 | { | | |||
770 | // GIVEN | | |||
771 | AkonadiFakeData data; | | |||
772 | | ||||
773 | // One top level collection | | |||
774 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | | |||
775 | | ||||
776 | // One note which shows in one query and not the other | | |||
777 | data.createItem(GenNote().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | | |||
778 | | ||||
779 | // Couple of tasks in the collection which should not appear or create trouble | | |||
780 | data.createItem(GenTodo().withId(39).withParent(42).withTitle(QStringLiteral("39"))); | | |||
781 | data.createItem(GenTodo().withId(40).withParent(42).withTitle(QStringLiteral("40-ex"))); | | |||
782 | data.createItem(GenTodo().withId(41).withParent(42).withTitle(QStringLiteral("41-in"))); | | |||
783 | | ||||
784 | auto integrator = createIntegrator(data); | | |||
785 | auto storage = createStorage(data); | | |||
786 | | ||||
787 | auto inQuery = Domain::LiveQueryOutput<Domain::Note::Ptr>::Ptr(); | | |||
788 | auto exQuery = Domain::LiveQueryOutput<Domain::Note::Ptr>::Ptr(); | | |||
789 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | | |||
790 | auto inPredicate = [] (const Akonadi::Item &item) { | | |||
791 | return titleFromItem(item).endsWith(QLatin1String("-in")); | | |||
792 | }; | | |||
793 | auto exPredicate = [] (const Akonadi::Item &item) { | | |||
794 | return titleFromItem(item).endsWith(QLatin1String("-ex")); | | |||
795 | }; | | |||
796 | | ||||
797 | integrator->bind("note-in", inQuery, fetch, inPredicate); | | |||
798 | auto inResult = inQuery->result(); | | |||
799 | | ||||
800 | integrator->bind("note-ex", exQuery, fetch, exPredicate); | | |||
801 | auto exResult = exQuery->result(); | | |||
802 | | ||||
803 | TestHelpers::waitForEmptyJobQueue(); | | |||
804 | | ||||
805 | QCOMPARE(inResult->data().size(), 1); | | |||
806 | QCOMPARE(exResult->data().size(), 0); | | |||
807 | | ||||
808 | // WHEN | | |||
809 | data.modifyItem(GenNote(data.item(42)).withTitle(QStringLiteral("42-ex"))); | | |||
810 | | ||||
811 | // THEN | | |||
812 | QCOMPARE(inResult->data().size(), 0); | | |||
813 | QCOMPARE(exResult->data().size(), 1); | | |||
814 | } | | |||
815 | | ||||
816 | void shouldReactToCollectionSelectionChangesForNoteQueries() | | |||
817 | { | | |||
818 | // GIVEN | | |||
819 | AkonadiFakeData data; | | |||
820 | | ||||
821 | // Two top level collections | | |||
822 | data.createCollection(GenCollection().withId(42).withRootAsParent().withNoteContent()); | | |||
823 | data.createCollection(GenCollection().withId(43).withRootAsParent().withNoteContent()); | | |||
824 | | ||||
825 | // One note in each collection | | |||
826 | data.createItem(GenNote().withId(42).withParent(42).withTitle(QStringLiteral("42"))); | | |||
827 | data.createItem(GenNote().withId(43).withParent(43).withTitle(QStringLiteral("43"))); | | |||
828 | | ||||
829 | // Couple of tasks in the collections which should not appear or create trouble | | |||
830 | data.createItem(GenTodo().withId(40).withParent(42).withTitle(QStringLiteral("40"))); | | |||
831 | data.createItem(GenTodo().withId(41).withParent(43).withTitle(QStringLiteral("41"))); | | |||
832 | | ||||
833 | auto integrator = createIntegrator(data); | | |||
834 | auto storage = createStorage(data); | | |||
835 | auto serializer = createSerializer(); | | |||
836 | | ||||
837 | auto query = Domain::LiveQueryOutput<Domain::Note::Ptr>::Ptr(); | | |||
838 | auto fetch = fetchItemsInSelectedCollectionsFunction(storage, serializer); | | |||
839 | auto predicate = [] (const Akonadi::Item &) { | | |||
840 | return true; | | |||
841 | }; | | |||
842 | | ||||
843 | integrator->bind("note query", query, fetch, predicate); | | |||
844 | auto result = query->result(); | | |||
845 | TestHelpers::waitForEmptyJobQueue(); | | |||
846 | QCOMPARE(result->data().size(), 2); | | |||
847 | | ||||
848 | // WHEN | | |||
849 | data.modifyCollection(GenCollection(data.collection(43)).selected(false)); | | |||
850 | TestHelpers::waitForEmptyJobQueue(); | | |||
851 | | ||||
852 | // THEN | | |||
853 | QCOMPARE(result->data().size(), 1); | | |||
854 | QCOMPARE(result->data().at(0)->title(), QStringLiteral("42")); | | |||
855 | } | | |||
856 | | ||||
857 | | ||||
858 | | ||||
859 | | ||||
860 | void shouldBindProjectQueries() | 656 | void shouldBindProjectQueries() | ||
861 | { | 657 | { | ||
862 | // GIVEN | 658 | // GIVEN | ||
863 | AkonadiFakeData data; | 659 | AkonadiFakeData data; | ||
864 | 660 | | |||
865 | // One top level collection | 661 | // One top level collection | ||
866 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | 662 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | ||
867 | 663 | | |||
▲ Show 20 Lines • Show All 185 Lines • ▼ Show 20 Line(s) | 816 | { | |||
1053 | TestHelpers::waitForEmptyJobQueue(); | 849 | TestHelpers::waitForEmptyJobQueue(); | ||
1054 | 850 | | |||
1055 | // THEN | 851 | // THEN | ||
1056 | QCOMPARE(result->data().size(), 1); | 852 | QCOMPARE(result->data().size(), 1); | ||
1057 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); | 853 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); | ||
1058 | } | 854 | } | ||
1059 | 855 | | |||
1060 | 856 | | |||
1061 | void shouldBindTagQueries() | | |||
1062 | { | | |||
1063 | // GIVEN | | |||
1064 | AkonadiFakeData data; | | |||
1065 | | ||||
1066 | // Three plain tags, one not matching the predicate | | |||
1067 | data.createTag(GenTag().withId(42).asPlain().withName(QStringLiteral("42-in"))); | | |||
1068 | data.createTag(GenTag().withId(43).asPlain().withName(QStringLiteral("43-in"))); | | |||
1069 | data.createTag(GenTag().withId(44).asPlain().withName(QStringLiteral("44-ex"))); | | |||
1070 | | ||||
1071 | // Couple of context tags which should not appear or create trouble | | |||
1072 | data.createTag(GenTag().withId(40).asContext().withName(QStringLiteral("40"))); | | |||
1073 | data.createTag(GenTag().withId(41).asContext().withName(QStringLiteral("41-in"))); | | |||
1074 | | ||||
1075 | auto integrator = createIntegrator(data); | | |||
1076 | auto storage = createStorage(data); | | |||
1077 | | ||||
1078 | auto query = Domain::LiveQueryOutput<Domain::Tag::Ptr>::Ptr(); | | |||
1079 | auto fetch = [storage] (const Domain::LiveQueryInput<Akonadi::Tag>::AddFunction &add) { | | |||
1080 | auto job = storage->fetchTags(); | | |||
1081 | Utils::JobHandler::install(job->kjob(), [add, job] { | | |||
1082 | foreach (const auto &tag, job->tags()) { | | |||
1083 | add(tag); | | |||
1084 | } | | |||
1085 | }); | | |||
1086 | }; | | |||
1087 | auto predicate = [] (const Akonadi::Tag &tag) { | | |||
1088 | return tag.name().endsWith(QLatin1String("-in")); | | |||
1089 | }; | | |||
1090 | | ||||
1091 | // Initial listing | | |||
1092 | // WHEN | | |||
1093 | integrator->bind("tag1", query, fetch, predicate); | | |||
1094 | auto result = query->result(); | | |||
1095 | result->data(); | | |||
1096 | integrator->bind("tag2", query, fetch, predicate); | | |||
1097 | result = query->result(); // Should not cause any problem or wrong data | | |||
1098 | | ||||
1099 | // THEN | | |||
1100 | QVERIFY(result->data().isEmpty()); | | |||
1101 | TestHelpers::waitForEmptyJobQueue(); | | |||
1102 | | ||||
1103 | QCOMPARE(result->data().size(), 2); | | |||
1104 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-in")); | | |||
1105 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1106 | | ||||
1107 | // Reacts to add | | |||
1108 | // WHEN | | |||
1109 | data.createTag(GenTag().withId(45).asPlain().withName(QStringLiteral("45-in"))); | | |||
1110 | | ||||
1111 | // THEN | | |||
1112 | QCOMPARE(result->data().size(), 3); | | |||
1113 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-in")); | | |||
1114 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1115 | QCOMPARE(result->data().at(2)->name(), QStringLiteral("45-in")); | | |||
1116 | | ||||
1117 | // Reacts to remove | | |||
1118 | // WHEN | | |||
1119 | data.removeTag(Akonadi::Tag(45)); | | |||
1120 | | ||||
1121 | // THEN | | |||
1122 | QCOMPARE(result->data().size(), 2); | | |||
1123 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-in")); | | |||
1124 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1125 | | ||||
1126 | // Reacts to change | | |||
1127 | // WHEN | | |||
1128 | data.modifyTag(GenTag(data.tag(42)).withName(QStringLiteral("42-bis-in"))); | | |||
1129 | | ||||
1130 | // THEN | | |||
1131 | QCOMPARE(result->data().size(), 2); | | |||
1132 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-bis-in")); | | |||
1133 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1134 | | ||||
1135 | // Reacts to change (which adds) | | |||
1136 | // WHEN | | |||
1137 | data.modifyTag(GenTag(data.tag(44)).withName(QStringLiteral("44-in"))); | | |||
1138 | | ||||
1139 | // THEN | | |||
1140 | QCOMPARE(result->data().size(), 3); | | |||
1141 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-bis-in")); | | |||
1142 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1143 | QCOMPARE(result->data().at(2)->name(), QStringLiteral("44-in")); | | |||
1144 | | ||||
1145 | // Reacts to change (which removes) | | |||
1146 | // WHEN | | |||
1147 | data.modifyTag(GenTag(data.tag(44)).withName(QStringLiteral("44-ex"))); | | |||
1148 | | ||||
1149 | // THEN | | |||
1150 | QCOMPARE(result->data().size(), 2); | | |||
1151 | QCOMPARE(result->data().at(0)->name(), QStringLiteral("42-bis-in")); | | |||
1152 | QCOMPARE(result->data().at(1)->name(), QStringLiteral("43-in")); | | |||
1153 | | ||||
1154 | // Don't keep a reference on any result | | |||
1155 | result.clear(); | | |||
1156 | | ||||
1157 | // The bug we're trying to hit here is the following: | | |||
1158 | // - when bind is called the first time a provider is created internally | | |||
1159 | // - result is deleted at the end of the loop, no one holds the provider with | | |||
1160 | // a strong reference anymore so it is deleted as well | | |||
1161 | // - when bind is called the second time, there's a risk of a dangling | | |||
1162 | // pointer if the recycling of providers is wrongly implemented which can lead | | |||
1163 | // to a crash, if it is properly done no crash will occur | | |||
1164 | for (int i = 0; i < 2; i++) { | | |||
1165 | // WHEN * 2 | | |||
1166 | integrator->bind("tagN", query, fetch, predicate); | | |||
1167 | auto result = query->result(); | | |||
1168 | | ||||
1169 | // THEN * 2 | | |||
1170 | QVERIFY(result->data().isEmpty()); | | |||
1171 | TestHelpers::waitForEmptyJobQueue(); | | |||
1172 | QVERIFY(!result->data().isEmpty()); | | |||
1173 | } | | |||
1174 | } | | |||
1175 | | ||||
1176 | void shouldMoveTagBetweenQueries() | | |||
1177 | { | | |||
1178 | // GIVEN | | |||
1179 | AkonadiFakeData data; | | |||
1180 | | ||||
1181 | // One plain tag which shows in one query not the other | | |||
1182 | data.createTag(GenTag().withId(42).asPlain().withName(QStringLiteral("42-in"))); | | |||
1183 | | ||||
1184 | // Couple of context tags which should not appear or create trouble | | |||
1185 | data.createTag(GenTag().withId(39).asContext().withName(QStringLiteral("39"))); | | |||
1186 | data.createTag(GenTag().withId(40).asContext().withName(QStringLiteral("40-ex"))); | | |||
1187 | data.createTag(GenTag().withId(41).asContext().withName(QStringLiteral("41-in"))); | | |||
1188 | | ||||
1189 | auto integrator = createIntegrator(data); | | |||
1190 | auto storage = createStorage(data); | | |||
1191 | | ||||
1192 | auto inQuery = Domain::LiveQueryOutput<Domain::Tag::Ptr>::Ptr(); | | |||
1193 | auto exQuery = Domain::LiveQueryOutput<Domain::Tag::Ptr>::Ptr(); | | |||
1194 | auto fetch = [storage] (const Domain::LiveQueryInput<Akonadi::Tag>::AddFunction &add) { | | |||
1195 | auto job = storage->fetchTags(); | | |||
1196 | Utils::JobHandler::install(job->kjob(), [add, job] { | | |||
1197 | foreach (const auto &tag, job->tags()) { | | |||
1198 | add(tag); | | |||
1199 | } | | |||
1200 | }); | | |||
1201 | }; | | |||
1202 | auto inPredicate = [] (const Akonadi::Tag &tag) { | | |||
1203 | return tag.name().endsWith(QLatin1String("-in")); | | |||
1204 | }; | | |||
1205 | auto exPredicate = [] (const Akonadi::Tag &tag) { | | |||
1206 | return tag.name().endsWith(QLatin1String("-ex")); | | |||
1207 | }; | | |||
1208 | | ||||
1209 | integrator->bind("tag-in", inQuery, fetch, inPredicate); | | |||
1210 | auto inResult = inQuery->result(); | | |||
1211 | | ||||
1212 | integrator->bind("tag-ex", exQuery, fetch, exPredicate); | | |||
1213 | auto exResult = exQuery->result(); | | |||
1214 | | ||||
1215 | TestHelpers::waitForEmptyJobQueue(); | | |||
1216 | | ||||
1217 | QCOMPARE(inResult->data().size(), 1); | | |||
1218 | QCOMPARE(exResult->data().size(), 0); | | |||
1219 | | ||||
1220 | // WHEN | | |||
1221 | data.modifyTag(GenTag(data.tag(42)).withName(QStringLiteral("42-ex"))); | | |||
1222 | | ||||
1223 | // THEN | | |||
1224 | QCOMPARE(inResult->data().size(), 0); | | |||
1225 | QCOMPARE(exResult->data().size(), 1); | | |||
1226 | } | | |||
1227 | | ||||
1228 | 857 | | |||
1229 | 858 | | |||
1230 | 859 | | |||
1231 | void shouldBindTaskQueries() | 860 | void shouldBindTaskQueries() | ||
1232 | { | 861 | { | ||
1233 | // GIVEN | 862 | // GIVEN | ||
1234 | AkonadiFakeData data; | 863 | AkonadiFakeData data; | ||
1235 | 864 | | |||
1236 | // One top level collection | 865 | // One top level collection | ||
1237 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | 866 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | ||
1238 | 867 | | |||
1239 | // Three tasks in the collection, one not matching the predicate | 868 | // Three tasks in the collection, one not matching the predicate | ||
1240 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | 869 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | ||
1241 | data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | 870 | data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43-in"))); | ||
1242 | data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44-ex"))); | 871 | data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44-ex"))); | ||
1243 | 872 | | |||
1244 | // Couple of notes and projects in the collection which should not appear or create trouble | 873 | // Couple of projects in the collection which should not appear or create trouble | ||
1245 | data.createItem(GenTodo().withId(38).withParent(42).asProject().withTitle(QStringLiteral("38"))); | 874 | data.createItem(GenTodo().withId(38).withParent(42).asProject().withTitle(QStringLiteral("38"))); | ||
1246 | data.createItem(GenTodo().withId(39).withParent(42).asProject().withTitle(QStringLiteral("39-in"))); | 875 | data.createItem(GenTodo().withId(39).withParent(42).asProject().withTitle(QStringLiteral("39-in"))); | ||
1247 | data.createItem(GenNote().withId(40).withParent(42).withTitle(QStringLiteral("40"))); | | |||
1248 | data.createItem(GenNote().withId(41).withParent(42).withTitle(QStringLiteral("41-in"))); | | |||
1249 | 876 | | |||
1250 | auto integrator = createIntegrator(data); | 877 | auto integrator = createIntegrator(data); | ||
1251 | auto storage = createStorage(data); | 878 | auto storage = createStorage(data); | ||
1252 | 879 | | |||
1253 | auto query = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | 880 | auto query = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | ||
1254 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | 881 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | ||
1255 | auto predicate = [] (const Akonadi::Item &item) { | 882 | auto predicate = [] (const Akonadi::Item &item) { | ||
1256 | return titleFromItem(item).endsWith(QLatin1String("-in")); | 883 | return titleFromItem(item).endsWith(QLatin1String("-in")); | ||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Line(s) | 972 | { | |||
1347 | AkonadiFakeData data; | 974 | AkonadiFakeData data; | ||
1348 | 975 | | |||
1349 | // One top level collection | 976 | // One top level collection | ||
1350 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | 977 | data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); | ||
1351 | 978 | | |||
1352 | // One task which shows in one query and not the other | 979 | // One task which shows in one query and not the other | ||
1353 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | 980 | data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42-in"))); | ||
1354 | 981 | | |||
1355 | // Couple of notes in the collection which should not appear or create trouble | | |||
1356 | data.createItem(GenNote().withId(39).withParent(42).withTitle(QStringLiteral("39"))); | | |||
1357 | data.createItem(GenNote().withId(40).withParent(42).withTitle(QStringLiteral("40-ex"))); | | |||
1358 | data.createItem(GenNote().withId(41).withParent(42).withTitle(QStringLiteral("41-in"))); | | |||
1359 | | ||||
1360 | auto integrator = createIntegrator(data); | 982 | auto integrator = createIntegrator(data); | ||
1361 | auto storage = createStorage(data); | 983 | auto storage = createStorage(data); | ||
1362 | 984 | | |||
1363 | auto inQuery = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | 985 | auto inQuery = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | ||
1364 | auto exQuery = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | 986 | auto exQuery = Domain::LiveQueryOutput<Domain::Task::Ptr>::Ptr(); | ||
1365 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | 987 | auto fetch = fetchItemsInAllCollectionsFunction(storage); | ||
1366 | auto inPredicate = [] (const Akonadi::Item &item) { | 988 | auto inPredicate = [] (const Akonadi::Item &item) { | ||
1367 | return titleFromItem(item).endsWith(QLatin1String("-in")); | 989 | return titleFromItem(item).endsWith(QLatin1String("-in")); | ||
▲ Show 20 Lines • Show All 138 Lines • Show Last 20 Lines |