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)