1 /*
2 SPDX-FileCopyrightText: 2011 Dmitry Risenberg <dmitry.risenberg@gmail.com>
3 
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "interactivedifftest.h"
8 
9 #include <QTest>
10 #include "diffmodel.h"
11 #include "diffmodellist.h"
12 #include "parser.h"
13 
14 using namespace Diff2;
15 
16 typedef QHash<int, QPair<QStringList, QStringList> > DifferenceHash;
17 Q_DECLARE_METATYPE(DifferenceHash);
18 typedef QHash<int, QPair<int, int> > LineNumberHash;
19 Q_DECLARE_METATYPE(LineNumberHash);
20 
CompareDifferenceStringList(const DifferenceStringList & actual,const QStringList & expected)21 void InteractiveDiffTest::CompareDifferenceStringList(const DifferenceStringList& actual, const QStringList& expected)
22 {
23     DifferenceStringListConstIterator actualIter;
24     QStringList::const_iterator expectedIter;
25     for (actualIter = actual.constBegin(), expectedIter = expected.constBegin(); actualIter != actual.constEnd() && expectedIter != expected.constEnd(); ++actualIter, ++expectedIter) {
26         QCOMPARE((*actualIter)->string(), *expectedIter);
27     }
28     if (actualIter != actual.constEnd()) {
29         QFAIL(QStringLiteral("Actual has too many items, starting with '%1', line %2").arg((*actualIter)->string()).arg(actualIter - actual.constBegin()).toLatin1().constData());
30     }
31     if (expectedIter != expected.constEnd()) {
32         QFAIL(QStringLiteral("Actual has too few items, no match for '%1', line %2").arg(*expectedIter).arg(expectedIter - expected.constBegin()).toLatin1().constData());
33     }
34 }
35 
36 // The most basic test - something is actually working
testOneLineChange()37 void InteractiveDiffTest::testOneLineChange()
38 {
39     DiffModel* model = new DiffModel();
40     QStringList newLines;
41     newLines << "newline\n";
42     QStringList oldLines;
43     oldLines << "oldline\n";
44     model->linesChanged(oldLines, newLines, 2);
45     QCOMPARE(model->differences()->size(), 1);
46 
47     QCOMPARE(model->differenceCount(), 1);
48     const Difference* diff = model->differenceAt(0);
49     CompareDifferenceStringList(diff->sourceLines(), oldLines);
50     CompareDifferenceStringList(diff->destinationLines(), newLines);
51     QCOMPARE(diff->type(), int(Difference::Change));
52 }
53 
testSameLine()54 void InteractiveDiffTest::testSameLine()
55 {
56     DiffModel* model = new DiffModel();
57     QStringList newLines;
58     newLines << "oldline2\n";
59     QStringList oldLines;
60     oldLines << "oldline1\n" << "oldline2\n";
61     model->linesChanged(oldLines, newLines, 2);
62 
63     QCOMPARE(model->differenceCount(), 1);
64     const Difference* diff = model->differenceAt(0);
65     CompareDifferenceStringList(diff->sourceLines(), QStringList() << "oldline1\n");
66     CompareDifferenceStringList(diff->destinationLines(), QStringList());
67     QCOMPARE(diff->type(), int(Difference::Delete));
68 }
69 
testDifferenceContents()70 void InteractiveDiffTest::testDifferenceContents()
71 {
72     QFETCH(QStringList, patch);
73     Parser parser(nullptr);
74     bool malformed;
75     DiffModelList* models = parser.parse(patch, &malformed);
76     QVERIFY(!malformed);
77     QCOMPARE(models->size(), 1);
78     DiffModel* model = models->at(0);
79 
80     QFETCH(QStringList, oldLines);
81     QFETCH(QStringList, newLines);
82     QFETCH(int, editLineNumber);
83     QFETCH(bool, isAlreadyApplied);
84     model->applyAllDifferences(isAlreadyApplied);
85     model->linesChanged(oldLines, newLines, editLineNumber);
86     QFETCH(int, expectedDifferenceCount);
87     QCOMPARE(model->differenceCount(), expectedDifferenceCount);
88 
89     QFETCH(DifferenceHash, expectedDifferences);
90     for (DifferenceHash::ConstIterator iter = expectedDifferences.constBegin(); iter != expectedDifferences.constEnd(); ++iter) {
91         const Difference* diff = model->differenceAt(iter.key());
92         CompareDifferenceStringList(diff->sourceLines(), iter.value().first);
93         CompareDifferenceStringList(diff->destinationLines(), iter.value().second);
94     }
95 }
96 
testDifferenceContents_data()97 void InteractiveDiffTest::testDifferenceContents_data()
98 {
99     QTest::addColumn<QStringList>("patch");
100     QTest::addColumn<QStringList>("oldLines");     // lines that are replaced
101     QTest::addColumn<QStringList>("newLines");     // replacement lines
102     QTest::addColumn<int>("editLineNumber");
103     QTest::addColumn<bool>("isAlreadyApplied");
104     QTest::addColumn<int>("expectedDifferenceCount");
105     QTest::addColumn<DifferenceHash>("expectedDifferences");
106 
107     {
108         QStringList patch;
109         patch <<
110               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
111               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
112               "@@ -1,5 +1,5 @@\n" <<
113               " abcd\n" <<
114               "-delete1\n" <<
115               "+insert1\n" <<
116               " efgh\n" <<
117               "-delete2\n" <<
118               "+insert2\n" <<
119               " ijkl\n";
120 
121         QStringList newLines;
122         newLines << "newline1\n" << "newline2\n";
123         QStringList oldLines;
124         oldLines << "efgh\n";
125         QStringList sourceLines;
126         sourceLines << "delete1\n" << "efgh\n" << "delete2\n";
127         QStringList destinationLines;
128         destinationLines << "insert1\n" << "newline1\n" << "newline2\n" << "insert2\n";
129         DifferenceHash expectedDifferences;
130         expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines));
131 
132         QTest::newRow("Merge adjacent differences") << patch << oldLines << newLines << 3 << true << 1 << expectedDifferences;
133     }
134     {
135         QStringList patch;
136         patch <<
137               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
138               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
139               "@@ -1,4 +1,4 @@\n" <<
140               " abcd\n" <<
141               "-delete1\n" <<
142               "-delete2\n" <<
143               "+insert1\n" <<
144               "+insert2\n" <<
145               " efgh\n";
146 
147         QStringList newLines;
148         newLines << "newline1\n";
149         QStringList oldLines;
150         oldLines << "efgh\n";
151         QStringList sourceLines;
152         sourceLines << "delete1\n" << "delete2\n" << "efgh\n";
153         QStringList destinationLines;
154         destinationLines << "insert1\n" << "insert2\n" << "newline1\n";
155         DifferenceHash expectedDifferences;
156         expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines));
157 
158         // Append a line to a multiline diff
159         QTest::newRow("Append multiline") << patch << oldLines << newLines << 4 << true << 1 << expectedDifferences;
160     }
161     {
162         QStringList patch;
163         patch <<
164               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
165               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
166               "@@ -1,3 +1,3 @@\n" <<
167               " abcd\n" <<
168               "-delete1\n" <<
169               "+insert1\n" <<
170               " efgh\n";
171 
172         QStringList newLines;
173         newLines << "delete1\n";
174         QStringList oldLines;
175         oldLines << "insert1\n";
176         QTest::newRow("Revert existing difference") << patch << oldLines << newLines << 2 << true << 0 << DifferenceHash();
177     }
178     {
179         QStringList patch;
180         patch <<
181               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
182               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
183               "@@ -1,3 +1,2 @@\n" <<
184               " abcd\n" <<
185               "-delete1\n" <<
186               " efgh\n";
187 
188         QStringList newLines;
189         newLines << "abcd\n" << "delete1\n";
190         QStringList oldLines;
191         oldLines << "abcd\n";
192         QTest::newRow("Revert deletion") << patch << oldLines << newLines << 1 << true << 0 << DifferenceHash();
193     }
194     {
195         QStringList patch;
196         patch <<
197               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
198               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
199               "@@ -1,4 +1,5 @@\n" <<
200               " abcd\n" <<
201               "-delete1\n" <<
202               "-delete2\n" <<
203               "+insert1\n" <<
204               "+insert2\n" <<
205               "+insert3\n" <<
206               " efgh\n";
207 
208         QStringList newLines;
209         newLines << "delete2\n";
210         QStringList oldLines;
211         oldLines << "insert2\n";
212         DifferenceHash expectedDifferences;
213         expectedDifferences.insert(0, qMakePair(QStringList() << "delete1\n", QStringList() << "insert1\n"));
214         expectedDifferences.insert(1, qMakePair(QStringList(), QStringList() << "insert3\n"));
215 
216         QTest::newRow("Partial reversion") << patch << oldLines << newLines << 3 << true << 2 << expectedDifferences;
217     }
218     {
219         QStringList patch;
220         patch <<
221               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
222               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
223               "@@ -1,4 +1,4 @@\n" <<
224               " abcd\n" <<
225               "-delete1\n" <<
226               "-delete2\n" <<
227               "+insert1\n" <<
228               "+insert2\n" <<
229               " efgh\n";
230 
231         QStringList newLines;
232         newLines << "newline1\n" << "newline2\n";
233         QStringList oldLines;
234         oldLines << "abcd\n" << "insert1\n" << "insert2\n" << "efgh\n";
235         QStringList sourceLines;
236         sourceLines << "abcd\n" << "delete1\n" << "delete2\n" << "efgh\n";
237         QStringList destinationLines;
238         destinationLines << "newline1\n" << "newline2\n";
239         DifferenceHash expectedDifferences;
240         expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines));
241 
242         // The first existing difference inside the edit
243         QTest::newRow("First inside") << patch << oldLines << newLines << 1 << true << 1 << expectedDifferences;
244     }
245     {
246         QStringList patch;
247         patch <<
248               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
249               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
250               "@@ -1,4 +1,4 @@\n" <<
251               " abcd\n" <<
252               "-delete1\n" <<
253               "-delete2\n" <<
254               "+insert1\n" <<
255               "+insert2\n" <<
256               " efgh\n";
257 
258         QStringList newLines;
259         newLines << "newline1\n" << "newline2\n";
260         QStringList oldLines;
261         oldLines << "insert2\n" << "efgh\n";
262         QStringList sourceLines;
263         sourceLines << "delete1\n" << "delete2\n" << "efgh\n";
264         QStringList destinationLines;
265         destinationLines << "insert1\n" << "newline1\n" << "newline2\n";
266         DifferenceHash expectedDifferences;
267         expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines));
268 
269         // The first existing difference intersects with the edit
270         QTest::newRow("First intersects") << patch << oldLines << newLines << 3 << true << 1 << expectedDifferences;
271     }
272     {
273         QStringList patch;
274         patch <<
275               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
276               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
277               "@@ -1,2 +1,3 @@\n" <<
278               " abcd\n" <<
279               "+\n" <<
280               " efgh\n";
281 
282         QStringList newLines;
283         newLines << "a\n";
284         QStringList oldLines;
285         oldLines << "\n";
286         DifferenceHash expectedDifferences;
287         expectedDifferences.insert(0, qMakePair(QStringList(), QStringList() << "a\n"));
288 
289         QTest::newRow("Replace empty line") << patch << oldLines << newLines << 2 << true << 1 << expectedDifferences;
290     }
291     {
292         QStringList patch;
293         patch <<
294               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
295               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
296               "@@ -1,3 +1,3 @@\n" <<
297               " abcd\n" <<
298               "+insert1\n" <<
299               "+insert2\n" <<
300               "+insert3\n" <<
301               "+insert4\n" <<
302               "+insert5\n" <<
303               " efgh\n" <<
304               "@@ -10,3 +15,3 @@\n" <<
305               " abcd\n" <<
306               "-delete1\n" <<
307               "+insert1\n" <<
308               " efgh\n";
309 
310         QStringList newLines;
311         newLines << "newline1\n";
312         QStringList oldLines;
313         oldLines << "delete1\n";
314         DifferenceHash expectedDifferences;
315         expectedDifferences.insert(1, qMakePair(QStringList() << "delete1\n", QStringList() << "newline1\n"));
316 
317         QTest::newRow("Replace line in source") << patch << oldLines << newLines << 11 << false << 2 << expectedDifferences;
318     }
319 }
320 
testLineNumbers_data()321 void InteractiveDiffTest::testLineNumbers_data()
322 {
323     QTest::addColumn<QStringList>("patch");
324     QTest::addColumn<QStringList>("oldLines");     // lines that are replaced
325     QTest::addColumn<QStringList>("newLines");     // replacement lines
326     QTest::addColumn<int>("editLineNumber");
327     QTest::addColumn<int>("expectedDifferenceCount");
328     QTest::addColumn<LineNumberHash>("expectedLineNumbers");
329 
330     {
331         QStringList patch;
332         patch <<
333               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
334               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
335               "@@ -1,4 +1,6 @@\n" <<
336               " abcd\n" <<
337               "-delete1\n" <<
338               "-delete2\n" <<
339               "+insert1\n" <<
340               "+insert2\n" <<
341               "+insert3\n" <<
342               "+insert4\n" <<
343               " efgh\n" <<
344               "@@ -15,3 +17,4 @@\n" <<
345               " abcd\n" <<
346               "-delete1\n" <<
347               "+insert1\n" <<
348               "+insert2\n" <<
349               " efgh\n";
350 
351         QStringList newLines;
352         newLines << "newline1\n" << "newline2\n" << "newline2\n";
353         QStringList oldLines;
354         oldLines << "oldline1\n";
355         LineNumberHash expectedLineNumbers;
356         expectedLineNumbers.insert(0, qMakePair(2, 2));
357         expectedLineNumbers.insert(2, qMakePair(16, 20));
358         QTest::newRow("Update existing line numbers") << patch << oldLines << newLines << 10 << 3 << expectedLineNumbers;
359     }
360     {
361         QStringList patch;
362         patch <<
363               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
364               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
365               "@@ -1,4 +1,6 @@\n" <<
366               " abcd\n" <<
367               "-delete1\n" <<
368               "-delete2\n" <<
369               "+insert1\n" <<
370               "+insert2\n" <<
371               "+insert3\n" <<
372               "+insert4\n" <<
373               " efgh\n" <<
374               "@@ -15,3 +17,4 @@\n" <<
375               " abcd\n" <<
376               "-delete1\n" <<
377               "+insert1\n" <<
378               "+insert2\n" <<
379               " efgh\n";
380 
381         QStringList newLines;
382         newLines << "newline1\n";
383         QStringList oldLines;
384         oldLines << "oldline1\n";
385         LineNumberHash expectedLineNumbers;
386         expectedLineNumbers.insert(2, qMakePair(22, 25));
387 
388         // Line numbers assigned to new difference when it is inserted after all existing differences
389         QTest::newRow("Last edit line number") << patch << oldLines << newLines << 25 << 3 << expectedLineNumbers;
390     }
391     {
392         QStringList patch;
393         patch <<
394               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
395               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
396               "@@ -1,4 +1,6 @@\n" <<
397               " abcd\n" <<
398               "-delete1\n" <<
399               "-delete2\n" <<
400               "+insert1\n" <<
401               "+insert2\n" <<
402               "+insert3\n" <<
403               "+insert4\n" <<
404               " efgh\n" <<
405               "@@ -15,3 +17,4 @@\n" <<
406               " abcd\n" <<
407               "-delete1\n" <<
408               "+insert1\n" <<
409               "+insert2\n" <<
410               " efgh\n";
411 
412         QStringList newLines;
413         newLines << "newline1\n";
414         QStringList oldLines;
415         oldLines << "oldline1\n";
416         LineNumberHash expectedLineNumbers;
417         expectedLineNumbers.insert(1, qMakePair(11, 13));
418 
419         // Line numbers assigned to new difference when it is inserted between existing differences
420         QTest::newRow("Middle edit line number") << patch << oldLines << newLines << 13 << 3 << expectedLineNumbers;
421     }
422     {
423         QStringList patch;
424         patch <<
425               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
426               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
427               "@@ -10,4 +10,4 @@\n" <<
428               " abcd\n" <<
429               "-delete1\n" <<
430               "-delete2\n" <<
431               "+insert1\n" <<
432               "+insert2\n" <<
433               " efgh\n";
434 
435         QStringList newLines;
436         newLines << "newline1\n";
437         QStringList oldLines;
438         oldLines << "oldline1\n";
439         LineNumberHash expectedLineNumbers;
440         expectedLineNumbers.insert(0, qMakePair(5, 5));
441 
442         // Line numbers assigned to new difference when it is inserted before all existing differences
443         QTest::newRow("First edit line number") << patch << oldLines << newLines << 5 << 2 << expectedLineNumbers;
444     }
445     {
446         QStringList patch;
447         patch <<
448               "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" <<
449               "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" <<
450               "@@ -1,3 +1,4 @@\n" <<
451               " abcd\n" <<
452               "-delete1\n" <<
453               "+insert1\n" <<
454               "+insert2\n" <<
455               " efgh\n" <<
456               "@@ -11,4 +12,5 @@\n" <<
457               " abcd\n" <<
458               "-delete2\n" <<
459               "-delete3\n" <<
460               "+insert3\n" <<
461               "+insert4\n" <<
462               "+insert5\n" <<
463               " efgh\n" <<
464               "@@ -21,4 +23,3 @@\n" <<
465               " abcd\n" <<
466               "-delete4\n" <<
467               "-delete5\n" <<
468               "+insert6\n" <<
469               " efgh\n";
470 
471         QStringList newLines;
472         newLines << "delete2\n";
473         QStringList oldLines;
474         oldLines << "insert4\n";
475         LineNumberHash expectedLineNumbers;
476         expectedLineNumbers.insert(0, qMakePair(2, 2));
477         expectedLineNumbers.insert(1, qMakePair(12, 13));
478         expectedLineNumbers.insert(2, qMakePair(13, 15));
479         expectedLineNumbers.insert(3, qMakePair(22, 24));
480 
481         QTest::newRow("Partial reversion") << patch << oldLines << newLines << 14 << 4 << expectedLineNumbers;
482     }
483 }
484 
testLineNumbers()485 void InteractiveDiffTest::testLineNumbers()
486 {
487     QFETCH(QStringList, patch);
488     Parser parser(nullptr);
489     bool malformed;
490     DiffModelList* models = parser.parse(patch, &malformed);
491     QVERIFY(!malformed);
492     QCOMPARE(models->size(), 1);
493     DiffModel* model = models->at(0);
494     model->applyAllDifferences(true);
495 
496     QFETCH(QStringList, oldLines);
497     QFETCH(QStringList, newLines);
498     QFETCH(int, editLineNumber);
499     model->linesChanged(oldLines, newLines, editLineNumber);
500     QFETCH(int, expectedDifferenceCount);
501     QCOMPARE(model->differenceCount(), expectedDifferenceCount);
502 
503     QFETCH(LineNumberHash, expectedLineNumbers);
504     for (LineNumberHash::ConstIterator iter = expectedLineNumbers.constBegin(); iter != expectedLineNumbers.constEnd(); ++iter) {
505         const Difference* diff = model->differenceAt(iter.key());
506         QCOMPARE(diff->sourceLineNumber(), iter.value().first);
507         QCOMPARE(diff->trackingDestinationLineNumber(), iter.value().second);
508     }
509 }
510 
511 // When the new diff and an existing unapplied one are on neighbour lines, do not merge the unapplied with the new.
testAppliedTouch()512 void InteractiveDiffTest::testAppliedTouch()
513 {
514     Difference* first = new Difference(2, 2);
515     first->addSourceLine(QString("delete1"));
516     first->addDestinationLine(QString("insert1"));
517     first->apply(false);
518     Difference* second = new Difference(4, 4);
519     second->addSourceLine(QString("delete2"));
520     second->addDestinationLine(QString("insert2"));
521     second->apply(false);
522     DiffModel model;
523     model.addDiff(first);
524     model.addDiff(second);
525     model.linesChanged(QStringList() << "oldline\n", QStringList() << "newline\n", 3);
526     QCOMPARE(model.differenceCount(), 3);
527     QCOMPARE(model.differenceAt(0), first);
528     QCOMPARE(model.differenceAt(2), second);
529 }
530 
531 // When the new diff and an existing unapplied one intersect, the unapplied one should be removed
testAppliedIntersect()532 void InteractiveDiffTest::testAppliedIntersect()
533 {
534     Difference* first = new Difference(2, 2);
535     first->addSourceLine(QString("delete1"));
536     first->addSourceLine(QString("delete2"));
537     first->addDestinationLine(QString("insert1"));
538     first->addDestinationLine(QString("insert2"));
539     first->apply(false);
540     Difference* second = new Difference(5, 5);
541     second->addSourceLine(QString("delete3"));
542     second->addSourceLine(QString("delete4"));
543     second->addDestinationLine(QString("insert3"));
544     second->addDestinationLine(QString("insert4"));
545     second->apply(false);
546     DiffModel model;
547     model.addDiff(first);
548     model.addDiff(second);
549     QStringList removedLines;
550     removedLines << "delete2\n" << "oldline1\n" << "delete3\n";
551     QStringList insertedLines;
552     insertedLines << "newline1\n";
553     model.linesChanged(removedLines, insertedLines, 3);
554     QCOMPARE(model.differenceCount(), 1);
555     const Difference* newDiff = model.differenceAt(0);
556     QCOMPARE(newDiff->applied(), true);
557     QCOMPARE(newDiff->sourceLineNumber(), 3);
558     QCOMPARE(newDiff->trackingDestinationLineNumber(), 3);
559     CompareDifferenceStringList(newDiff->sourceLines(), removedLines);
560     CompareDifferenceStringList(newDiff->destinationLines(), insertedLines);
561 }
562 
testExistingAndApplied()563 void InteractiveDiffTest::testExistingAndApplied()
564 {
565     Difference* first = new Difference(2, 2);
566     first->addSourceLine(QString("delete1"));
567     first->addDestinationLine(QString("insert1"));
568     first->apply(true);
569     Difference* second = new Difference(3, 3);
570     second->addSourceLine(QString("delete2"));
571     second->addDestinationLine(QString("insert2"));
572     second->apply(false);
573     DiffModel model;
574     model.addDiff(first);
575     model.addDiff(second);
576     QStringList removedLines;
577     removedLines << "delete1\n";
578     QStringList insertedLines;
579     insertedLines << "newline1\n";
580     model.linesChanged(removedLines, insertedLines, 2);
581     QCOMPARE(model.differenceCount(), 2);
582     QVERIFY(model.differenceAt(0)->applied());
583     QVERIFY(!model.differenceAt(1)->applied());
584 }
585 
testOneLineDeletionUnapplied()586 void InteractiveDiffTest::testOneLineDeletionUnapplied()
587 {
588     Difference* unappliedDeletion = new Difference(1, 1);
589     unappliedDeletion->addSourceLine("delete1\n");
590     unappliedDeletion->apply(false);
591     DiffModel model;
592     model.addDiff(unappliedDeletion);
593     QStringList removedLines;
594     removedLines << "delete1\n";
595     QStringList insertedLines;
596     insertedLines << "newline1\n";
597     model.linesChanged(removedLines, insertedLines, 1);
598     QCOMPARE(model.differenceCount(), 1);
599     const Difference* actual = model.differenceAt(0);
600     CompareDifferenceStringList(actual->sourceLines(), removedLines);
601     CompareDifferenceStringList(actual->destinationLines(), insertedLines);
602 }
603 
testApplyUnapply()604 void InteractiveDiffTest::testApplyUnapply()
605 {
606     QStringList patch;
607     patch <<
608           "--- file1\t2011-01-01 20:23:45 +0300\n" <<
609           "+++ file2\t2011-01-01 20:24:02 +0300\n" <<
610           "@@ -1,3 +1,4 @@\n" <<
611           " line1\n" <<
612           "-delete1\n" <<
613           "+insert1\n" <<
614           "+insert2\n" <<
615           " line2\n" <<
616           "@@ -11,4 +12,5 @@\n" <<
617           " line3\n" <<
618           "-delete2\n" <<
619           "-delete3\n" <<
620           "+insert3\n" <<
621           "+insert4\n" <<
622           "+insert5\n" <<
623           " line4\n" <<
624           "@@ -21,4 +23,2 @@\n" <<
625           " line5\n" <<
626           "-delete4\n" <<
627           "-delete5\n" <<
628           " line6\n" <<
629           "@@ -31,3 +31,3 @@\n" <<
630           " line7\n" <<
631           "-delete6\n" <<
632           "+insert6\n" <<
633           " line8\n";
634     Parser parser(nullptr);
635     bool malformed;
636     DiffModelList* models = parser.parse(patch, &malformed);
637     QVERIFY(!malformed);
638     QCOMPARE(models->size(), 1);
639     DiffModel* model = models->at(0);
640     QCOMPARE(model->differenceCount(), 4);
641     model->applyAllDifferences(true);
642 
643     const auto differences = *model->differences();
644     for (Difference* diff : differences) {
645         QVERIFY(diff->applied());
646     }
647     model->applyAllDifferences(false);
648     QVERIFY(!model->differenceAt(0)->applied());
649     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2);
650     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2);
651     QVERIFY(!model->differenceAt(1)->applied());
652     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12);
653     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12);
654     QVERIFY(!model->differenceAt(2)->applied());
655     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
656     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 22);
657     QVERIFY(!model->differenceAt(3)->applied());
658     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
659     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32);
660 
661     model->differenceAt(1)->apply(true);
662     QVERIFY(model->differenceAt(1)->applied());
663     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12);
664     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12);
665     QVERIFY(!model->differenceAt(2)->applied());
666     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
667     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23);
668     QVERIFY(!model->differenceAt(3)->applied());
669     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
670     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 33);
671 
672     model->differenceAt(1)->apply(true);
673     QVERIFY(model->differenceAt(1)->applied());
674     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12);
675     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12);
676     QVERIFY(!model->differenceAt(2)->applied());
677     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
678     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23);
679     QVERIFY(!model->differenceAt(3)->applied());
680     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
681     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 33);
682 
683     model->differenceAt(2)->apply(true);
684     QVERIFY(model->differenceAt(2)->applied());
685     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
686     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23);
687     QVERIFY(!model->differenceAt(3)->applied());
688     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
689     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 31);
690 
691     model->applyAllDifferences(true);
692     QVERIFY(model->differenceAt(0)->applied());
693     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2);
694     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2);
695     QVERIFY(model->differenceAt(1)->applied());
696     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12);
697     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 13);
698     QVERIFY(model->differenceAt(2)->applied());
699     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
700     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 24);
701     QVERIFY(model->differenceAt(3)->applied());
702     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
703     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32);
704 
705     model->applyAllDifferences(true);
706     QVERIFY(model->differenceAt(0)->applied());
707     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2);
708     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2);
709     QVERIFY(model->differenceAt(1)->applied());
710     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12);
711     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 13);
712     QVERIFY(model->differenceAt(2)->applied());
713     QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22);
714     QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 24);
715     QVERIFY(model->differenceAt(3)->applied());
716     QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32);
717     QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32);
718 }
719 
720 static void
contextDiff1()721 contextDiff1()
722 {
723     QStringList patch;
724     patch <<
725           "commit 7377fcc682e85ef9784adb2a2da2c8c6756f9018 (HEAD, KDE/4.11)\n" <<
726           "Author:     Dr. Chocholoušek <bla@zin.ec>\n" <<
727           "AuthorDate: Sat Jan 25 17:30:01 2014 +0100\n" <<
728           "\n" <<
729           "    Fake diff.\n" <<
730           "\n" <<
731           "diff --git a/libdiff2/diffmodel.cpp b/libdiff2/diffmodel.cpp\n" <<
732           "new file mode 100644\n" <<
733           "index a42e82d..a8da0c9\n" <<
734           "*** a/libdiff2/diffmodel.cpp\n" <<  // note the missing timestamps
735           "--- b/libdiff2/diffmodel.cpp\n" <<
736           "*************** DiffModel::DiffModel() :\n" <<
737           "*** 58,64 ****\n" <<
738           "  	m_sourceFile( "" ),\n" <<
739           "  	m_destinationFile( "" ),\n" <<
740           "  	m_sourceTimestamp( "" ),\n" <<
741           "! 	m_destinationTimestamp( "" ),\n" <<
742           "  	m_sourceRevision( "" ),\n" <<
743           "  	m_destinationRevision( "" ),\n" <<
744           "  	m_appliedCount( 0 ),\n" <<
745           "--- 58,64 ----\n" <<
746           "  	m_sourceFile( "" ),\n" <<
747           "  	m_destinationFile( "" ),\n" <<
748           "  	m_sourceTimestamp( "" ),\n" <<
749           "! 	m_destinationTimestamp( \"doh\" ),\n" <<
750           "  	m_sourceRevision( "" ),\n" <<
751           "  	m_destinationRevision( "" ),\n" <<
752           "  	m_appliedCount( 0 ),\n" <<
753           "*************** void DiffModel::splitSourceInPathAndFile\n" <<
754           "*** 84,89 ****\n" <<
755           "--- 84,91 ----\n" <<
756           "  	if( ( pos = m_source.lastIndexOf( \"/\" ) ) >= 0 )\n" <<
757           "  		m_sourcePath = m_source.mid( 0, pos+1 );\n" <<
758           "  \n" <<
759           "+ 	add_this;\n" <<
760           "+ \n" <<
761           "  	if( ( pos = m_source.lastIndexOf( \"/\" ) ) >= 0 )\n" <<
762           "  		m_sourceFile = m_source.mid( pos+1, m_source.length() - pos );\n" <<
763           "  	else\n";
764     Parser parser(nullptr);
765     bool malformed;
766     DiffModelList* models = parser.parse(patch, &malformed);
767     QVERIFY(!malformed);
768     QCOMPARE(models->size(), 1);
769     DiffModel* model = models->at(0);
770     QCOMPARE(model->differenceCount(), 2);
771     model->applyAllDifferences(true);
772     QVERIFY(model->differenceAt(0)->applied());
773     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 61);
774     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 61);
775     QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 87);
776     QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 87);
777 }
778 
779 static void
contextDiff2()780 contextDiff2()
781 {
782     QStringList patch;
783     patch <<
784           "*** a/libdiff2/diffmodel.cpp\n" <<
785           "--- b/libdiff2/diffmodel.cpp\n" <<
786           "***************\n" <<
787           "*** 55,60 **** DiffModel::DiffModel() :\n" << // note the  context here
788           "--- 55,61 ----\n" <<
789           "  	m_destination( "" ),\n" <<
790           "  	m_sourcePath( "" ),\n" <<
791           "  	m_destinationPath( "" ),\n" <<
792           "+ 	m_hoh ( "" );\n" <<
793           "  	m_sourceFile( "" ),\n" <<
794           "  	m_destinationFile( "" ),\n" <<
795           "  	m_sourceTimestamp( "" ),\n";
796 
797     Parser parser(nullptr);
798     bool malformed;
799     DiffModelList* models = parser.parse(patch, &malformed);
800     QVERIFY(!malformed);
801     QCOMPARE(models->size(), 1);
802     DiffModel* model = models->at(0);
803     QCOMPARE(model->differenceCount(), 1);
804     model->applyAllDifferences(true);
805     QVERIFY(model->differenceAt(0)->applied());
806     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 58);
807     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 58);
808 }
809 
testContextDiff()810 void InteractiveDiffTest::testContextDiff()
811 {
812     contextDiff1();
813     contextDiff2();
814 }
815 
testNormalDiff()816 void InteractiveDiffTest::testNormalDiff()
817 {
818     QStringList patch;
819     patch <<
820           "1c1\n" <<
821           "< a\n" <<
822           "---\n" <<
823           "> b\n";
824     Parser parser(nullptr);
825     bool malformed;
826     DiffModelList* models = parser.parse(patch, &malformed);
827     QVERIFY(!malformed);
828     QCOMPARE(models->size(), 1);
829     DiffModel* model = models->at(0);
830     QCOMPARE(model->differenceCount(), 1);
831     model->applyAllDifferences(true);
832     QVERIFY(model->differenceAt(0)->applied());
833     QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 1);
834     QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 1);
835 }
836 
837 QTEST_GUILESS_MAIN(InteractiveDiffTest);
838