1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "SQLiteObjectDbiUnitTests.h"
23 
24 #include <U2Core/DNAAlphabet.h>
25 #include <U2Core/U2AttributeDbi.h>
26 #include <U2Core/U2MsaDbi.h>
27 #include <U2Core/U2OpStatusUtils.h>
28 #include <U2Core/U2SequenceDbi.h>
29 #include <U2Core/U2SequenceUtils.h>
30 
31 #include <U2Formats/SQLiteDbi.h>
32 #include <U2Formats/SQLiteObjectDbi.h>
33 #include <U2Formats/SQLiteSequenceDbi.h>
34 
35 #include "core/util/MsaDbiUtilsUnitTests.h"
36 
37 namespace U2 {
38 
39 TestDbiProvider SQLiteObjectDbiTestData::dbiProvider = TestDbiProvider();
40 const QString &SQLiteObjectDbiTestData::SQLITE_OBJ_DB_URL("sqlite-obj-dbi.ugenedb");
41 U2AttributeDbi *SQLiteObjectDbiTestData::attributeDbi = nullptr;
42 U2MsaDbi *SQLiteObjectDbiTestData::msaDbi = nullptr;
43 U2SequenceDbi *SQLiteObjectDbiTestData::sequenceDbi = nullptr;
44 SQLiteDbi *SQLiteObjectDbiTestData::sqliteDbi = nullptr;
45 SQLiteObjectDbi *SQLiteObjectDbiTestData::sqliteObjectDbi = nullptr;
46 
init()47 void SQLiteObjectDbiTestData::init() {
48     SAFE_POINT(nullptr == sqliteDbi, "sqliteDbi has already been initialized!", );
49 
50     // Get URL
51     bool ok = dbiProvider.init(SQLITE_OBJ_DB_URL, false);
52     SAFE_POINT(ok, "Dbi provider failed to initialize in MsaTestData::init()!", );
53 
54     U2Dbi *dbi = dbiProvider.getDbi();
55     QString url = dbi->getDbiRef().dbiId;
56     dbiProvider.close();
57 
58     // Init DBI
59     sqliteDbi = new SQLiteDbi();
60     QHash<QString, QString> initProperties;
61     initProperties[U2DbiOptions::U2_DBI_OPTION_URL] = url;
62     U2OpStatusImpl os;
63     sqliteDbi->init(initProperties, QVariantMap(), os);
64     SAFE_POINT_OP(os, );
65 
66     sqliteObjectDbi = sqliteDbi->getSQLiteObjectDbi();
67     SAFE_POINT(nullptr != sqliteObjectDbi, "Failed to get sqliteObjectDbi!", );
68 
69     attributeDbi = sqliteDbi->getAttributeDbi();
70     SAFE_POINT(nullptr != attributeDbi, "Failed to get attributeDbi!", );
71 
72     msaDbi = sqliteDbi->getMsaDbi();
73     SAFE_POINT(nullptr != msaDbi, "Failed to get msaDbi!", );
74 
75     sequenceDbi = sqliteDbi->getSequenceDbi();
76     SAFE_POINT(nullptr != sequenceDbi, "Failed to get sequenceDbi!", );
77 }
78 
shutdown()79 void SQLiteObjectDbiTestData::shutdown() {
80     if (nullptr != sqliteDbi) {
81         delete sqliteDbi;
82 
83         sqliteDbi = nullptr;
84         sqliteObjectDbi = nullptr;
85         attributeDbi = nullptr;
86         msaDbi = nullptr;
87         sequenceDbi = nullptr;
88     }
89 }
90 
getSQLiteDbi()91 SQLiteDbi *SQLiteObjectDbiTestData::getSQLiteDbi() {
92     if (nullptr == sqliteDbi) {
93         init();
94     }
95     return sqliteDbi;
96 }
97 
getSQLiteObjectDbi()98 SQLiteObjectDbi *SQLiteObjectDbiTestData::getSQLiteObjectDbi() {
99     if (nullptr == sqliteObjectDbi) {
100         init();
101     }
102     return sqliteObjectDbi;
103 }
104 
getAttributeDbi()105 U2AttributeDbi *SQLiteObjectDbiTestData::getAttributeDbi() {
106     if (nullptr == attributeDbi) {
107         init();
108     }
109     return attributeDbi;
110 }
111 
getMsaDbi()112 U2MsaDbi *SQLiteObjectDbiTestData::getMsaDbi() {
113     if (nullptr == msaDbi) {
114         init();
115     }
116     return msaDbi;
117 }
118 
getSequenceDbi()119 U2SequenceDbi *SQLiteObjectDbiTestData::getSequenceDbi() {
120     if (nullptr == sequenceDbi) {
121         init();
122     }
123     return sequenceDbi;
124 }
125 
createTestMsa(bool enableModTracking,U2OpStatus & os)126 U2DataId SQLiteObjectDbiTestData::createTestMsa(bool enableModTracking, U2OpStatus &os) {
127     // Create an alignment
128     U2AlphabetId alphabet = BaseDNAAlphabetIds::NUCL_DNA_DEFAULT();
129     U2DataId msaId = sqliteDbi->getMsaDbi()->createMsaObject("", "Test alignment", alphabet, os);
130     CHECK_OP(os, U2DataId());
131 
132     if (enableModTracking) {
133         sqliteDbi->getObjectDbi()->setTrackModType(msaId, TrackOnUpdate, os);
134         CHECK_OP(os, U2DataId());
135     }
136 
137     return msaId;
138 }
139 
addTestRow(const U2DataId & msaId,U2OpStatus & os)140 void SQLiteObjectDbiTestData::addTestRow(const U2DataId &msaId, U2OpStatus &os) {
141     U2Sequence seq;
142     seq.alphabet = BaseDNAAlphabetIds::NUCL_DNA_DEFAULT();
143     seq.circular = false;
144     seq.trackModType = NoTrack;
145     seq.visualName = "Test sequence";
146     sqliteDbi->getSQLiteSequenceDbi()->createSequenceObject(seq, "", os, U2DbiObjectRank_TopLevel);
147     SAFE_POINT_OP(os, );
148 
149     U2MsaRow row;
150     row.sequenceId = seq.id;
151     row.gstart = 0;
152     row.gend = 0;
153     row.length = 0;
154 
155     sqliteDbi->getMsaDbi()->addRow(msaId, -1, row, os);
156     SAFE_POINT_OP(os, );
157 }
158 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,removeMsaObject)159 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, removeMsaObject) {
160     U2OpStatusImpl os;
161     U2MsaDbi *msaDbi = SQLiteObjectDbiTestData::getMsaDbi();
162 
163     // FIRST ALIGNMENT
164     // Create an alignment
165     U2DataId msaId = msaDbi->createMsaObject("", "Test name", BaseDNAAlphabetIds::NUCL_DNA_DEFAULT(), os);
166     CHECK_NO_ERROR(os);
167 
168     // Add alignment info
169     U2StringAttribute attr(msaId, "MSA1 info key", "MSA1 info value");
170     U2AttributeDbi *attrDbi = SQLiteObjectDbiTestData::getAttributeDbi();
171     attrDbi->createStringAttribute(attr, os);
172     CHECK_NO_ERROR(os);
173 
174     // Create sequences
175     U2SequenceDbi *sequenceDbi = SQLiteObjectDbiTestData::getSequenceDbi();
176     U2Sequence seq1;
177     U2Sequence seq2;
178     sequenceDbi->createSequenceObject(seq1, "", os);
179     CHECK_NO_ERROR(os);
180     sequenceDbi->createSequenceObject(seq2, "", os);
181     CHECK_NO_ERROR(os);
182 
183     // Add rows
184     U2MsaRow row1;
185     row1.rowId = 0;
186     row1.sequenceId = seq1.id;
187     row1.gstart = 0;
188     row1.gend = 5;
189 
190     U2MsaGap row1gap1(0, 2);
191     U2MsaGap row1gap2(3, 1);
192     QList<U2MsaGap> row1gaps;
193     row1gaps << row1gap1 << row1gap2;
194 
195     row1.gaps = row1gaps;
196 
197     U2MsaRow row2;
198     row2.rowId = 1;
199     row2.sequenceId = seq2.id;
200     row2.gstart = 2;
201     row2.gend = 4;
202 
203     U2MsaGap row2gap(1, 2);
204     QList<U2MsaGap> row2gaps;
205     row2gaps << row2gap;
206 
207     row2.gaps = row2gaps;
208 
209     QList<U2MsaRow> rows;
210     rows << row1 << row2;
211 
212     msaDbi->addRows(msaId, rows, -1, os);
213     CHECK_NO_ERROR(os);
214 
215     // SECOND ALIGNMENT
216     // Create an alignment
217     U2DataId msaId2 = msaDbi->createMsaObject("", "Test name 2", BaseDNAAlphabetIds::AMINO_DEFAULT(), os);
218     CHECK_NO_ERROR(os);
219 
220     // Add alignment info
221     U2StringAttribute attr2(msaId2, "MSA2 info key", "MSA2 info value");
222     attrDbi->createStringAttribute(attr2, os);
223     CHECK_NO_ERROR(os);
224 
225     // Create sequences
226     U2Sequence al2Seq;
227     sequenceDbi->createSequenceObject(al2Seq, "", os);
228     CHECK_NO_ERROR(os);
229 
230     // Add rows
231     U2MsaRow al2Row;
232     al2Row.rowId = 0;
233     al2Row.sequenceId = al2Seq.id;
234     al2Row.gstart = 0;
235     al2Row.gend = 15;
236 
237     U2MsaGap al2RowGap(1, 12);
238     QList<U2MsaGap> al2RowGaps;
239     al2RowGaps << al2RowGap;
240 
241     al2Row.gaps = al2RowGaps;
242 
243     QList<U2MsaRow> al2Rows;
244     al2Rows << al2Row;
245 
246     msaDbi->addRows(msaId2, al2Rows, -1, os);
247     CHECK_NO_ERROR(os);
248 
249     // REMOVE THE FIRST ALIGNMENT OBJECT
250     SQLiteObjectDbi *sqliteObjectDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
251     sqliteObjectDbi->removeObject(msaId, os);
252 
253     // VERIFY THAT THERE IS ONLY THE SECOND ALIGNMENT'S RECORDS LEFT IN TABLES
254     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
255 
256     // "Attribute"
257     SQLiteReadQuery qAttr("SELECT COUNT(*) FROM Attribute WHERE name = ?1", sqliteDbi->getDbRef(), os);
258     qAttr.bindString(1, "MSA1 info key");
259     qint64 msa1AttrNum = qAttr.selectInt64();
260     CHECK_EQUAL(0, msa1AttrNum, "MSA1 attributes number");
261 
262     qAttr.reset(true);
263     qAttr.bindString(1, "MSA2 info key");
264     qint64 msa2AttrNum = qAttr.selectInt64();
265     CHECK_EQUAL(1, msa2AttrNum, "MSA2 attributes number");
266 
267     // "StringAttribute"
268     SQLiteReadQuery qStringAttr("SELECT COUNT(*) FROM StringAttribute WHERE value = ?1", sqliteDbi->getDbRef(), os);
269     qStringAttr.bindString(1, "MSA1 info value");
270     qint64 msa1StrAttrNum = qStringAttr.selectInt64();
271     CHECK_EQUAL(0, msa1StrAttrNum, "MSA1 string attributes number");
272 
273     qStringAttr.reset(true);
274     qStringAttr.bindString(1, "MSA2 info value");
275     qint64 msa2StrAttrNum = qStringAttr.selectInt64();
276     CHECK_EQUAL(1, msa2StrAttrNum, "MSA2 string attributes number");
277 
278     // "MsaRow"
279     SQLiteReadQuery qMsaRow("SELECT COUNT(*) FROM MsaRow WHERE msa = ?1", sqliteDbi->getDbRef(), os);
280     qMsaRow.bindDataId(1, msaId);
281     qint64 msa1Rows = qMsaRow.selectInt64();
282     CHECK_EQUAL(0, msa1Rows, "number of rows in MSA1");
283 
284     qMsaRow.reset(true);
285     qMsaRow.bindDataId(1, msaId2);
286     qint64 msa2Rows = qMsaRow.selectInt64();
287     CHECK_EQUAL(1, msa2Rows, "number of rows in MSA2");
288 
289     // "MsaRowGap"
290     SQLiteReadQuery qMsaRowGap("SELECT COUNT(*) FROM MsaRowGap WHERE msa = ?1", sqliteDbi->getDbRef(), os);
291     qMsaRowGap.bindDataId(1, msaId);
292     qint64 msa1Gaps = qMsaRowGap.selectInt64();
293     CHECK_EQUAL(0, msa1Gaps, "number of gaps in MSA1 rows");
294 
295     qMsaRowGap.reset(true);
296     qMsaRowGap.bindDataId(1, msaId2);
297     qint64 msa2Gaps = qMsaRowGap.selectInt64();
298     CHECK_EQUAL(1, msa2Gaps, "number of gaps in MSA2 rows");
299 
300     // "Sequence"
301     SQLiteReadQuery qSeq("SELECT COUNT(*) FROM Sequence WHERE object = ?1", sqliteDbi->getDbRef(), os);
302     qSeq.bindDataId(1, seq1.id);
303     qint64 msa1seq1 = qSeq.selectInt64();
304     CHECK_EQUAL(0, msa1seq1, "seq1 of msa1");
305 
306     qSeq.reset(true);
307     qSeq.bindDataId(1, seq2.id);
308     qint64 msa1seq2 = qSeq.selectInt64();
309     CHECK_EQUAL(0, msa1seq2, "seq2 of msa1");
310 
311     qSeq.reset(true);
312     qSeq.bindDataId(1, al2Seq.id);
313     qint64 msa2seq = qSeq.selectInt64();
314     CHECK_EQUAL(1, msa2seq, "seq of msa2");
315 
316     // "Msa"
317     SQLiteReadQuery qMsa("SELECT COUNT(*) FROM Msa WHERE object = ?1", sqliteDbi->getDbRef(), os);
318     qMsa.bindDataId(1, msaId);
319     qint64 msa1records = qMsa.selectInt64();
320     CHECK_EQUAL(0, msa1records, "number of MSA1 records");
321 
322     qMsa.reset(true);
323     qMsa.bindDataId(1, msaId2);
324     qint64 msa2records = qMsa.selectInt64();
325     CHECK_EQUAL(1, msa2records, "number of MSA2 records");
326 
327     // "Object"
328     SQLiteReadQuery qObj("SELECT COUNT(*) FROM Object WHERE id = ?1", sqliteDbi->getDbRef(), os);
329     qObj.bindDataId(1, msaId);
330     qint64 msa1objects = qObj.selectInt64();
331     CHECK_EQUAL(0, msa1objects, "number of MSA1 objects");
332 
333     qObj.reset(true);
334     qObj.bindDataId(1, msaId2);
335     qint64 msa2objects = qObj.selectInt64();
336     CHECK_EQUAL(1, msa2objects, "number of MSA2 objects");
337 
338     // Remove the second alignment
339     sqliteObjectDbi->removeObject(msaId2, os);
340 }
341 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,setTrackModType)342 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, setTrackModType) {
343     U2OpStatusImpl os;
344     U2MsaDbi *msaDbi = SQLiteObjectDbiTestData::getMsaDbi();
345     SQLiteObjectDbi *objectDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
346 
347     // Create alignment 1
348     U2DataId msaId1 = msaDbi->createMsaObject("", "Test name 1", BaseDNAAlphabetIds::NUCL_DNA_DEFAULT(), os);
349     CHECK_NO_ERROR(os);
350     Utils::addRow(msaDbi->getRootDbi(), msaId1, "1", "ACGTACGT", QList<U2MsaGap>(), os);
351     CHECK_NO_ERROR(os);
352     QList<U2MsaRow> rows1 = msaDbi->getRows(msaId1, os);
353     CHECK_NO_ERROR(os);
354 
355     // Create alignment 2
356     U2DataId msaId2 = msaDbi->createMsaObject("", "Test name 2", BaseDNAAlphabetIds::NUCL_DNA_DEFAULT(), os);
357     CHECK_NO_ERROR(os);
358     Utils::addRow(msaDbi->getRootDbi(), msaId2, "2", "CCCCCCC", QList<U2MsaGap>(), os);
359     CHECK_NO_ERROR(os);
360     QList<U2MsaRow> rows2 = msaDbi->getRows(msaId2, os);
361     CHECK_NO_ERROR(os);
362 
363     // Change mod track 1
364     objectDbi->setTrackModType(msaId1, TrackOnUpdate, os);
365     CHECK_NO_ERROR(os);
366 
367     U2TrackModType newType1_1 = objectDbi->getTrackModType(rows1[0].sequenceId, os);
368     CHECK_EQUAL(TrackOnUpdate, newType1_1, "new mod track type 1_1");
369 
370     U2TrackModType newType1_2 = objectDbi->getTrackModType(rows2[0].sequenceId, os);
371     CHECK_EQUAL(NoTrack, newType1_2, "new mod track type 1_2");
372 
373     // Change mod track 2
374     objectDbi->setTrackModType(msaId1, NoTrack, os);
375     CHECK_NO_ERROR(os);
376     objectDbi->setTrackModType(msaId2, TrackOnUpdate, os);
377     CHECK_NO_ERROR(os);
378 
379     U2TrackModType newType2_1 = objectDbi->getTrackModType(rows1[0].sequenceId, os);
380     CHECK_EQUAL(NoTrack, newType2_1, "new mod track type 2_1");
381 
382     U2TrackModType newType2_2 = objectDbi->getTrackModType(rows2[0].sequenceId, os);
383     CHECK_EQUAL(TrackOnUpdate, newType2_2, "new mod track type 2_2");
384 }
385 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_noTrack)386 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_noTrack) {
387     U2OpStatusImpl os;
388     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
389 
390     // Create test msa
391     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(false, os);
392     CHECK_NO_ERROR(os);
393 
394     // Do action
395     SQLiteObjectDbiTestData::addTestRow(msaId, os);
396     CHECK_NO_ERROR(os);
397 
398     // Verify canUndo/canRedo
399     bool undoState = objDbi->canUndo(msaId, os);
400     CHECK_NO_ERROR(os);
401     bool redoState = objDbi->canRedo(msaId, os);
402     CHECK_NO_ERROR(os);
403     CHECK_FALSE(undoState, "undo state");
404     CHECK_FALSE(redoState, "redo state");
405 }
406 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_noAction)407 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_noAction) {
408     U2OpStatusImpl os;
409     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
410 
411     // Create test msa
412     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
413     CHECK_NO_ERROR(os);
414 
415     // Verify canUndo/canRedo
416     bool undoState = objDbi->canUndo(msaId, os);
417     CHECK_NO_ERROR(os);
418     bool redoState = objDbi->canRedo(msaId, os);
419     CHECK_NO_ERROR(os);
420     CHECK_FALSE(undoState, "undo state");
421     CHECK_FALSE(redoState, "redo state");
422 }
423 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_lastState)424 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_lastState) {
425     U2OpStatusImpl os;
426     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
427 
428     // Create test msa
429     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
430     CHECK_NO_ERROR(os);
431 
432     // Do action
433     SQLiteObjectDbiTestData::addTestRow(msaId, os);
434     CHECK_NO_ERROR(os);
435 
436     // Verify canUndo/canRedo
437     bool undoState = objDbi->canUndo(msaId, os);
438     CHECK_NO_ERROR(os);
439     bool redoState = objDbi->canRedo(msaId, os);
440     CHECK_NO_ERROR(os);
441     CHECK_TRUE(undoState, "undo state");
442     CHECK_FALSE(redoState, "redo state");
443 }
444 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_firstState)445 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_firstState) {
446     U2OpStatusImpl os;
447     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
448 
449     // Create test msa
450     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
451     CHECK_NO_ERROR(os);
452 
453     // Do action, undo
454     SQLiteObjectDbiTestData::addTestRow(msaId, os);
455     CHECK_NO_ERROR(os);
456     objDbi->undo(msaId, os);
457     CHECK_NO_ERROR(os);
458 
459     // Verify canUndo/canRedo
460     bool undoState = objDbi->canUndo(msaId, os);
461     CHECK_NO_ERROR(os);
462     bool redoState = objDbi->canRedo(msaId, os);
463     CHECK_NO_ERROR(os);
464     CHECK_FALSE(undoState, "undo state");
465     CHECK_TRUE(redoState, "redo state");
466 }
467 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_midState)468 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_midState) {
469     U2OpStatusImpl os;
470     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
471 
472     // Create test msa
473     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
474     CHECK_NO_ERROR(os);
475 
476     // Do action twice, undo
477     SQLiteObjectDbiTestData::addTestRow(msaId, os);
478     CHECK_NO_ERROR(os);
479     SQLiteObjectDbiTestData::addTestRow(msaId, os);
480     CHECK_NO_ERROR(os);
481 
482     objDbi->undo(msaId, os);
483     CHECK_NO_ERROR(os);
484 
485     // Verify canUndo/canRedo
486     bool undoState = objDbi->canUndo(msaId, os);
487     CHECK_NO_ERROR(os);
488     bool redoState = objDbi->canRedo(msaId, os);
489     CHECK_NO_ERROR(os);
490     CHECK_TRUE(undoState, "undo state");
491     CHECK_TRUE(redoState, "redo state");
492 }
493 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,canUndoRedo_oneUserStep)494 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, canUndoRedo_oneUserStep) {
495     U2OpStatusImpl os;
496     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
497 
498     // Create test msa
499     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
500     CHECK_NO_ERROR(os);
501 
502     // Do action twice inside one UserStep
503     {
504         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
505         CHECK_NO_ERROR(os);
506         Q_UNUSED(userStep);
507 
508         SQLiteObjectDbiTestData::addTestRow(msaId, os);
509         CHECK_NO_ERROR(os);
510         SQLiteObjectDbiTestData::addTestRow(msaId, os);
511         CHECK_NO_ERROR(os);
512     }
513 
514     // Verify canUndo/canRedo
515     bool undoState = objDbi->canUndo(msaId, os);
516     CHECK_NO_ERROR(os);
517     bool redoState = objDbi->canRedo(msaId, os);
518     CHECK_NO_ERROR(os);
519     CHECK_TRUE(undoState, "undo state before undo");
520     CHECK_FALSE(redoState, "redo state before undo");
521 
522     // Undo UserStep
523     objDbi->undo(msaId, os);
524     CHECK_NO_ERROR(os);
525 
526     // Verify canUndo/canRedo
527     undoState = objDbi->canUndo(msaId, os);
528     CHECK_NO_ERROR(os);
529     redoState = objDbi->canRedo(msaId, os);
530     CHECK_NO_ERROR(os);
531     CHECK_FALSE(undoState, "undo state after undo");
532     CHECK_TRUE(redoState, "redo state after undo");
533 }
534 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_user3Multi)535 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_user3Multi) {
536     U2OpStatusImpl os;
537     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
538     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
539 
540     // Create test msa
541     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
542     CHECK_NO_ERROR(os);
543 
544     // Get msa version
545     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
546     CHECK_NO_ERROR(os);
547 
548     // Do 3 actions
549     {
550         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
551         CHECK_NO_ERROR(os);
552         Q_UNUSED(userStep);
553 
554         SQLiteObjectDbiTestData::addTestRow(msaId, os);
555         CHECK_NO_ERROR(os);
556 
557         SQLiteObjectDbiTestData::addTestRow(msaId, os);
558         CHECK_NO_ERROR(os);
559 
560         SQLiteObjectDbiTestData::addTestRow(msaId, os);
561         CHECK_NO_ERROR(os);
562     }
563 
564     // Verify version in the userModStep
565     SQLiteReadQuery qVersion("SELECT version FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
566     qVersion.bindDataId(1, msaId);
567     if (qVersion.step()) {
568         qint64 userStepVersion = qVersion.getInt64(0);
569         CHECK_EQUAL(msaVersion, userStepVersion, "version in user step");
570     } else {
571         CHECK_TRUE(false, "Failed to get userModStep version!");
572     }
573     CHECK_NO_ERROR(os);
574 
575     // Verify msa version
576     qint64 msaVersionAftAct = objDbi->getObjectVersion(msaId, os);
577     CHECK_NO_ERROR(os);
578     CHECK_EQUAL(msaVersion + 3, msaVersionAftAct, "msa version after 3 actions");
579 
580     // Verify canUndo/canRedo
581     bool undoState = objDbi->canUndo(msaId, os);
582     CHECK_NO_ERROR(os);
583     bool redoState = objDbi->canRedo(msaId, os);
584     CHECK_NO_ERROR(os);
585     CHECK_TRUE(undoState, "undo state before undo");
586     CHECK_FALSE(redoState, "redo state before undo");
587 
588     // Undo
589     objDbi->undo(msaId, os);
590     CHECK_NO_ERROR(os);
591 
592     // Verify msa version
593     qint64 msaVersionAftUndo = objDbi->getObjectVersion(msaId, os);
594     CHECK_NO_ERROR(os);
595     CHECK_EQUAL(msaVersion, msaVersionAftUndo, "msa version after undo");
596 
597     // Verify canUndo/canRedo
598     undoState = objDbi->canUndo(msaId, os);
599     CHECK_NO_ERROR(os);
600     redoState = objDbi->canRedo(msaId, os);
601     CHECK_NO_ERROR(os);
602     CHECK_FALSE(undoState, "undo state after undo");
603     CHECK_TRUE(redoState, "redo state after undo");
604 
605     // Redo
606     objDbi->redo(msaId, os);
607     CHECK_NO_ERROR(os);
608 
609     // Verify msa version
610     qint64 msaVersionAftRedo = objDbi->getObjectVersion(msaId, os);
611     CHECK_NO_ERROR(os);
612     CHECK_EQUAL(msaVersion + 3, msaVersionAftRedo, "msa version after redo");
613 
614     // Verify canUndo/canRedo
615     undoState = objDbi->canUndo(msaId, os);
616     CHECK_NO_ERROR(os);
617     redoState = objDbi->canRedo(msaId, os);
618     CHECK_NO_ERROR(os);
619     CHECK_TRUE(undoState, "undo state after redo");
620     CHECK_FALSE(redoState, "redo state after redo");
621 }
622 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_actionAfterUndo)623 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_actionAfterUndo) {
624     U2OpStatusImpl os;
625     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
626     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
627 
628     // Create test msa
629     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
630     CHECK_NO_ERROR(os);
631 
632     // Get msa version
633     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
634     CHECK_NO_ERROR(os);
635 
636     // Do 2 actions
637     {
638         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
639         CHECK_NO_ERROR(os);
640         Q_UNUSED(userStep);
641 
642         SQLiteObjectDbiTestData::addTestRow(msaId, os);
643         CHECK_NO_ERROR(os);
644 
645         SQLiteObjectDbiTestData::addTestRow(msaId, os);
646         CHECK_NO_ERROR(os);
647     }
648 
649     // Undo
650     objDbi->undo(msaId, os);
651     CHECK_NO_ERROR(os);
652 
653     // Do action
654     SQLiteObjectDbiTestData::addTestRow(msaId, os);
655 
656     // Check there is no obsolete steps
657     SQLiteReadQuery qUser("SELECT COUNT(*) FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
658     qUser.bindDataId(1, msaId);
659     if (qUser.step()) {
660         CHECK_EQUAL(1, qUser.getInt64(0), "number of user steps");
661     } else {
662         CHECK_TRUE(false, "Unexpected error!");
663     }
664     CHECK_NO_ERROR(os);
665 
666     SQLiteReadQuery qSingle("SELECT COUNT(*) FROM SingleModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
667     qSingle.bindDataId(1, msaId);
668     if (qSingle.step()) {
669         CHECK_EQUAL(1, qSingle.getInt64(0), "number of single steps");
670     } else {
671         CHECK_TRUE(false, "Unexpected error!");
672     }
673     CHECK_NO_ERROR(os);
674 
675     // Undo
676     objDbi->undo(msaId, os);
677     CHECK_NO_ERROR(os);
678 
679     // Verify msa version
680     qint64 msaVersionAfterActUndo = objDbi->getObjectVersion(msaId, os);
681     CHECK_NO_ERROR(os);
682     CHECK_EQUAL(msaVersion, msaVersionAfterActUndo, "msa version after undo, action and undo");
683 
684     // Verify canUndo/canRedo
685     bool undoState = objDbi->canUndo(msaId, os);
686     CHECK_NO_ERROR(os);
687     bool redoState = objDbi->canRedo(msaId, os);
688     CHECK_NO_ERROR(os);
689     CHECK_FALSE(undoState, "undo state after undo, action and undo");
690     CHECK_TRUE(redoState, "redo state after undo, action and undo");
691 
692     // Redo
693     objDbi->redo(msaId, os);
694     CHECK_NO_ERROR(os);
695 
696     // Verify msa version
697     qint64 msaVersionAfterActRedo = objDbi->getObjectVersion(msaId, os);
698     CHECK_NO_ERROR(os);
699     CHECK_EQUAL(msaVersion + 1, msaVersionAfterActRedo, "msa version after undo, action and undo/redo");
700 
701     // Verify canUndo/canRedo
702     undoState = objDbi->canUndo(msaId, os);
703     CHECK_NO_ERROR(os);
704     redoState = objDbi->canRedo(msaId, os);
705     CHECK_NO_ERROR(os);
706     CHECK_TRUE(undoState, "undo state after undo, action and undo/redo");
707     CHECK_FALSE(redoState, "redo state after undo, action and undo/redo");
708 }
709 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_actionUndoActionUndo1)710 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_actionUndoActionUndo1) {
711     U2OpStatusImpl os;
712     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
713     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
714 
715     // Create test msa
716     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
717     CHECK_NO_ERROR(os);
718 
719     // Get msa version
720     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
721     CHECK_NO_ERROR(os);
722 
723     // Do an action
724     {
725         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
726         CHECK_NO_ERROR(os);
727         Q_UNUSED(userStep);
728 
729         SQLiteObjectDbiTestData::addTestRow(msaId, os);
730         CHECK_NO_ERROR(os);
731     }
732 
733     // Undo
734     objDbi->undo(msaId, os);
735     CHECK_NO_ERROR(os);
736 
737     // Do an action
738     {
739         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
740         CHECK_NO_ERROR(os);
741         Q_UNUSED(userStep);
742 
743         SQLiteObjectDbiTestData::addTestRow(msaId, os);
744         CHECK_NO_ERROR(os);
745     }
746 
747     // Check there is no obsolete steps
748     SQLiteReadQuery qUser("SELECT COUNT(*) FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
749     qUser.bindDataId(1, msaId);
750     if (qUser.step()) {
751         CHECK_EQUAL(1, qUser.getInt64(0), "number of user steps");
752     } else {
753         CHECK_TRUE(false, "Unexpected error!");
754     }
755     CHECK_NO_ERROR(os);
756 
757     SQLiteReadQuery qSingle("SELECT COUNT(*) FROM SingleModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
758     qSingle.bindDataId(1, msaId);
759     if (qSingle.step()) {
760         CHECK_EQUAL(1, qSingle.getInt64(0), "number of single steps");
761     } else {
762         CHECK_TRUE(false, "Unexpected error!");
763     }
764     CHECK_NO_ERROR(os);
765 
766     // Undo
767     objDbi->undo(msaId, os);
768     CHECK_NO_ERROR(os);
769 
770     // Verify msa version
771     qint64 msaVersionAfter = objDbi->getObjectVersion(msaId, os);
772     CHECK_NO_ERROR(os);
773     CHECK_EQUAL(msaVersion, msaVersionAfter, "msa version after action, undo, action, undo");
774 
775     // Verify canUndo/canRedo
776     bool undoState = objDbi->canUndo(msaId, os);
777     CHECK_NO_ERROR(os);
778     bool redoState = objDbi->canRedo(msaId, os);
779     CHECK_NO_ERROR(os);
780     CHECK_FALSE(undoState, "undo state after undo, action and undo/redo");
781     CHECK_TRUE(redoState, "redo state after undo, action and undo/redo");
782 }
783 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_actionUndoActionUndo2)784 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_actionUndoActionUndo2) {
785     U2OpStatusImpl os;
786     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
787     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
788 
789     // Create test msa
790     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
791     CHECK_NO_ERROR(os);
792 
793     // Get msa version
794     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
795     CHECK_NO_ERROR(os);
796 
797     // Do an action
798     SQLiteObjectDbiTestData::addTestRow(msaId, os);
799 
800     // Undo
801     objDbi->undo(msaId, os);
802     CHECK_NO_ERROR(os);
803 
804     // Do an action
805     SQLiteObjectDbiTestData::addTestRow(msaId, os);
806 
807     // Check there is no obsolete steps
808     SQLiteReadQuery qUser("SELECT COUNT(*) FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
809     qUser.bindDataId(1, msaId);
810     if (qUser.step()) {
811         CHECK_EQUAL(1, qUser.getInt64(0), "number of user steps");
812     } else {
813         CHECK_TRUE(false, "Unexpected error!");
814     }
815     CHECK_NO_ERROR(os);
816 
817     SQLiteReadQuery qSingle("SELECT COUNT(*) FROM SingleModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
818     qSingle.bindDataId(1, msaId);
819     if (qSingle.step()) {
820         CHECK_EQUAL(1, qSingle.getInt64(0), "number of single steps");
821     } else {
822         CHECK_TRUE(false, "Unexpected error!");
823     }
824     CHECK_NO_ERROR(os);
825 
826     // Undo
827     objDbi->undo(msaId, os);
828     CHECK_NO_ERROR(os);
829 
830     // Verify msa version
831     qint64 msaVersionAfter = objDbi->getObjectVersion(msaId, os);
832     CHECK_NO_ERROR(os);
833     CHECK_EQUAL(msaVersion, msaVersionAfter, "msa version after action, undo, action, undo");
834 
835     // Verify canUndo/canRedo
836     bool undoState = objDbi->canUndo(msaId, os);
837     CHECK_NO_ERROR(os);
838     bool redoState = objDbi->canRedo(msaId, os);
839     CHECK_NO_ERROR(os);
840     CHECK_FALSE(undoState, "undo state after undo, action and undo/redo");
841     CHECK_TRUE(redoState, "redo state after undo, action and undo/redo");
842 }
843 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_actionUndoActionUndo3)844 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_actionUndoActionUndo3) {
845     U2OpStatusImpl os;
846     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
847     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
848 
849     // Create test msa
850     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
851     CHECK_NO_ERROR(os);
852 
853     // Get msa version
854     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
855     CHECK_NO_ERROR(os);
856 
857     // Do an action
858     SQLiteObjectDbiTestData::addTestRow(msaId, os);
859 
860     // Undo
861     objDbi->undo(msaId, os);
862     CHECK_NO_ERROR(os);
863 
864     // Do an action
865     {
866         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
867         CHECK_NO_ERROR(os);
868         Q_UNUSED(userStep);
869 
870         SQLiteObjectDbiTestData::addTestRow(msaId, os);
871         CHECK_NO_ERROR(os);
872     }
873 
874     // Check there is no obsolete steps
875     SQLiteReadQuery qUser("SELECT COUNT(*) FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
876     qUser.bindDataId(1, msaId);
877     if (qUser.step()) {
878         CHECK_EQUAL(1, qUser.getInt64(0), "number of user steps");
879     } else {
880         CHECK_TRUE(false, "Unexpected error!");
881     }
882     CHECK_NO_ERROR(os);
883 
884     SQLiteReadQuery qSingle("SELECT COUNT(*) FROM SingleModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
885     qSingle.bindDataId(1, msaId);
886     if (qSingle.step()) {
887         CHECK_EQUAL(1, qSingle.getInt64(0), "number of single steps");
888     } else {
889         CHECK_TRUE(false, "Unexpected error!");
890     }
891     CHECK_NO_ERROR(os);
892 
893     // Undo
894     objDbi->undo(msaId, os);
895     CHECK_NO_ERROR(os);
896 
897     // Verify msa version
898     qint64 msaVersionAfter = objDbi->getObjectVersion(msaId, os);
899     CHECK_NO_ERROR(os);
900     CHECK_EQUAL(msaVersion, msaVersionAfter, "msa version after action, undo, action, undo");
901 
902     // Verify canUndo/canRedo
903     bool undoState = objDbi->canUndo(msaId, os);
904     CHECK_NO_ERROR(os);
905     bool redoState = objDbi->canRedo(msaId, os);
906     CHECK_NO_ERROR(os);
907     CHECK_FALSE(undoState, "undo state after undo, action and undo/redo");
908     CHECK_TRUE(redoState, "redo state after undo, action and undo/redo");
909 }
910 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_actionUndoActionUndo4)911 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_actionUndoActionUndo4) {
912     U2OpStatusImpl os;
913     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
914     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
915 
916     // Create test msa
917     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
918     CHECK_NO_ERROR(os);
919 
920     // Get msa version
921     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
922     CHECK_NO_ERROR(os);
923 
924     // Do an action
925     {
926         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
927         CHECK_NO_ERROR(os);
928         Q_UNUSED(userStep);
929 
930         SQLiteObjectDbiTestData::addTestRow(msaId, os);
931         CHECK_NO_ERROR(os);
932     }
933 
934     // Undo
935     objDbi->undo(msaId, os);
936     CHECK_NO_ERROR(os);
937 
938     // Do an action
939     SQLiteObjectDbiTestData::addTestRow(msaId, os);
940 
941     // Check there is no obsolete steps
942     SQLiteReadQuery qUser("SELECT COUNT(*) FROM UserModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
943     qUser.bindDataId(1, msaId);
944     if (qUser.step()) {
945         CHECK_EQUAL(1, qUser.getInt64(0), "number of user steps");
946     } else {
947         CHECK_TRUE(false, "Unexpected error!");
948     }
949     CHECK_NO_ERROR(os);
950 
951     SQLiteReadQuery qSingle("SELECT COUNT(*) FROM SingleModStep WHERE object = ?1", sqliteDbi->getDbRef(), os);
952     qSingle.bindDataId(1, msaId);
953     if (qSingle.step()) {
954         CHECK_EQUAL(1, qSingle.getInt64(0), "number of single steps");
955     } else {
956         CHECK_TRUE(false, "Unexpected error!");
957     }
958     CHECK_NO_ERROR(os);
959 
960     // Undo
961     objDbi->undo(msaId, os);
962     CHECK_NO_ERROR(os);
963 
964     // Verify msa version
965     qint64 msaVersionAfter = objDbi->getObjectVersion(msaId, os);
966     CHECK_NO_ERROR(os);
967     CHECK_EQUAL(msaVersion, msaVersionAfter, "msa version after action, undo, action, undo");
968 
969     // Verify canUndo/canRedo
970     bool undoState = objDbi->canUndo(msaId, os);
971     CHECK_NO_ERROR(os);
972     bool redoState = objDbi->canRedo(msaId, os);
973     CHECK_NO_ERROR(os);
974     CHECK_FALSE(undoState, "undo state after undo, action and undo/redo");
975     CHECK_TRUE(redoState, "redo state after undo, action and undo/redo");
976 }
977 
IMPLEMENT_TEST(SQLiteObjectDbiUnitTests,commonUndoRedo_user3Single6)978 IMPLEMENT_TEST(SQLiteObjectDbiUnitTests, commonUndoRedo_user3Single6) {
979     U2OpStatusImpl os;
980     U2ObjectDbi *objDbi = SQLiteObjectDbiTestData::getSQLiteObjectDbi();
981     SQLiteDbi *sqliteDbi = SQLiteObjectDbiTestData::getSQLiteDbi();
982 
983     // Create test msa
984     U2DataId msaId = SQLiteObjectDbiTestData::createTestMsa(true, os);
985     CHECK_NO_ERROR(os);
986 
987     // Get msa version
988     qint64 msaVersion = objDbi->getObjectVersion(msaId, os);
989     CHECK_NO_ERROR(os);
990 
991     // User step 1
992     {
993         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
994         CHECK_NO_ERROR(os);
995         Q_UNUSED(userStep);
996 
997         SQLiteObjectDbiTestData::addTestRow(msaId, os);  // multi/single step 1
998         CHECK_NO_ERROR(os);
999 
1000         SQLiteObjectDbiTestData::addTestRow(msaId, os);  // multi/single step 2
1001         CHECK_NO_ERROR(os);
1002     }
1003 
1004     // User step 2
1005     SQLiteObjectDbiTestData::addTestRow(msaId, os);  // multi/single step 3
1006 
1007     // Verify version
1008     qint64 msaVersionAfterUser1 = msaVersion + 2;  // verified in another test
1009     qint64 msaVersionAfterUser2 = objDbi->getObjectVersion(msaId, os);
1010     CHECK_NO_ERROR(os);
1011     CHECK_EQUAL(msaVersionAfterUser1 + 1, msaVersionAfterUser2, "msa version after user step 2");
1012 
1013     // Verify canUndo/canRedo
1014     bool undoState = objDbi->canUndo(msaId, os);
1015     CHECK_NO_ERROR(os);
1016     bool redoState = objDbi->canRedo(msaId, os);
1017     CHECK_NO_ERROR(os);
1018     CHECK_TRUE(undoState, "undo state after user step 2");
1019     CHECK_FALSE(redoState, "redo state after user step 2");
1020 
1021     // User step 3
1022     U2Sequence seq;
1023     seq.alphabet = BaseDNAAlphabetIds::NUCL_DNA_DEFAULT();
1024     seq.circular = false;
1025     seq.trackModType = NoTrack;
1026     seq.visualName = "Test sequence";
1027     sqliteDbi->getSQLiteSequenceDbi()->createSequenceObject(seq, "", os, U2DbiObjectRank_TopLevel);
1028     CHECK_NO_ERROR(os);
1029 
1030     U2MsaRow row;
1031     row.sequenceId = seq.id;
1032     row.gstart = 0;
1033     row.gend = 0;
1034     row.length = 0;
1035 
1036     {
1037         U2UseCommonUserModStep userStep(objDbi->getRootDbi(), msaId, os);
1038         CHECK_NO_ERROR(os);
1039         Q_UNUSED(userStep);
1040 
1041         sqliteDbi->getMsaDbi()->addRow(msaId, -1, row, os);  // multi/single step 4
1042         CHECK_NO_ERROR(os);
1043 
1044         sqliteDbi->getMsaDbi()->updateRowContent(msaId, row.rowId, "ACGT", QList<U2MsaGap>(), os);  // multi step 5, single steps 5-6
1045         CHECK_NO_ERROR(os);
1046     }
1047 
1048     // Verify version
1049     qint64 msaVersionAfterUser3 = objDbi->getObjectVersion(msaId, os);
1050     CHECK_NO_ERROR(os);
1051     CHECK_EQUAL(msaVersionAfterUser2 + 2, msaVersionAfterUser3, "msa version after user step 3");
1052 
1053     // Verify canUndo/canRedo
1054     undoState = objDbi->canUndo(msaId, os);
1055     CHECK_NO_ERROR(os);
1056     redoState = objDbi->canRedo(msaId, os);
1057     CHECK_NO_ERROR(os);
1058     CHECK_TRUE(undoState, "undo state after user step 3");
1059     CHECK_FALSE(redoState, "redo state after user step 3");
1060 
1061     // Undo 1 (to user step 2)
1062     objDbi->undo(msaId, os);
1063     CHECK_NO_ERROR(os);
1064 
1065     // Verify version
1066     qint64 msaVersionAfterUndo1 = objDbi->getObjectVersion(msaId, os);
1067     CHECK_NO_ERROR(os);
1068     CHECK_EQUAL(msaVersionAfterUser2, msaVersionAfterUndo1, "msa version after undo 1");
1069 
1070     // Verify canUndo/canRedo
1071     undoState = objDbi->canUndo(msaId, os);
1072     CHECK_NO_ERROR(os);
1073     redoState = objDbi->canRedo(msaId, os);
1074     CHECK_NO_ERROR(os);
1075     CHECK_TRUE(undoState, "undo state after undo 1");
1076     CHECK_TRUE(redoState, "redo state after undo 1");
1077 
1078     // Undo 2 (to user step 1)
1079     objDbi->undo(msaId, os);
1080     CHECK_NO_ERROR(os);
1081 
1082     // Verify version
1083     qint64 msaVersionAfterUndo2 = objDbi->getObjectVersion(msaId, os);
1084     CHECK_NO_ERROR(os);
1085     CHECK_EQUAL(msaVersionAfterUser1, msaVersionAfterUndo2, "msa version after undo 2");
1086 
1087     // Verify canUndo/canRedo
1088     undoState = objDbi->canUndo(msaId, os);
1089     CHECK_NO_ERROR(os);
1090     redoState = objDbi->canRedo(msaId, os);
1091     CHECK_NO_ERROR(os);
1092     CHECK_TRUE(undoState, "undo state after undo 2");
1093     CHECK_TRUE(redoState, "redo state after undo 2");
1094 
1095     // Undo 3 (to original)
1096     objDbi->undo(msaId, os);
1097     CHECK_NO_ERROR(os);
1098 
1099     // Verify version
1100     qint64 msaVersionAfterUndo3 = objDbi->getObjectVersion(msaId, os);
1101     CHECK_NO_ERROR(os);
1102     CHECK_EQUAL(msaVersion, msaVersionAfterUndo3, "msa version after undo 3");
1103 
1104     // Verify canUndo/canRedo
1105     undoState = objDbi->canUndo(msaId, os);
1106     CHECK_NO_ERROR(os);
1107     redoState = objDbi->canRedo(msaId, os);
1108     CHECK_NO_ERROR(os);
1109     CHECK_FALSE(undoState, "undo state after undo 3");
1110     CHECK_TRUE(redoState, "redo state after undo 3");
1111 
1112     // Redo 1 (to user step 1)
1113     objDbi->redo(msaId, os);
1114     CHECK_NO_ERROR(os);
1115 
1116     // Verify version
1117     qint64 msaVersionAfterRedo1 = objDbi->getObjectVersion(msaId, os);
1118     CHECK_NO_ERROR(os);
1119     CHECK_EQUAL(msaVersionAfterUser1, msaVersionAfterRedo1, "msa version after redo 1");
1120 
1121     // Verify canUndo/canRedo
1122     undoState = objDbi->canUndo(msaId, os);
1123     CHECK_NO_ERROR(os);
1124     redoState = objDbi->canRedo(msaId, os);
1125     CHECK_NO_ERROR(os);
1126     CHECK_TRUE(undoState, "undo state after redo 1");
1127     CHECK_TRUE(redoState, "redo state after redo 1");
1128 
1129     // Redo 2 (to user step 2)
1130     objDbi->redo(msaId, os);
1131     CHECK_NO_ERROR(os);
1132 
1133     // Verify version
1134     qint64 msaVersionAfterRedo2 = objDbi->getObjectVersion(msaId, os);
1135     CHECK_NO_ERROR(os);
1136     CHECK_EQUAL(msaVersionAfterUser2, msaVersionAfterRedo2, "msa version after redo 2");
1137 
1138     // Verify canUndo/canRedo
1139     undoState = objDbi->canUndo(msaId, os);
1140     CHECK_NO_ERROR(os);
1141     redoState = objDbi->canRedo(msaId, os);
1142     CHECK_NO_ERROR(os);
1143     CHECK_TRUE(undoState, "undo state after redo 2");
1144     CHECK_TRUE(redoState, "redo state after redo 2");
1145 
1146     // Redo 3 (to user step 3)
1147     objDbi->redo(msaId, os);
1148     CHECK_NO_ERROR(os);
1149 
1150     // Verify version
1151     qint64 msaVersionAfterRedo3 = objDbi->getObjectVersion(msaId, os);
1152     CHECK_NO_ERROR(os);
1153     CHECK_EQUAL(msaVersionAfterUser3, msaVersionAfterRedo3, "msa version after redo 3");
1154 
1155     // Verify canUndo/canRedo
1156     undoState = objDbi->canUndo(msaId, os);
1157     CHECK_NO_ERROR(os);
1158     redoState = objDbi->canRedo(msaId, os);
1159     CHECK_NO_ERROR(os);
1160     CHECK_TRUE(undoState, "undo state after redo 3");
1161     CHECK_FALSE(redoState, "redo state after redo 3");
1162 }
1163 
1164 }  // namespace U2
1165