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 "RepeatFinderTests.h"
23 
24 #include <QFile>
25 
26 #include <U2Algorithm/SArrayBasedFindTask.h>
27 #include <U2Algorithm/SArrayIndex.h>
28 
29 #include <U2Core/AppContext.h>
30 #include <U2Core/BaseDocumentFormats.h>
31 #include <U2Core/DNAAlphabet.h>
32 #include <U2Core/DNASequenceObject.h>
33 #include <U2Core/DocumentModel.h>
34 #include <U2Core/U2OpStatusUtils.h>
35 #include <U2Core/U2SafePoints.h>
36 
37 #include "FindRepeatsTask.h"
38 #include "RF_SArray_TandemFinder.h"
39 
40 namespace U2 {
41 
42 #define SEQ_ATTR "seq"
43 #define SEQ2_ATTR "seq2"
44 #define REG_ATTR "reg"
45 #define W_ATTR "w"
46 #define C_ATTR "c"
47 #define INV_ATTR "inverted"
48 #define MIND_ATTR "mind"
49 #define MAXD_ATTR "maxd"
50 #define RESULT_ATTR "expected_result"
51 #define REFL_ATTR "reflect"
52 #define EXCL_ATTR "exclude"
53 #define SEQUENCE "sequence"
54 #define QUERY "query"
55 #define USE_BITMASK "bit-mask"
56 #define MISMATCHES "mismatches"
57 #define ALG_ATTR "alg"
58 
parseRegion(const QString & n,const QDomElement & el)59 U2Region GTest_FindSingleSequenceRepeatsTask::parseRegion(const QString &n, const QDomElement &el) {
60     U2Region res;
61     QString v = el.attribute(n);
62     if (v.isEmpty()) {
63         return res;
64     }
65     int idx = v.indexOf("..");
66     if (idx == -1 || idx + 2 >= v.length()) {
67         return res;
68     }
69     QString v1 = v.left(idx);
70     QString v2 = v.mid(idx + 2);
71     int startPos = v1.toInt();
72     int endPos = v2.toInt();
73     if (startPos >= 0 && endPos > startPos) {
74         res.startPos = startPos - 1;
75         res.length = endPos - startPos + 1;
76     }
77     return res;
78 }
79 
init(XMLTestFormat *,const QDomElement & el)80 void GTest_FindSingleSequenceRepeatsTask::init(XMLTestFormat *, const QDomElement &el) {
81     seq = el.attribute(SEQ_ATTR);
82     if (seq.isEmpty()) {
83         stateInfo.setError(QString("Value not found '%1'").arg(SEQ_ATTR));
84         return;
85     }
86     seq2 = el.attribute(SEQ2_ATTR);
87     if (seq2.isEmpty()) {
88         seq2 = seq;
89     }
90     region = parseRegion(REG_ATTR, el);
91 
92     QString algStr = el.attribute(ALG_ATTR);
93 
94     if (algStr == "suffix") {
95         alg = RFAlgorithm_Suffix;
96     } else {
97         if (algStr == "diagonal") {
98             alg = RFAlgorithm_Diagonal;
99         } else {
100             alg = RFAlgorithm_Auto;
101         }
102     }
103 
104     minD = el.attribute(MIND_ATTR, "-1").toInt();
105     maxD = el.attribute(MAXD_ATTR, "-1").toInt();
106 
107     QString wStr = el.attribute(W_ATTR);
108     if (wStr.isEmpty()) {
109         stateInfo.setError(QString("Value not found '%1'").arg(W_ATTR));
110         return;
111     }
112     w = wStr.toInt();
113     if (w < 2) {
114         stateInfo.setError(QString("Illegal value for '%1': %2").arg(W_ATTR).arg(wStr));
115         return;
116     }
117 
118     QString cStr = el.attribute(C_ATTR, "0");
119     c = cStr.toInt();
120     if (c < 0 || c >= w) {
121         stateInfo.setError(QString("Illegal value for '%1': %2").arg(C_ATTR).arg(cStr));
122         return;
123     }
124 
125     inverted = el.attribute("invert") == "true";
126     reflect = el.attribute("reflect", "true") == "true";
127     filterNested = el.attribute("filterNested", "false") == "true";
128     filterUnique = el.attribute("filterUnique", "false") == "true";
129 
130     if (filterNested && filterUnique) {
131         stateInfo.setError(QString("Filter unique and filter nested cannot go together"));
132         return;
133     }
134 
135     resultFile = el.attribute(RESULT_ATTR);
136     if (resultFile.isEmpty()) {
137         stateInfo.setError(QString("Value not found '%1'").arg(RESULT_ATTR));
138         return;
139     }
140 
141     excludeList = el.attribute(EXCL_ATTR).split(',', QString::SkipEmptyParts);
142 }
143 
getAlgName(RFAlgorithm alg)144 static QString getAlgName(RFAlgorithm alg) {
145     QString res;
146     switch (alg) {
147         case RFAlgorithm_Diagonal:
148             res = "diagonal";
149             break;
150         case RFAlgorithm_Suffix:
151             res = "suffix";
152             break;
153         default:
154             res = "UNKNOWN";
155             break;
156     }
157     return res;
158 }
159 
prepare()160 void GTest_FindSingleSequenceRepeatsTask::prepare() {
161     if (hasError() || isCanceled()) {
162         return;
163     }
164     U2SequenceObject *seq1IObj = getContext<U2SequenceObject>(this, seq);
165     if (seq1IObj == nullptr) {
166         stateInfo.setError("can't find sequence1");
167         return;
168     }
169 
170     if (region.isEmpty()) {
171         region = U2Region(0, seq1IObj->getSequenceLength());
172     }
173 
174     int maxLen = seq1IObj->getSequenceLength();
175     if (minD == -1) {
176         minD = -maxLen;
177     }
178     if (maxD == -1) {
179         maxD = maxLen;
180     }
181 
182     QList<RFAlgorithm> algos;
183     if (alg == RFAlgorithm_Auto) {
184         algos << RFAlgorithm_Diagonal << RFAlgorithm_Suffix;
185     } else {
186         algos << alg;
187     }
188 
189     FindRepeatsTaskSettings s;
190     s.minLen = w;
191     s.mismatches = c;
192     s.minDist = minD;
193     s.maxDist = maxD;
194     s.inverted = inverted;
195     s.seqRegion = region;
196     s.seq2Region = region;
197     s.reportReflected = reflect;
198     s.nThreads = 1;  // todo: add to settings
199 
200     if (filterNested == true) {
201         s.filter = DisjointRepeats;
202     } else if (filterUnique == true) {
203         s.filter = UniqueRepeats;
204     } else {
205         s.filter = NoFiltering;
206     }
207 
208     U2OpStatusImpl os;
209     foreach (RFAlgorithm algo, algos) {
210         QString algName = getAlgName(algo);
211         if (excludeList.contains(algName)) {
212             continue;
213         }
214         s.algo = algo;
215         DNASequence seqData = seq1IObj->getWholeSequence(os);
216         CHECK_OP_EXT(os, setError(os.getError()), );
217         Task *sub = new FindRepeatsTask(s, seqData, seqData);
218         addSubTask(sub);
219     }
220 }
221 
run()222 void GTest_FindSingleSequenceRepeatsTask::run() {
223     if (hasError() || isCanceled()) {
224         return;
225     }
226     QVector<RFResult> expectedResults;
227     // load file with results
228     QString fname = env->getVar("COMMON_DATA_DIR") + "/" + resultFile;
229     QFile file(fname);
230     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
231         stateInfo.setError(QString("Can't open results file %1").arg(fname));
232         return;
233     }
234 
235     while (!file.atEnd()) {
236         QString line = file.readLine();
237         QStringList hit = line.split(' ', QString::SkipEmptyParts);
238         if (!(hit.size() == 3 || hit.size() == 4)) {
239             stateInfo.setError(QString("Can't parse results line: %1").arg(line));
240             return;
241         }
242         RFResult r;
243         r.x = hit[0].toInt() - 1;
244         r.y = hit[1].toInt() - 1;
245         r.l = hit[2].toInt();
246         r.c = hit.size() == 4 ? hit[3].toInt() : -1;
247         if (r.x < 0 || r.y < 0 || r.l < 0 || (hit.size() == 4 && r.c < 0)) {
248             stateInfo.setError(QString("Can't parse results line: %1").arg(line));
249             return;
250         }
251         expectedResults.append(r);
252     }
253     file.close();
254 
255     std::sort(expectedResults.begin(), expectedResults.end());
256 
257     // check all subtasks
258     FindRepeatsTask *sub = qobject_cast<FindRepeatsTask *>(getSubtasks()[0].data());
259     QVector<RFResult> calcResults = sub->getResults();
260     if (expectedResults.size() != calcResults.size()) {
261         stateInfo.setError(QString("Results count not matched, num = %1, expected = %2, alg = %3")
262                                .arg(calcResults.size())
263                                .arg(expectedResults.size())
264                                .arg(getAlgName(sub->getSettings().algo)));
265         return;
266     }
267     std::sort(calcResults.begin(), calcResults.end());
268 
269     for (int i = 0, n = expectedResults.size(); i < n; i++) {
270         RFResult re = expectedResults[i];
271         RFResult rc = calcResults[i];
272         if (re != rc || ((re.c >= 0) && (re.c != rc.c))) {
273             QString errorString = QString("Results not matched, expected(%1, %2, %3), computed(%4, %5, %6), algo = %7")
274                                       .arg(re.x)
275                                       .arg(re.y)
276                                       .arg(re.l)
277                                       .arg(rc.x)
278                                       .arg(rc.y)
279                                       .arg(rc.l)
280                                       .arg(getAlgName(sub->getSettings().algo));
281 
282             if (re.c >= 0) {
283                 errorString = QString("Results not matched, expected(%1, %2, %3, %4), computed(%5, %6, %7, %8), algo = %9")
284                                   .arg(re.x)
285                                   .arg(re.y)
286                                   .arg(re.l)
287                                   .arg(re.c)
288                                   .arg(rc.x)
289                                   .arg(rc.y)
290                                   .arg(rc.l)
291                                   .arg(rc.c)
292                                   .arg(getAlgName(sub->getSettings().algo));
293             }
294 
295             stateInfo.setError(errorString);
296             return;
297         }
298     }
299 }
300 
301 //---------------------------------------------------------------------------------------------------------
302 //---------------------------------------------------------------------------------------------------------
init(XMLTestFormat *,const QDomElement & el)303 void GTest_FindTandemRepeatsTask::init(XMLTestFormat *, const QDomElement &el) {
304     minD = el.attribute(MIND_ATTR, "-1").toInt();
305     maxD = el.attribute(MAXD_ATTR, "-1").toInt();
306 
307     minSize = el.attribute("minSize", "3").toInt();
308     repeatCount = el.attribute("repeatCount", "3").toInt();
309 
310     inverted = el.attribute("invert") == "true";
311     reflect = el.attribute("reflect", "true") == "true";
312     filterNested = el.attribute("filterNested", "false") == "true";
313     filterUnique = el.attribute("filterUnique", "false") == "true";
314 
315     if (filterNested && filterUnique) {
316         stateInfo.setError(QString("Filter unique and filter nested cannot go together"));
317         return;
318     }
319 
320     results = el.attribute(RESULT_ATTR);
321     sequence = el.attribute("sequence");
322 }
323 
prepare()324 void GTest_FindTandemRepeatsTask::prepare() {
325     if (hasError() || isCanceled()) {
326         return;
327     }
328     // this->getContext(this,"")
329     // new DNAAlphabetRegistryImpl(
330     //     TaskResourceUsage* tru = AppContext::getTaskScheduler()->getTaskResources(NULL).constData();
331     const DNAAlphabet *alph = AppContext::getDNAAlphabetRegistry()->findById(BaseDNAAlphabetIds::NUCL_DNA_DEFAULT());
332     seqObj = new DNASequence(QString("sequence"), sequence.toLatin1(), alph);
333     if (seqObj == nullptr) {
334         stateInfo.setError("can't find sequence1");
335         return;
336     }
337     string = (char *)(seqObj->constData());
338 
339     int maxLen = sequence.length();
340     if (minD == -1) {
341         minD = -maxLen;
342     }
343     if (maxD == -1) {
344         maxD = maxLen;
345     }
346     if (maxSize == 0) {
347         maxSize = maxLen;
348     }
349 
350     FindTandemsTaskSettings s;
351     s.minPeriod = minSize;
352     s.minRepeatCount = repeatCount;
353     s.seqRegion = region;
354     s.nThreads = 1;  // todo: add to settings
355 
356     addSubTask(new TandemFinder(s, *seqObj));
357 }
358 
run()359 void GTest_FindTandemRepeatsTask::run() {
360     if (hasError() || isCanceled()) {
361         return;
362     }
363     QList<Tandem> expectedResults;
364     // load file with results
365     QStringList resList = results.split(';', QString::SkipEmptyParts);
366     foreach (const QString &result, resList) {
367         QStringList hit = result.split(',', QString::SkipEmptyParts);
368         if (hit.size() != 3) {
369             stateInfo.setError(QString("Can't parse results line: %1").arg(result));
370             return;
371         }
372         bool offsetConverted;
373         Tandem tnd(hit[0].toInt(&offsetConverted), hit[2].toInt(), hit[1].toInt());
374         if (!offsetConverted || tnd.size == 0 || tnd.repeatLen == 0) {
375             stateInfo.setError(QString("Can't parse results line: %1").arg(result));
376             return;
377         }
378         expectedResults.append(tnd);
379     }
380 
381     // check all subtasks
382     TandemFinder *sub = qobject_cast<TandemFinder *>(this->getSubtasks()[0].data());
383     QList<Tandem> calcResults = sub->getResults();
384     if (expectedResults.size() != calcResults.size()) {
385         QString results("First results are:\n");
386         for (int i = 0, n = qMin(calcResults.size(), 3); i < n; i++) {
387             Tandem rc = calcResults[i];
388             results.append(QString("%1 %2 %3\n").arg(rc.offset).arg(rc.size).arg(rc.repeatLen));
389         }
390         stateInfo.setError(QString("Results count not matched, num = %1, expected = %2\n%3").arg(calcResults.size()).arg(expectedResults.size()).arg(results));
391         return;
392     }
393     std::sort(expectedResults.begin(), expectedResults.end());
394     std::sort(calcResults.begin(), calcResults.end());
395 
396     for (int i = 0, n = expectedResults.size(); i < n; i++) {
397         Tandem re = expectedResults[i];
398         Tandem rc = calcResults[i];
399         if (re.offset != rc.offset || re.size != rc.size || re.repeatLen != rc.repeatLen) {
400             stateInfo.setError(QString("Results not matched, expected(%1, %2, %3), computed(%4, %5, %6)")
401                                    .arg(re.offset)
402                                    .arg(re.size)
403                                    .arg(re.repeatLen)
404                                    .arg(rc.offset)
405                                    .arg(rc.size)
406                                    .arg(rc.repeatLen));
407             return;
408         }
409     }
410 
411     delete seqObj;
412 }
413 
414 //---------------------------------------------------------------------------------------------------------
415 //---------------------------------------------------------------------------------------------------------
416 
parseRegion(const QString & n,const QDomElement & el)417 U2Region GTest_FindRealTandemRepeatsTask::parseRegion(const QString &n, const QDomElement &el) {
418     U2Region res;
419     QString v = el.attribute(n);
420     if (v.isEmpty()) {
421         return res;
422     }
423     int idx = v.indexOf("..");
424     if (idx == -1 || idx + 2 >= v.length()) {
425         return res;
426     }
427     QString v1 = v.left(idx);
428     QString v2 = v.mid(idx + 2);
429     int startPos = v1.toInt();
430     int endPos = v2.toInt();
431     if (startPos >= 0 && endPos > startPos) {
432         res.startPos = startPos - 1;
433         res.length = endPos - startPos + 1;
434     }
435     return res;
436 }
437 
init(XMLTestFormat *,const QDomElement & el)438 void GTest_FindRealTandemRepeatsTask::init(XMLTestFormat *, const QDomElement &el) {
439     minD = el.attribute(MIND_ATTR, "-1").toInt();
440     maxD = el.attribute(MAXD_ATTR, "-1").toInt();
441 
442     minSize = el.attribute("minSize", "1").toInt();
443     repeatCount = el.attribute("repeatCount", "3").toInt();
444 
445     inverted = el.attribute("invert") == "true";
446     reflect = el.attribute("reflect", "true") == "true";
447     filterNested = el.attribute("filterNested", "false") == "true";
448     filterUnique = el.attribute("filterUnique", "false") == "true";
449 
450     if (filterNested && filterUnique) {
451         stateInfo.setError(QString("Filter unique and filter nested cannot go together"));
452         return;
453     }
454 
455     results = el.attribute(RESULT_ATTR);
456     if (results.isEmpty()) {
457         stateInfo.setError(QString("Value not found '%1'").arg(RESULT_ATTR));
458         return;
459     }
460 
461     sequence = el.attribute("sequence");
462     if (sequence.isEmpty()) {
463         stateInfo.setError(QString("Value not found '%1'").arg("sequence"));
464         return;
465     }
466 }
467 
prepare()468 void GTest_FindRealTandemRepeatsTask::prepare() {
469     CHECK_OP(stateInfo, );
470     U2SequenceObject *seqObj = getContext<U2SequenceObject>(this, sequence);
471     if (seqObj == nullptr) {
472         stateInfo.setError("can't find sequence1");
473         return;
474     }
475     if (region.isEmpty()) {
476         region = U2Region(0, seqObj->getSequenceLength());
477     }
478 
479     int maxLen = seqObj->getSequenceLength();
480     if (minD == -1) {
481         minD = -maxLen;
482     }
483     if (maxD == -1) {
484         maxD = maxLen;
485     }
486 
487     FindTandemsTaskSettings s;
488     s.minPeriod = minSize;
489     s.minRepeatCount = repeatCount;
490     s.seqRegion = region;
491     s.nThreads = 1;  // todo: add to settings
492 
493     U2OpStatusImpl os;
494     DNASequence dna = seqObj->getWholeSequence(os);
495     CHECK_OP_EXT(os, setError(os.getError()), );
496     addSubTask(new TandemFinder(s, dna));
497 }
498 
run()499 void GTest_FindRealTandemRepeatsTask::run() {
500     if (hasError() || isCanceled()) {
501         return;
502     }
503     QList<Tandem> expectedResults;
504     // load file with results
505     QString fname = env->getVar("COMMON_DATA_DIR") + "/" + results;
506     QFile file(fname);
507     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
508         stateInfo.setError(QString("Can't open results file %1").arg(fname));
509         return;
510     }
511 
512     while (!file.atEnd()) {
513         QString line = file.readLine();
514         QStringList hit = line.split(' ', QString::SkipEmptyParts);
515         if (hit.size() != 5) {
516             stateInfo.setError(QString("Can't parse results line: %1").arg(line));
517             return;
518         }
519         bool h1ok, h2ok, h3ok;
520         Tandem t(hit[0].toInt(&h1ok) - 1, hit[3].toInt(&h2ok), hit[2].toInt(&h3ok));
521         if (!h1ok || !h2ok || !h3ok) {
522             stateInfo.setError(QString("Can't parse results line: %1").arg(line));
523             return;
524         }
525         expectedResults.append(t);
526     }
527     file.close();
528 
529     std::sort(expectedResults.begin(), expectedResults.end());
530 
531     // check all subtasks
532     TandemFinder *sub = qobject_cast<TandemFinder *>(this->getSubtasks()[0].data());
533     QList<Tandem> calcResults = sub->getResults();
534     QMutableListIterator<Tandem> cIt(calcResults);
535     QMutableListIterator<Tandem> eIt(expectedResults);
536     while (cIt.hasNext() && eIt.hasNext()) {
537         Tandem er = eIt.peekNext();
538         Tandem cr = cIt.peekNext();
539         if (er < cr) {
540             eIt.next();
541             //            eIt.remove();
542         } else if (cr < er) {
543             cIt.next();
544             cIt.remove();
545         } else {
546             eIt.next();
547             eIt.remove();
548             cIt.next();
549             cIt.remove();
550         }
551     }
552     if (!expectedResults.isEmpty()) {
553         QString result("First of them:\n");
554         Tandem rc = expectedResults[0];
555         result.append(QString("%1 %2 %3\n").arg(rc.offset).arg(rc.size).arg(rc.repeatLen));
556         stateInfo.setError(QString("Not all expected tandems found: total %1\n%2").arg(expectedResults.size()).arg(result));
557     }
558 }
559 
560 //---------------------------------------------------------------------------------------------------------
561 //---------------------------------------------------------------------------------------------------------
562 
init(XMLTestFormat *,const QDomElement & el)563 void GTest_SArrayBasedFindTask::init(XMLTestFormat *, const QDomElement &el) {
564     QString buf = el.attribute(RESULT_ATTR);
565     if (buf.isEmpty()) {
566         stateInfo.setError(QString("Value not found: '%1'").arg(RESULT_ATTR));
567         return;
568     }
569 
570     QStringList results = buf.split(",");
571     foreach (const QString &str, results) {
572         bool ok = false;
573         int pos = str.toInt(&ok);
574         if (!ok) {
575             stateInfo.setError("Can't parse expected results");
576             return;
577         } else {
578             expectedResults.append(pos);
579         }
580     }
581 
582     seqObjName = el.attribute(SEQUENCE);
583     if (seqObjName.isEmpty()) {
584         stateInfo.setError(QString("Value not found: '%1'").arg(SEQUENCE));
585         return;
586     }
587 
588     buf = el.attribute(MISMATCHES);
589     bool ok = false;
590     nMismatches = buf.toInt(&ok);
591     if (!ok) {
592         nMismatches = 0;
593     }
594 
595     useBitMask = el.attribute(USE_BITMASK) == "true";
596 
597     query = el.attribute(QUERY);
598     if (query.isEmpty()) {
599         stateInfo.setError(QString("Value not found: '%1'").arg(QUERY));
600         return;
601     }
602 }
603 
cleanup()604 void GTest_SArrayBasedFindTask::cleanup() {
605     wholeSeq = QByteArray();
606 
607     XmlTest::cleanup();
608 }
609 
prepare()610 void GTest_SArrayBasedFindTask::prepare() {
611     CHECK_OP(stateInfo, );
612 
613     U2SequenceObject *seqObj = getContext<U2SequenceObject>(this, seqObjName);
614     if (seqObj == nullptr) {
615         stateInfo.setError(QString("Can't find index sequence %1").arg(seqObjName));
616         return;
617     }
618     DNAAlphabetType seqType = seqObj->getAlphabet()->getType();
619     char unknownChar = seqType == DNAAlphabet_AMINO ? 'X' : seqType == DNAAlphabet_NUCL ? 'N'
620                                                                                         : '\0';
621 
622     const quint32 *bitMask = nullptr;
623     int bitCharLen = 0;
624 
625     if (useBitMask) {
626         bitCharLen = bt.getBitMaskCharBitsNum(seqType);
627         bitMask = bt.getBitMaskCharBits(seqType);
628     }
629 
630     int prefixSize = query.size();
631     if (nMismatches > 0) {
632         prefixSize = prefixSize / (nMismatches + 1);
633     }
634 
635     wholeSeq = seqObj->getWholeSequenceData(stateInfo);
636     CHECK_OP(stateInfo, );
637     index = new SArrayIndex(wholeSeq.constData(), seqObj->getSequenceLength(), prefixSize, stateInfo, unknownChar, bitMask, bitCharLen);
638 
639     if (hasError()) {
640         return;
641     }
642 
643     SArrayBasedSearchSettings s;
644     s.query = query.toLatin1();
645     s.useBitMask = useBitMask;
646     s.bitMask = bitMask;
647     s.nMismatches = nMismatches;
648     s.bitMaskCharBitsNum = bitCharLen;
649     s.unknownChar = unknownChar;
650     findTask = new SArrayBasedFindTask(index, s);
651     addSubTask(findTask);
652 }
653 
run()654 void GTest_SArrayBasedFindTask::run() {
655     if (hasError() || isCanceled()) {
656         return;
657     }
658 
659     std::sort(expectedResults.begin(), expectedResults.end());
660 
661     QList<int> calcResults = findTask->getResults();
662     if (expectedResults.size() != calcResults.size()) {
663         stateInfo.setError(QString("Results count do not match, num = %1, expected = %2")
664                                .arg(calcResults.size())
665                                .arg(expectedResults.size()));
666         return;
667     }
668 
669     std::sort(calcResults.begin(), calcResults.end());
670     for (int i = 0, n = expectedResults.size(); i < n; i++) {
671         int re = expectedResults[i];
672         int rc = calcResults[i];
673         if (re != rc) {
674             stateInfo.setError(QString("Results not matched, expected %1, computed %2")
675                                    .arg(re)
676                                    .arg(rc));
677             return;
678         }
679     }
680 }
681 
682 //---------------------------------------------------------------------------------------------------------
683 //---------------------------------------------------------------------------------------------------------
684 
createTestFactories()685 QList<XMLTestFactory *> RepeatFinderTests::createTestFactories() {
686     QList<XMLTestFactory *> res;
687     res.append(GTest_FindSingleSequenceRepeatsTask::createFactory());
688     res.append(GTest_FindTandemRepeatsTask::createFactory());
689     res.append(GTest_FindRealTandemRepeatsTask::createFactory());
690     res.append(GTest_SArrayBasedFindTask::createFactory());
691     return res;
692 }
693 
694 }  // namespace U2
695