File indexing completed on 2024-12-22 04:10:25

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_strokes_queue_test.h"
0008 #include <simpletest.h>
0009 
0010 #include "kistest.h"
0011 
0012 #include "scheduler_utils.h"
0013 #include "kis_strokes_queue.h"
0014 #include "kis_updater_context.h"
0015 #include "kis_update_job_item.h"
0016 #include "kis_merge_walker.h"
0017 
0018 
0019 void KisStrokesQueueTest::testSequentialJobs()
0020 {
0021     KisStrokesQueue queue;
0022     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("tri_"), false));
0023     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0024     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0025     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0026     queue.endStroke(id);
0027 
0028     KisTestableUpdaterContext context(2);
0029     QVector<KisUpdateJobItem*> jobs;
0030 
0031     queue.processQueue(context, false);
0032 
0033     jobs = context.getJobs();
0034     COMPARE_NAME(jobs[0], "tri_init");
0035     VERIFY_EMPTY(jobs[1]);
0036 
0037     context.clear();
0038     queue.processQueue(context, false);
0039 
0040     jobs = context.getJobs();
0041     COMPARE_NAME(jobs[0], "tri_dab");
0042     COMPARE_NAME(jobs[1], "tri_dab");
0043 
0044     context.clear();
0045     queue.processQueue(context, false);
0046 
0047     jobs = context.getJobs();
0048     COMPARE_NAME(jobs[0], "tri_dab");
0049     VERIFY_EMPTY(jobs[1]);
0050 
0051     context.clear();
0052     queue.processQueue(context, false);
0053 
0054     jobs = context.getJobs();
0055     COMPARE_NAME(jobs[0], "tri_finish");
0056     VERIFY_EMPTY(jobs[1]);
0057 }
0058 
0059 void KisStrokesQueueTest::testConcurrentSequentialBarrier()
0060 {
0061     KisStrokesQueue queue;
0062     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("tri_"), false));
0063     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0064     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0065     queue.endStroke(id);
0066 
0067     // make the number of threads higher
0068     KisTestableUpdaterContext context(3);
0069     QVector<KisUpdateJobItem*> jobs;
0070 
0071     queue.processQueue(context, false);
0072 
0073     jobs = context.getJobs();
0074     COMPARE_NAME(jobs[0], "tri_init");
0075     VERIFY_EMPTY(jobs[1]);
0076 
0077     context.clear();
0078     queue.processQueue(context, false);
0079 
0080     jobs = context.getJobs();
0081     COMPARE_NAME(jobs[0], "tri_dab");
0082     COMPARE_NAME(jobs[1], "tri_dab");
0083 
0084     context.clear();
0085     queue.processQueue(context, false);
0086 
0087     jobs = context.getJobs();
0088     COMPARE_NAME(jobs[0], "tri_finish");
0089     VERIFY_EMPTY(jobs[1]);
0090 }
0091 
0092 void KisStrokesQueueTest::testExclusiveStrokes()
0093 {
0094     KisStrokesQueue queue;
0095     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("excl_"), true));
0096     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0097     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0098     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0099     queue.endStroke(id);
0100 
0101     // well, this walker is not initialized... but who cares?
0102     KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
0103 
0104     KisTestableUpdaterContext context(2);
0105     QVector<KisUpdateJobItem*> jobs;
0106 
0107     context.addMergeJob(walker);
0108     queue.processQueue(context, false);
0109 
0110     jobs = context.getJobs();
0111     COMPARE_WALKER(jobs[0], walker);
0112     VERIFY_EMPTY(jobs[1]);
0113     QCOMPARE(queue.needsExclusiveAccess(), true);
0114 
0115     context.clear();
0116     queue.processQueue(context, false);
0117 
0118     jobs = context.getJobs();
0119     COMPARE_NAME(jobs[0], "excl_init");
0120     VERIFY_EMPTY(jobs[1]);
0121     QCOMPARE(queue.needsExclusiveAccess(), true);
0122 
0123     context.clear();
0124     queue.processQueue(context, false);
0125 
0126     jobs = context.getJobs();
0127     COMPARE_NAME(jobs[0], "excl_dab");
0128     COMPARE_NAME(jobs[1], "excl_dab");
0129     QCOMPARE(queue.needsExclusiveAccess(), true);
0130 
0131     context.clear();
0132     context.addMergeJob(walker);
0133     queue.processQueue(context, false);
0134 
0135     COMPARE_WALKER(jobs[0], walker);
0136     VERIFY_EMPTY(jobs[1]);
0137     QCOMPARE(queue.needsExclusiveAccess(), true);
0138 
0139     context.clear();
0140     queue.processQueue(context, false);
0141 
0142     jobs = context.getJobs();
0143     COMPARE_NAME(jobs[0], "excl_dab");
0144     VERIFY_EMPTY(jobs[1]);
0145     QCOMPARE(queue.needsExclusiveAccess(), true);
0146 
0147     context.clear();
0148     queue.processQueue(context, false);
0149 
0150     jobs = context.getJobs();
0151     COMPARE_NAME(jobs[0], "excl_finish");
0152     VERIFY_EMPTY(jobs[1]);
0153     QCOMPARE(queue.needsExclusiveAccess(), true);
0154 
0155     context.clear();
0156     queue.processQueue(context, false);
0157 
0158     QCOMPARE(queue.needsExclusiveAccess(), false);
0159 }
0160 
0161 void KisStrokesQueueTest::testBarrierStrokeJobs()
0162 {
0163     KisStrokesQueue queue;
0164     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("nor_"), false));
0165     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0166     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::BARRIER));
0167     queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
0168     queue.endStroke(id);
0169 
0170     // yes, this walker is not initialized again... but who cares?
0171     KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
0172     bool externalJobsPending = false;
0173 
0174     KisTestableUpdaterContext context(3);
0175     QVector<KisUpdateJobItem*> jobs;
0176 
0177     queue.processQueue(context, externalJobsPending);
0178 
0179     jobs = context.getJobs();
0180     COMPARE_NAME(jobs[0], "nor_init");
0181     VERIFY_EMPTY(jobs[1]);
0182     VERIFY_EMPTY(jobs[2]);
0183 
0184     context.clear();
0185 
0186     queue.processQueue(context, externalJobsPending);
0187 
0188     jobs = context.getJobs();
0189     COMPARE_NAME(jobs[0], "nor_dab");
0190     VERIFY_EMPTY(jobs[1]);
0191     VERIFY_EMPTY(jobs[2]);
0192 
0193     // Now some updates has come...
0194     context.addMergeJob(walker);
0195 
0196     jobs = context.getJobs();
0197     COMPARE_NAME(jobs[0], "nor_dab");
0198     COMPARE_WALKER(jobs[1], walker);
0199     VERIFY_EMPTY(jobs[2]);
0200 
0201     // No difference for the queue
0202     queue.processQueue(context, externalJobsPending);
0203 
0204     jobs = context.getJobs();
0205     COMPARE_NAME(jobs[0], "nor_dab");
0206     COMPARE_WALKER(jobs[1], walker);
0207     VERIFY_EMPTY(jobs[2]);
0208 
0209     // Even more updates has come...
0210     externalJobsPending = true;
0211 
0212     // Still no difference for the queue
0213     queue.processQueue(context, externalJobsPending);
0214 
0215     jobs = context.getJobs();
0216     COMPARE_NAME(jobs[0], "nor_dab");
0217     COMPARE_WALKER(jobs[1], walker);
0218     VERIFY_EMPTY(jobs[2]);
0219 
0220     // Now clear the context
0221     context.clear();
0222 
0223     // And still no difference for the queue
0224     queue.processQueue(context, externalJobsPending);
0225 
0226     jobs = context.getJobs();
0227     VERIFY_EMPTY(jobs[0]);
0228     VERIFY_EMPTY(jobs[1]);
0229     VERIFY_EMPTY(jobs[2]);
0230 
0231     // Process the last update...
0232     context.addMergeJob(walker);
0233     externalJobsPending = false;
0234 
0235     // Yep, the queue is still waiting
0236     queue.processQueue(context, externalJobsPending);
0237 
0238     jobs = context.getJobs();
0239     COMPARE_WALKER(jobs[0], walker);
0240     VERIFY_EMPTY(jobs[1]);
0241     VERIFY_EMPTY(jobs[2]);
0242 
0243     context.clear();
0244 
0245     // Finally, we can do our work. Barrier job is executed alone
0246     queue.processQueue(context, externalJobsPending);
0247 
0248     jobs = context.getJobs();
0249     COMPARE_NAME(jobs[0], "nor_dab");
0250     VERIFY_EMPTY(jobs[1]);
0251     VERIFY_EMPTY(jobs[2]);
0252 
0253     // Barrier job has finished
0254     context.clear();
0255 
0256     jobs = context.getJobs();
0257     VERIFY_EMPTY(jobs[0]);
0258     VERIFY_EMPTY(jobs[1]);
0259     VERIFY_EMPTY(jobs[2]);
0260 
0261     // fetch the last (concurrent) one
0262     queue.processQueue(context, externalJobsPending);
0263 
0264     jobs = context.getJobs();
0265     COMPARE_NAME(jobs[0], "nor_dab");
0266     VERIFY_EMPTY(jobs[1]);
0267     VERIFY_EMPTY(jobs[2]);
0268 
0269     context.clear();
0270 
0271     // finish the stroke
0272     queue.processQueue(context, externalJobsPending);
0273 
0274     jobs = context.getJobs();
0275     COMPARE_NAME(jobs[0], "nor_finish");
0276     VERIFY_EMPTY(jobs[1]);
0277     VERIFY_EMPTY(jobs[2]);
0278 
0279     context.clear();
0280 }
0281 
0282 void KisStrokesQueueTest::testStrokesOverlapping()
0283 {
0284     KisStrokesQueue queue;
0285     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("1_"), false, true));
0286     queue.addJob(id, 0);
0287 
0288     // comment out this line to catch an assert
0289     queue.endStroke(id);
0290 
0291     id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("2_"), false, true));
0292     queue.addJob(id, 0);
0293     queue.endStroke(id);
0294 
0295     // uncomment this line to catch an assert
0296     // queue.addJob(id, 0);
0297 
0298     KisTestableUpdaterContext context(2);
0299     QVector<KisUpdateJobItem*> jobs;
0300 
0301     queue.processQueue(context, false);
0302 
0303     jobs = context.getJobs();
0304     COMPARE_NAME(jobs[0], "1_dab");
0305     VERIFY_EMPTY(jobs[1]);
0306 
0307     context.clear();
0308     queue.processQueue(context, false);
0309 
0310     jobs = context.getJobs();
0311     COMPARE_NAME(jobs[0], "2_dab");
0312     VERIFY_EMPTY(jobs[1]);
0313 }
0314 
0315 void KisStrokesQueueTest::testImmediateCancel()
0316 {
0317     KisStrokesQueue queue;
0318     KisTestableUpdaterContext context(2);
0319 
0320     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("1_"), false, false));
0321     queue.cancelStroke(id);
0322 
0323     // this should not crash
0324     queue.processQueue(context, false);
0325 }
0326 
0327 void KisStrokesQueueTest::testOpenedStrokeCounter()
0328 {
0329     KisStrokesQueue queue;
0330 
0331     QVERIFY(!queue.hasOpenedStrokes());
0332     KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("0")));
0333     QVERIFY(queue.hasOpenedStrokes());
0334     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("1")));
0335     QVERIFY(queue.hasOpenedStrokes());
0336     queue.endStroke(id0);
0337     QVERIFY(queue.hasOpenedStrokes());
0338     queue.endStroke(id1);
0339     QVERIFY(!queue.hasOpenedStrokes());
0340 
0341     KisTestableUpdaterContext context(2);
0342     queue.processQueue(context, false); context.clear();
0343     queue.processQueue(context, false); context.clear();
0344     queue.processQueue(context, false); context.clear();
0345     queue.processQueue(context, false); context.clear();
0346 }
0347 
0348 void KisStrokesQueueTest::testAsyncCancelWhileOpenedStroke()
0349 {
0350     KisStrokesQueue queue;
0351     KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("nor_"), false));
0352     queue.addJob(id, 0);
0353     queue.addJob(id, 0);
0354     queue.addJob(id, 0);
0355 
0356     // no async cancelling until the stroke is ended by the owner
0357     QVERIFY(!queue.tryCancelCurrentStrokeAsync());
0358 
0359     queue.endStroke(id);
0360 
0361     QVERIFY(queue.tryCancelCurrentStrokeAsync());
0362 
0363     bool externalJobsPending = false;
0364 
0365     KisTestableUpdaterContext context(3);
0366     QVector<KisUpdateJobItem*> jobs;
0367 
0368     queue.processQueue(context, externalJobsPending);
0369 
0370     // no? really?
0371     jobs = context.getJobs();
0372     VERIFY_EMPTY(jobs[0]);
0373     VERIFY_EMPTY(jobs[1]);
0374     VERIFY_EMPTY(jobs[2]);
0375 }
0376 
0377 struct KisStrokesQueueTest::LodStrokesQueueTester {
0378 
0379     LodStrokesQueueTester(bool real = false)
0380         : fakeContext(2),
0381           realContext(2),
0382           context(!real ? fakeContext : realContext)
0383     {
0384         queue.setSuspendResumeUpdatesStrokeStrategyFactory(
0385             []() {
0386                 KisSuspendResumePair suspend(
0387                     new KisTestingStrokeStrategy(QLatin1String("susp_u_"), false, true, true),
0388                     QList<KisStrokeJobData*>());
0389 
0390                 KisSuspendResumePair resume(
0391                     new KisTestingStrokeStrategy(QLatin1String("resu_u_"), false, true, true),
0392                     QList<KisStrokeJobData*>());
0393 
0394                 return std::make_pair(suspend, resume);
0395             });
0396 
0397         queue.setLod0ToNStrokeStrategyFactory(
0398             [] (bool forgettable) {
0399                 Q_UNUSED(forgettable);
0400                 return KisLodSyncPair(
0401                     new KisTestingStrokeStrategy(QLatin1String("sync_u_"), false, true, true),
0402                     QList<KisStrokeJobData*>());
0403             });
0404     }
0405 
0406     KisStrokesQueue queue;
0407 
0408     KisTestableUpdaterContext fakeContext;
0409     KisUpdaterContext realContext;
0410     KisUpdaterContext &context;
0411     QVector<KisUpdateJobItem*> jobs;
0412 
0413     void processQueueNoAdd() {
0414         if (&context != &fakeContext) return;
0415 
0416         fakeContext.clear();
0417 
0418         jobs = fakeContext.getJobs();
0419         VERIFY_EMPTY(jobs[0]);
0420         VERIFY_EMPTY(jobs[1]);
0421     }
0422 
0423     void processQueueNoContextClear() {
0424         queue.processQueue(context, false);
0425 
0426         if (&context == &realContext) {
0427             context.waitForDone();
0428         }
0429     }
0430 
0431     void processQueue() {
0432         processQueueNoAdd();
0433         queue.processQueue(context, false);
0434 
0435         if (&context == &realContext) {
0436             context.waitForDone();
0437         }
0438     }
0439 
0440     void checkNothing() {
0441         KIS_ASSERT(&context == &fakeContext);
0442 
0443         jobs = fakeContext.getJobs();
0444         VERIFY_EMPTY(jobs[0]);
0445         VERIFY_EMPTY(jobs[1]);
0446     }
0447 
0448     void checkJobs(const QStringList &list) {
0449         KIS_ASSERT(&context == &fakeContext);
0450 
0451         jobs = fakeContext.getJobs();
0452 
0453         for (int i = 0; i < 2; i++) {
0454             if (list.size() <= i) {
0455                 VERIFY_EMPTY(jobs[i]);
0456             } else {
0457                 QVERIFY(jobs[i]->isRunning());
0458                 COMPARE_NAME(jobs[i], list[i]);
0459             }
0460         }
0461 
0462         QCOMPARE(queue.needsExclusiveAccess(), false);
0463     }
0464 
0465     void checkOnlyJob(const QString &name) {
0466         KIS_ASSERT(&context == &fakeContext);
0467 
0468         jobs = fakeContext.getJobs();
0469         COMPARE_NAME(jobs[0], name);
0470         VERIFY_EMPTY(jobs[1]);
0471         QCOMPARE(queue.needsExclusiveAccess(), false);
0472     }
0473 
0474     void checkOnlyExecutedJob(const QString &name) {
0475         realContext.waitForDone();
0476         QVERIFY(!globalExecutedDabs.isEmpty());
0477         QCOMPARE(globalExecutedDabs[0], name);
0478 
0479         QCOMPARE(globalExecutedDabs.size(), 1);
0480         globalExecutedDabs.clear();
0481     }
0482 
0483     void checkExecutedJobs(const QStringList &list) {
0484         realContext.waitForDone();
0485 
0486         QCOMPARE(globalExecutedDabs, list);
0487         globalExecutedDabs.clear();
0488     }
0489 
0490     void checkNothingExecuted() {
0491         realContext.waitForDone();
0492         QVERIFY(globalExecutedDabs.isEmpty());
0493     }
0494 };
0495 
0496 
0497 void KisStrokesQueueTest::testStrokesLevelOfDetail()
0498 {
0499     LodStrokesQueueTester t;
0500     KisStrokesQueue &queue = t.queue;
0501 
0502     // create a stroke with LOD0 + LOD2
0503     queue.setLodPreferences(KisLodPreferences(2));
0504 
0505     // process sync-lodn-planes stroke
0506     t.processQueue();
0507     t.checkOnlyJob("sync_u_init");
0508 
0509     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod_"), false, true));
0510     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0511     queue.endStroke(id2);
0512 
0513     // create a update with LOD == 0 (default one)
0514     // well, this walker is not initialized... but who cares?
0515     KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
0516 
0517     KisTestableUpdaterContext context(2);
0518     QVector<KisUpdateJobItem*> jobs;
0519 
0520     context.addMergeJob(walker);
0521     queue.processQueue(context, false);
0522 
0523     jobs = context.getJobs();
0524     COMPARE_WALKER(jobs[0], walker);
0525     VERIFY_EMPTY(jobs[1]);
0526     QCOMPARE(queue.needsExclusiveAccess(), false);
0527 
0528     context.clear();
0529 
0530     jobs = context.getJobs();
0531     VERIFY_EMPTY(jobs[0]);
0532     VERIFY_EMPTY(jobs[1]);
0533 
0534     context.clear();
0535     queue.processQueue(context, false);
0536 
0537     jobs = context.getJobs();
0538     COMPARE_NAME(jobs[0], "clone2_lod_dab");
0539     VERIFY_EMPTY(jobs[1]);
0540     QCOMPARE(queue.needsExclusiveAccess(), false);
0541 
0542     // walker of a different LOD must not be allowed
0543     QCOMPARE(context.isJobAllowed(walker), false);
0544 
0545     context.clear();
0546     context.addMergeJob(walker);
0547     queue.processQueue(context, false);
0548 
0549     jobs = context.getJobs();
0550     COMPARE_WALKER(jobs[0], walker);
0551     COMPARE_NAME(jobs[1], "susp_u_init");
0552     QCOMPARE(queue.needsExclusiveAccess(), false);
0553 
0554     context.clear();
0555     queue.processQueue(context, false);
0556 
0557     jobs = context.getJobs();
0558     COMPARE_NAME(jobs[0], "lod_dab");
0559     VERIFY_EMPTY(jobs[1]);
0560     QCOMPARE(queue.needsExclusiveAccess(), false);
0561 
0562     context.clear();
0563     queue.processQueue(context, false);
0564 
0565     jobs = context.getJobs();
0566     COMPARE_NAME(jobs[0], "resu_u_init");
0567     VERIFY_EMPTY(jobs[1]);
0568     QCOMPARE(queue.needsExclusiveAccess(), false);
0569 
0570     context.clear();
0571 }
0572 
0573 void KisStrokesQueueTest::testStrokeWithMixedLodJobs()
0574 {
0575     LodStrokesQueueTester t;
0576     KisStrokesQueue &queue = t.queue;
0577 
0578     // create a stroke with LOD0 + LOD2
0579     queue.setLodPreferences(KisLodPreferences(2));
0580 
0581     // process sync-lodn-planes stroke
0582     t.processQueue();
0583     t.checkOnlyJob("sync_u_init");
0584 
0585     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod_"), false, true, false, false, true));
0586 
0587     KisStrokeJobData *data = 0;
0588     data = new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT,
0589                                        KisStrokeJobData::NORMAL,
0590                                        false, "job0_l0");
0591     queue.addJob(id2, data);
0592 
0593     data = new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT,
0594                                        KisStrokeJobData::NORMAL,
0595                                        false, "job1_l0");
0596     queue.addJob(id2, data);
0597 
0598     data = new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT,
0599                                        KisStrokeJobData::NORMAL,
0600                                        false, "job2_l0");
0601     queue.addJob(id2, data);
0602 
0603     data = new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT,
0604                                        KisStrokeJobData::NORMAL,
0605                                        false, "job3_l2");
0606     data->setLevelOfDetailOverride(2);
0607     queue.addJob(id2, data);
0608 
0609     data = new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT,
0610                                        KisStrokeJobData::NORMAL,
0611                                        false, "job4_l0");
0612     queue.addJob(id2, data);
0613 
0614     queue.endStroke(id2);
0615 
0616     t.processQueue();
0617     t.checkJobs({"lod_dab_job0_l0", "lod_dab_job1_l0"});
0618     QCOMPARE(t.context.currentLevelOfDetail(), 0);
0619 
0620     t.processQueue();
0621     t.checkOnlyJob("lod_dab_job2_l0");
0622     QCOMPARE(t.context.currentLevelOfDetail(), 0);
0623 
0624     t.processQueue();
0625     t.checkOnlyJob("lod_dab_job3_l2");
0626     QCOMPARE(t.context.currentLevelOfDetail(), 2);
0627 
0628     t.processQueue();
0629     t.checkOnlyJob("lod_dab_job4_l0");
0630     QCOMPARE(t.context.currentLevelOfDetail(), 0);
0631 }
0632 
0633 void KisStrokesQueueTest::testMultipleLevelOfDetailStrokes()
0634 {
0635     LodStrokesQueueTester t;
0636     KisStrokesQueue &queue = t.queue;
0637 
0638     // create a stroke with LOD0 + LOD2
0639     queue.setLodPreferences(KisLodPreferences(2));
0640 
0641     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod1_"), false, true));
0642     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0643     queue.endStroke(id1);
0644 
0645     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod2_"), false, true));
0646     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0647     queue.endStroke(id2);
0648 
0649     t.processQueue();
0650     t.checkOnlyJob("sync_u_init");
0651 
0652     t.processQueue();
0653     t.checkOnlyJob("clone2_lod1_dab");
0654 
0655     t.processQueue();
0656     t.checkOnlyJob("clone2_lod2_dab");
0657 
0658     t.processQueue();
0659     t.checkOnlyJob("susp_u_init");
0660 
0661     t.processQueue();
0662     t.checkOnlyJob("lod1_dab");
0663 
0664     t.processQueue();
0665     t.checkOnlyJob("lod2_dab");
0666 
0667     t.processQueue();
0668     t.checkOnlyJob("resu_u_init");
0669 }
0670 
0671 void KisStrokesQueueTest::testMultipleLevelOfDetailAfterLegacy()
0672 {
0673     LodStrokesQueueTester t;
0674     KisStrokesQueue &queue = t.queue;
0675 
0676     // create a stroke with LOD0 + LOD2
0677     queue.setLodPreferences(KisLodPreferences(2));
0678 
0679     KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("leg0_"), false, true, false, false, true));
0680     queue.addJob(id0, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0681     queue.endStroke(id0);
0682 
0683     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod1_"), false, true));
0684     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0685     queue.endStroke(id1);
0686 
0687     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod2_"), false, true));
0688     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0689     queue.endStroke(id2);
0690 
0691     t.processQueue();
0692     t.checkOnlyJob("sync_u_init");
0693 
0694     t.processQueue();
0695     t.checkOnlyJob("leg0_dab");
0696 
0697     t.processQueue();
0698     t.checkOnlyJob("sync_u_init");
0699 
0700     t.processQueue();
0701     t.checkOnlyJob("clone2_lod1_dab");
0702 
0703     t.processQueue();
0704     t.checkOnlyJob("clone2_lod2_dab");
0705 
0706     t.processQueue();
0707     t.checkOnlyJob("susp_u_init");
0708 
0709     t.processQueue();
0710     t.checkOnlyJob("lod1_dab");
0711 
0712     t.processQueue();
0713     t.checkOnlyJob("lod2_dab");
0714 
0715     t.processQueue();
0716     t.checkOnlyJob("resu_u_init");
0717 
0718 }
0719 
0720 void KisStrokesQueueTest::testMultipleLevelOfDetailMixedLegacy()
0721 {
0722     LodStrokesQueueTester t;
0723     KisStrokesQueue &queue = t.queue;
0724 
0725     // create a stroke with LOD0 + LOD2
0726     queue.setLodPreferences(KisLodPreferences(2));
0727 
0728     KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod0_"), false, true));
0729     queue.addJob(id0, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0730     queue.endStroke(id0);
0731 
0732     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("leg1_"), false, true, false, false, true));
0733     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0734     queue.endStroke(id1);
0735 
0736     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod2_"), false, true));
0737     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0738     queue.endStroke(id2);
0739 
0740     t.processQueue();
0741     t.checkOnlyJob("sync_u_init");
0742 
0743     t.processQueue();
0744     t.checkOnlyJob("clone2_lod0_dab");
0745 
0746     t.processQueue();
0747     t.checkOnlyJob("susp_u_init");
0748 
0749     t.processQueue();
0750     t.checkOnlyJob("lod0_dab");
0751 
0752     t.processQueue();
0753     t.checkOnlyJob("resu_u_init");
0754 
0755     t.processQueue();
0756     t.checkOnlyJob("leg1_dab");
0757 
0758     t.processQueue();
0759     t.checkOnlyJob("sync_u_init");
0760 
0761     t.processQueue();
0762     t.checkOnlyJob("clone2_lod2_dab");
0763 
0764     t.processQueue();
0765     t.checkOnlyJob("susp_u_init");
0766 
0767     t.processQueue();
0768     t.checkOnlyJob("lod2_dab");
0769 
0770     t.processQueue();
0771     t.checkOnlyJob("resu_u_init");
0772 }
0773 
0774 void KisStrokesQueueTest::testCancelBetweenLodNStrokes()
0775 {
0776     LodStrokesQueueTester t;
0777     KisStrokesQueue &queue = t.queue;
0778 
0779     // create a stroke with LOD0 + LOD2
0780     queue.setLodPreferences(KisLodPreferences(2));
0781 
0782     KisStrokeId idneg1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("legneg1_"), false, true, false, false, true));
0783     queue.addJob(idneg1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0784     queue.endStroke(idneg1);
0785 
0786     KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod0_"), false, true));
0787     queue.addJob(id0, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0788     queue.endStroke(id0);
0789 
0790     // cancelling the strokes should force synchronization of lod planes
0791     queue.tryCancelCurrentStrokeAsync();
0792 
0793     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod2_"), false, true));
0794     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0795     queue.endStroke(id2);
0796 
0797     t.processQueue();
0798     t.checkOnlyJob("sync_u_init");
0799 
0800     t.processQueue();
0801     t.checkOnlyJob("clone2_lod2_dab");
0802 
0803     t.processQueue();
0804     t.checkOnlyJob("susp_u_init");
0805 
0806     t.processQueue();
0807     t.checkOnlyJob("lod2_dab");
0808 
0809     t.processQueue();
0810     t.checkOnlyJob("resu_u_init");
0811 }
0812 
0813 void KisStrokesQueueTest::testUFOVisitBetweenLodNStrokes()
0814 {
0815     LodStrokesQueueTester t;
0816     KisStrokesQueue &queue = t.queue;
0817 
0818     // create a stroke with LOD0 + LOD2
0819     queue.setLodPreferences(KisLodPreferences(2));
0820 
0821     KisStrokeId idneg1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("legneg1_"), false, true, false, false, true));
0822     queue.addJob(idneg1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0823     queue.endStroke(idneg1);
0824 
0825     KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod0_"), false, true));
0826     queue.addJob(id0, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0827     queue.endStroke(id0);
0828 
0829     // force synchronization of lod planes
0830     queue.notifyUFOChangedImage();
0831 
0832     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("lod2_"), false, true));
0833     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0834     queue.endStroke(id2);
0835 
0836     t.processQueue();
0837     t.checkOnlyJob("sync_u_init");
0838 
0839     t.processQueue();
0840     t.checkOnlyJob("legneg1_dab");
0841 
0842     t.processQueue();
0843     t.checkOnlyJob("sync_u_init");
0844 
0845     t.processQueue();
0846     t.checkOnlyJob("clone2_lod0_dab");
0847 
0848     t.processQueue();
0849     t.checkOnlyJob("susp_u_init");
0850 
0851     t.processQueue();
0852     t.checkOnlyJob("lod0_dab");
0853 
0854     t.processQueue();
0855     t.checkOnlyJob("resu_u_init");
0856 
0857     t.processQueue();
0858     t.checkOnlyJob("sync_u_init");
0859 
0860     t.processQueue();
0861     t.checkOnlyJob("clone2_lod2_dab");
0862 
0863     t.processQueue();
0864     t.checkOnlyJob("susp_u_init");
0865 
0866     t.processQueue();
0867     t.checkOnlyJob("lod2_dab");
0868 
0869     t.processQueue();
0870     t.checkOnlyJob("resu_u_init");
0871 
0872 }
0873 
0874 #include <kundo2command.h>
0875 #include <kis_post_execution_undo_adapter.h>
0876 struct TestUndoCommand : public KUndo2Command
0877 {
0878     TestUndoCommand(const QString &text) : KUndo2Command(kundo2_noi18n(text)) {}
0879 
0880     void undo() override {
0881         ENTER_FUNCTION();
0882         undoCount++;
0883     }
0884 
0885     void redo() override {
0886         ENTER_FUNCTION();
0887         redoCount++;
0888     }
0889 
0890     int undoCount = 0;
0891     int redoCount = 0;
0892 };
0893 
0894 void KisStrokesQueueTest::testLodUndoBase()
0895 {
0896     LodStrokesQueueTester t;
0897     KisStrokesQueue &queue = t.queue;
0898 
0899     // create a stroke with LOD0 + LOD2
0900     queue.setLodPreferences(KisLodPreferences(2));
0901 
0902     // process sync-lodn-planes stroke
0903     t.processQueue();
0904     t.checkOnlyJob("sync_u_init");
0905 
0906     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str1_"), false, true));
0907     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0908     queue.endStroke(id1);
0909 
0910     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str2_"), false, true));
0911     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0912     queue.endStroke(id2);
0913 
0914     t.processQueue();
0915     t.checkOnlyJob("clone2_str1_dab");
0916 
0917 
0918     QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
0919     queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
0920 
0921     t.processQueue();
0922     t.checkOnlyJob("clone2_str2_dab");
0923 
0924     QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
0925     queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
0926 
0927     t.processQueue();
0928     t.checkOnlyJob("susp_u_init");
0929 
0930     t.processQueue();
0931     t.checkOnlyJob("str1_dab");
0932 
0933     t.processQueue();
0934     t.checkOnlyJob("str2_dab");
0935 
0936     t.processQueue();
0937     t.checkOnlyJob("resu_u_init");
0938 }
0939 
0940 void KisStrokesQueueTest::testLodUndoBase2()
0941 {
0942     LodStrokesQueueTester t(true);
0943     KisStrokesQueue &queue = t.queue;
0944 
0945     // create a stroke with LOD0 + LOD2
0946     queue.setLodPreferences(KisLodPreferences(2));
0947     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str1_"), false, true, false, true));
0948     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0949     queue.endStroke(id1);
0950 
0951     KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str2_"), false, true, false, true));
0952     queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
0953     queue.endStroke(id2);
0954 
0955     t.processQueue();
0956     t.checkOnlyExecutedJob("sync_u_init");
0957 
0958     t.processQueue();
0959     t.checkOnlyExecutedJob("clone2_str1_dab");
0960 
0961     QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
0962     queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
0963 
0964     t.processQueue();
0965     t.checkOnlyExecutedJob("clone2_str2_dab");
0966 
0967     QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
0968     queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
0969 
0970     t.processQueue();
0971     t.checkOnlyExecutedJob("susp_u_init");
0972 
0973     queue.tryUndoLastStrokeAsync();
0974     t.processQueue();
0975 
0976     while (queue.currentStrokeName() == kundo2_noi18n("str2_undo")) {
0977         //queue.debugPrintStrokes();
0978         t.processQueue();
0979     }
0980 
0981     QCOMPARE(undoStr2->undoCount, 1);
0982 
0983     t.checkOnlyExecutedJob("str1_dab");
0984 
0985     t.processQueue();
0986     t.checkOnlyExecutedJob("str2_cancel");
0987 
0988     t.processQueue();
0989     t.checkOnlyExecutedJob("resu_u_init");
0990 }
0991 
0992 void KisStrokesQueueTest::testMutatedJobs()
0993 {
0994     LodStrokesQueueTester t(true);
0995     KisStrokesQueue &queue = t.queue;
0996 
0997     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str1_"), false, true, false, true));
0998 
0999     queue.addJob(id1,
1000                  new KisTestingStrokeJobData(
1001                      KisStrokeJobData::CONCURRENT,
1002                      KisStrokeJobData::NORMAL,
1003                      true, "1"));
1004 
1005     queue.addJob(id1,
1006                  new KisTestingStrokeJobData(
1007                      KisStrokeJobData::SEQUENTIAL,
1008                      KisStrokeJobData::NORMAL,
1009                      false, "2"));
1010 
1011     queue.endStroke(id1);
1012 
1013     t.processQueue();
1014 
1015     t.checkOnlyExecutedJob("str1_dab_1");
1016 
1017     t.processQueue();
1018 
1019     QStringList refList;
1020     refList << "str1_dab_mutated" << "str1_dab_mutated";
1021     t.checkExecutedJobs(refList);
1022 
1023     t.processQueue();
1024     t.checkOnlyExecutedJob("str1_dab_mutated");
1025 
1026     t.processQueue();
1027     t.checkOnlyExecutedJob("str1_dab_2");
1028 
1029     t.processQueue();
1030     t.checkNothingExecuted();
1031 }
1032 
1033 QString sequentialityToString(KisStrokeJobData::Sequentiality seq) {
1034     QString result = "<unknown>";
1035 
1036     switch (seq) {
1037     case KisStrokeJobData::SEQUENTIAL:
1038         result = "SEQUENTIAL";
1039         break;
1040     case KisStrokeJobData::UNIQUELY_CONCURRENT:
1041         result = "UNIQUELY_CONCURRENT";
1042         break;
1043     case KisStrokeJobData::BARRIER:
1044         result = "BARRIER";
1045         break;
1046     case KisStrokeJobData::CONCURRENT:
1047         result = "CONCURRENT";
1048         break;
1049     }
1050 
1051     return result;
1052 }
1053 
1054 void KisStrokesQueueTest::checkJobsOverlapping(LodStrokesQueueTester &t,
1055                                                KisStrokeId id,
1056                                                KisStrokeJobData::Sequentiality first,
1057                                                KisStrokeJobData::Sequentiality second,
1058                                                bool allowed)
1059 {
1060     t.queue.addJob(id, new KisTestingStrokeJobData(first,
1061                                                    KisStrokeJobData::NORMAL, false, "first"));
1062     t.processQueue();
1063     t.checkJobs({"str1_dab_first"});
1064 
1065     t.queue.addJob(id, new KisTestingStrokeJobData(second,
1066                                                    KisStrokeJobData::NORMAL, false, "second"));
1067 
1068     qDebug() << QString("  test %1 after %2 allowed: %3 ")
1069                 .arg(sequentialityToString(second), 24)
1070                 .arg(sequentialityToString(first), 24)
1071                 .arg(allowed);
1072 
1073     if (allowed) {
1074         t.processQueueNoContextClear();
1075         t.checkJobs({"str1_dab_first", "str1_dab_second"});
1076     } else {
1077         t.processQueueNoContextClear();
1078         t.checkJobs({"str1_dab_first"});
1079 
1080         t.processQueue();
1081         t.checkJobs({"str1_dab_second"});
1082     }
1083 
1084     t.processQueueNoAdd();
1085     t.checkNothing();
1086 }
1087 
1088 void KisStrokesQueueTest::testUniquelyConcurrentJobs()
1089 {
1090     LodStrokesQueueTester t;
1091     KisStrokesQueue &queue = t.queue;
1092 
1093     KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy(QLatin1String("str1_"), false, true));
1094     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
1095     queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
1096 
1097     { // manual test
1098         t.processQueue();
1099         t.checkJobs({"str1_dab", "str1_dab"});
1100 
1101         queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
1102         t.processQueue();
1103         t.checkJobs({"str1_dab"});
1104 
1105         queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::UNIQUELY_CONCURRENT,
1106                                                   KisStrokeJobData::NORMAL, false, "ucon"));
1107         t.processQueueNoContextClear();
1108         t.checkJobs({"str1_dab", "str1_dab_ucon"});
1109 
1110         t.processQueueNoAdd();
1111         t.checkNothing();
1112     }
1113 
1114     // Test various cases of overlapping
1115 
1116     checkJobsOverlapping(t, id1, KisStrokeJobData::UNIQUELY_CONCURRENT, KisStrokeJobData::CONCURRENT, true);
1117     checkJobsOverlapping(t, id1, KisStrokeJobData::UNIQUELY_CONCURRENT, KisStrokeJobData::UNIQUELY_CONCURRENT, false);
1118     checkJobsOverlapping(t, id1, KisStrokeJobData::UNIQUELY_CONCURRENT, KisStrokeJobData::SEQUENTIAL, false);
1119     checkJobsOverlapping(t, id1, KisStrokeJobData::UNIQUELY_CONCURRENT, KisStrokeJobData::BARRIER, false);
1120 
1121     checkJobsOverlapping(t, id1, KisStrokeJobData::CONCURRENT, KisStrokeJobData::UNIQUELY_CONCURRENT , true);
1122     checkJobsOverlapping(t, id1, KisStrokeJobData::UNIQUELY_CONCURRENT, KisStrokeJobData::UNIQUELY_CONCURRENT, false);
1123     checkJobsOverlapping(t, id1, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::UNIQUELY_CONCURRENT, false);
1124     checkJobsOverlapping(t, id1, KisStrokeJobData::BARRIER, KisStrokeJobData::UNIQUELY_CONCURRENT, false);
1125 
1126     queue.endStroke(id1);
1127 }
1128 
1129 
1130 KISTEST_MAIN(KisStrokesQueueTest)