1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.diff;
12
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertSame;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17
18 import java.util.Arrays;
19 import java.util.List;
20
21 import org.eclipse.jgit.lib.AbbreviatedObjectId;
22 import org.eclipse.jgit.lib.FileMode;
23 import org.eclipse.jgit.lib.ObjectId;
24 import org.junit.Before;
25 import org.junit.Test;
26
27 public class RenameDetectorTest extends AbstractRenameDetectionTestCase {
28
29 private RenameDetector rd;
30
31 @Override
32 @Before
33 public void setUp() throws Exception {
34 super.setUp();
35 rd = new RenameDetector(db);
36 }
37
38 @Test
39 public void testExactRename_OneRename() throws Exception {
40 ObjectId foo = blob("foo");
41
42 DiffEntry a = DiffEntry.add(PATH_A, foo);
43 DiffEntry b = DiffEntry.delete(PATH_Q, foo);
44
45 rd.add(a);
46 rd.add(b);
47
48 List<DiffEntry> entries = rd.compute();
49 assertEquals(1, entries.size());
50 assertRename(b, a, 100, entries.get(0));
51 }
52
53 @Test
54 public void testExactRename_DifferentObjects() throws Exception {
55 ObjectId foo = blob("foo");
56 ObjectId bar = blob("bar");
57
58 DiffEntry a = DiffEntry.add(PATH_A, foo);
59 DiffEntry h = DiffEntry.add(PATH_H, foo);
60 DiffEntry q = DiffEntry.delete(PATH_Q, bar);
61
62 rd.add(a);
63 rd.add(h);
64 rd.add(q);
65
66 List<DiffEntry> entries = rd.compute();
67 assertEquals(3, entries.size());
68 assertSame(a, entries.get(0));
69 assertSame(h, entries.get(1));
70 assertSame(q, entries.get(2));
71 }
72
73 @Test
74 public void testExactRename_OneRenameOneModify() throws Exception {
75 ObjectId foo = blob("foo");
76 ObjectId bar = blob("bar");
77
78 DiffEntry a = DiffEntry.add(PATH_A, foo);
79 DiffEntry b = DiffEntry.delete(PATH_Q, foo);
80
81 DiffEntry c = DiffEntry.modify(PATH_H);
82 c.newId = c.oldId = AbbreviatedObjectId.fromObjectId(bar);
83
84 rd.add(a);
85 rd.add(b);
86 rd.add(c);
87
88 List<DiffEntry> entries = rd.compute();
89 assertEquals(2, entries.size());
90 assertRename(b, a, 100, entries.get(0));
91 assertSame(c, entries.get(1));
92 }
93
94 @Test
95 public void testExactRename_ManyRenames() throws Exception {
96 ObjectId foo = blob("foo");
97 ObjectId bar = blob("bar");
98
99 DiffEntry a = DiffEntry.add(PATH_A, foo);
100 DiffEntry b = DiffEntry.delete(PATH_Q, foo);
101
102 DiffEntry c = DiffEntry.add(PATH_H, bar);
103 DiffEntry d = DiffEntry.delete(PATH_B, bar);
104
105 rd.add(a);
106 rd.add(b);
107 rd.add(c);
108 rd.add(d);
109
110 List<DiffEntry> entries = rd.compute();
111 assertEquals(2, entries.size());
112 assertRename(b, a, 100, entries.get(0));
113 assertRename(d, c, 100, entries.get(1));
114 }
115
116 @Test
117 public void testExactRename_MultipleIdenticalDeletes() throws Exception {
118 ObjectId foo = blob("foo");
119
120 DiffEntry a = DiffEntry.delete(PATH_A, foo);
121 DiffEntry b = DiffEntry.delete(PATH_B, foo);
122
123 DiffEntry c = DiffEntry.delete(PATH_H, foo);
124 DiffEntry d = DiffEntry.add(PATH_Q, foo);
125
126 rd.add(a);
127 rd.add(b);
128 rd.add(c);
129 rd.add(d);
130
131
132 List<DiffEntry> entries = rd.compute();
133 assertEquals(3, entries.size());
134 assertEquals(b, entries.get(0));
135 assertEquals(c, entries.get(1));
136 assertRename(a, d, 100, entries.get(2));
137 }
138
139 @Test
140 public void testExactRename_PathBreaksTie() throws Exception {
141 ObjectId foo = blob("foo");
142
143 DiffEntry a = DiffEntry.add("src/com/foo/a.java", foo);
144 DiffEntry b = DiffEntry.delete("src/com/foo/b.java", foo);
145
146 DiffEntry c = DiffEntry.add("c.txt", foo);
147 DiffEntry d = DiffEntry.delete("d.txt", foo);
148 DiffEntry e = DiffEntry.add("the_e_file.txt", foo);
149
150
151 rd.add(a);
152 rd.add(d);
153 rd.add(e);
154 rd.add(b);
155 rd.add(c);
156
157 List<DiffEntry> entries = rd.compute();
158 assertEquals(3, entries.size());
159 assertRename(d, c, 100, entries.get(0));
160 assertRename(b, a, 100, entries.get(1));
161 assertCopy(d, e, 100, entries.get(2));
162 }
163
164 @Test
165 public void testExactRename_OneDeleteManyAdds() throws Exception {
166 ObjectId foo = blob("foo");
167
168 DiffEntry a = DiffEntry.add("src/com/foo/a.java", foo);
169 DiffEntry b = DiffEntry.add("src/com/foo/b.java", foo);
170 DiffEntry c = DiffEntry.add("c.txt", foo);
171
172 DiffEntry d = DiffEntry.delete("d.txt", foo);
173
174 rd.add(a);
175 rd.add(b);
176 rd.add(c);
177 rd.add(d);
178
179 List<DiffEntry> entries = rd.compute();
180 assertEquals(3, entries.size());
181 assertRename(d, c, 100, entries.get(0));
182 assertCopy(d, a, 100, entries.get(1));
183 assertCopy(d, b, 100, entries.get(2));
184 }
185
186 @Test
187 public void testExactRename_UnstagedFile() throws Exception {
188 ObjectId aId = blob("foo");
189 DiffEntry a = DiffEntry.delete(PATH_A, aId);
190 DiffEntry b = DiffEntry.add(PATH_B, aId);
191
192 rd.addAll(Arrays.asList(a, b));
193 List<DiffEntry> entries = rd.compute();
194
195 assertEquals(1, entries.size());
196 assertRename(a, b, 100, entries.get(0));
197 }
198
199 @Test
200 public void testInexactRename_OnePair() throws Exception {
201 ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
202 ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
203
204 DiffEntry a = DiffEntry.add(PATH_A, aId);
205 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
206
207 rd.add(a);
208 rd.add(b);
209
210 List<DiffEntry> entries = rd.compute();
211 assertEquals(1, entries.size());
212 assertRename(b, a, 66, entries.get(0));
213 }
214
215 @Test
216 public void testInexactRename_OneRenameTwoUnrelatedFiles() throws Exception {
217 ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
218 ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
219 DiffEntry a = DiffEntry.add(PATH_A, aId);
220 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
221
222 ObjectId cId = blob("some\nsort\nof\ntext\n");
223 ObjectId dId = blob("completely\nunrelated\ntext\n");
224 DiffEntry c = DiffEntry.add(PATH_B, cId);
225 DiffEntry d = DiffEntry.delete(PATH_H, dId);
226
227 rd.add(a);
228 rd.add(b);
229 rd.add(c);
230 rd.add(d);
231
232 List<DiffEntry> entries = rd.compute();
233 assertEquals(3, entries.size());
234 assertRename(b, a, 66, entries.get(0));
235 assertSame(c, entries.get(1));
236 assertSame(d, entries.get(2));
237 }
238
239 @Test
240 public void testInexactRename_LastByteDifferent() throws Exception {
241 ObjectId aId = blob("foo\nbar\na");
242 ObjectId bId = blob("foo\nbar\nb");
243
244 DiffEntry a = DiffEntry.add(PATH_A, aId);
245 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
246
247 rd.add(a);
248 rd.add(b);
249
250 List<DiffEntry> entries = rd.compute();
251 assertEquals(1, entries.size());
252 assertRename(b, a, 88, entries.get(0));
253 }
254
255 @Test
256 public void testInexactRename_NewlinesOnly() throws Exception {
257 ObjectId aId = blob("\n\n\n");
258 ObjectId bId = blob("\n\n\n\n");
259
260 DiffEntry a = DiffEntry.add(PATH_A, aId);
261 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
262
263 rd.add(a);
264 rd.add(b);
265
266 List<DiffEntry> entries = rd.compute();
267 assertEquals(1, entries.size());
268 assertRename(b, a, 74, entries.get(0));
269 }
270
271 @Test
272 public void testInexactRename_SameContentMultipleTimes() throws Exception {
273 ObjectId aId = blob("a\na\na\na\n");
274 ObjectId bId = blob("a\na\na\n");
275
276 DiffEntry a = DiffEntry.add(PATH_A, aId);
277 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
278
279 rd.add(a);
280 rd.add(b);
281
282 List<DiffEntry> entries = rd.compute();
283 assertEquals(1, entries.size());
284 assertRename(b, a, 74, entries.get(0));
285 }
286
287 @Test
288 public void testInexactRenames_OnePair2() throws Exception {
289 ObjectId aId = blob("ab\nab\nab\nac\nad\nae\n");
290 ObjectId bId = blob("ac\nab\nab\nab\naa\na0\na1\n");
291
292 DiffEntry a = DiffEntry.add(PATH_A, aId);
293 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
294
295 rd.add(a);
296 rd.add(b);
297 rd.setRenameScore(50);
298
299 List<DiffEntry> entries = rd.compute();
300 assertEquals(1, entries.size());
301 assertRename(b, a, 57, entries.get(0));
302 }
303
304 @Test
305 public void testNoRenames_SingleByteFiles() throws Exception {
306 ObjectId aId = blob("a");
307 ObjectId bId = blob("b");
308
309 DiffEntry a = DiffEntry.add(PATH_A, aId);
310 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
311
312 rd.add(a);
313 rd.add(b);
314
315 List<DiffEntry> entries = rd.compute();
316 assertEquals(2, entries.size());
317 assertSame(a, entries.get(0));
318 assertSame(b, entries.get(1));
319 }
320
321 @Test
322 public void testNoRenames_EmptyFile1() throws Exception {
323 ObjectId aId = blob("");
324 DiffEntry a = DiffEntry.add(PATH_A, aId);
325
326 rd.add(a);
327
328 List<DiffEntry> entries = rd.compute();
329 assertEquals(1, entries.size());
330 assertSame(a, entries.get(0));
331 }
332
333 @Test
334 public void testNoRenames_EmptyFile2() throws Exception {
335 ObjectId aId = blob("");
336 ObjectId bId = blob("blah");
337
338 DiffEntry a = DiffEntry.add(PATH_A, aId);
339 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
340
341 rd.add(a);
342 rd.add(b);
343
344 List<DiffEntry> entries = rd.compute();
345 assertEquals(2, entries.size());
346 assertSame(a, entries.get(0));
347 assertSame(b, entries.get(1));
348 }
349
350 @Test
351 public void testNoRenames_SymlinkAndFile() throws Exception {
352 ObjectId aId = blob("src/dest");
353
354 DiffEntry a = DiffEntry.add(PATH_A, aId);
355 DiffEntry b = DiffEntry.delete(PATH_Q, aId);
356 b.oldMode = FileMode.SYMLINK;
357
358 rd.add(a);
359 rd.add(b);
360
361 List<DiffEntry> entries = rd.compute();
362 assertEquals(2, entries.size());
363 assertSame(a, entries.get(0));
364 assertSame(b, entries.get(1));
365 }
366
367 @Test
368 public void testNoRenames_GitlinkAndFile() throws Exception {
369 ObjectId aId = blob("src/dest");
370
371 DiffEntry a = DiffEntry.add(PATH_A, aId);
372 DiffEntry b = DiffEntry.delete(PATH_Q, aId);
373 b.oldMode = FileMode.GITLINK;
374
375 rd.add(a);
376 rd.add(b);
377
378 List<DiffEntry> entries = rd.compute();
379 assertEquals(2, entries.size());
380 assertSame(a, entries.get(0));
381 assertSame(b, entries.get(1));
382 }
383
384 @Test
385 public void testNoRenames_SymlinkAndFileSamePath() throws Exception {
386 ObjectId aId = blob("src/dest");
387
388 DiffEntry a = DiffEntry.delete(PATH_A, aId);
389 DiffEntry b = DiffEntry.add(PATH_A, aId);
390 a.oldMode = FileMode.SYMLINK;
391
392 rd.add(a);
393 rd.add(b);
394
395
396 List<DiffEntry> entries = rd.compute();
397 assertEquals(2, entries.size());
398 assertSame(a, entries.get(0));
399 assertSame(b, entries.get(1));
400 }
401
402 @Test
403 public void testNoRenames_UntrackedFile() throws Exception {
404 ObjectId aId = blob("foo");
405 ObjectId bId = ObjectId
406 .fromString("3049eb6eee7e1318f4e78e799bf33f1e54af9cbf");
407
408 DiffEntry a = DiffEntry.delete(PATH_A, aId);
409 DiffEntry b = DiffEntry.add(PATH_B, bId);
410
411 rd.addAll(Arrays.asList(a, b));
412 List<DiffEntry> entries = rd.compute();
413
414 assertEquals(2, entries.size());
415 assertSame(a, entries.get(0));
416 assertSame(b, entries.get(1));
417 }
418
419 @Test
420 public void testBreakModify_BreakAll() throws Exception {
421 ObjectId aId = blob("foo");
422 ObjectId bId = blob("bar");
423
424 DiffEntry m = DiffEntry.modify(PATH_A);
425 m.oldId = AbbreviatedObjectId.fromObjectId(aId);
426 m.newId = AbbreviatedObjectId.fromObjectId(bId);
427
428 DiffEntry a = DiffEntry.add(PATH_B, aId);
429
430 rd.add(a);
431 rd.add(m);
432
433 rd.setBreakScore(101);
434
435 List<DiffEntry> entries = rd.compute();
436 assertEquals(2, entries.size());
437 assertAdd(PATH_A, bId, FileMode.REGULAR_FILE, entries.get(0));
438 assertRename(DiffEntry.breakModify(m).get(0), a, 100, entries.get(1));
439 }
440
441 @Test
442 public void testBreakModify_BreakNone() throws Exception {
443 ObjectId aId = blob("foo");
444 ObjectId bId = blob("bar");
445
446 DiffEntry m = DiffEntry.modify(PATH_A);
447 m.oldId = AbbreviatedObjectId.fromObjectId(aId);
448 m.newId = AbbreviatedObjectId.fromObjectId(bId);
449
450 DiffEntry a = DiffEntry.add(PATH_B, aId);
451
452 rd.add(a);
453 rd.add(m);
454
455 rd.setBreakScore(-1);
456
457 List<DiffEntry> entries = rd.compute();
458 assertEquals(2, entries.size());
459 assertSame(m, entries.get(0));
460 assertSame(a, entries.get(1));
461 }
462
463 @Test
464 public void testBreakModify_BreakBelowScore() throws Exception {
465 ObjectId aId = blob("foo");
466 ObjectId bId = blob("bar");
467
468 DiffEntry m = DiffEntry.modify(PATH_A);
469 m.oldId = AbbreviatedObjectId.fromObjectId(aId);
470 m.newId = AbbreviatedObjectId.fromObjectId(bId);
471
472 DiffEntry a = DiffEntry.add(PATH_B, aId);
473
474 rd.add(a);
475 rd.add(m);
476
477 rd.setBreakScore(20);
478
479 List<DiffEntry> entries = rd.compute();
480 assertEquals(2, entries.size());
481 assertAdd(PATH_A, bId, FileMode.REGULAR_FILE, entries.get(0));
482 assertRename(DiffEntry.breakModify(m).get(0), a, 100, entries.get(1));
483 }
484
485 @Test
486 public void testBreakModify_DontBreakAboveScore() throws Exception {
487 ObjectId aId = blob("blah\nblah\nfoo");
488 ObjectId bId = blob("blah\nblah\nbar");
489
490 DiffEntry m = DiffEntry.modify(PATH_A);
491 m.oldId = AbbreviatedObjectId.fromObjectId(aId);
492 m.newId = AbbreviatedObjectId.fromObjectId(bId);
493
494 DiffEntry a = DiffEntry.add(PATH_B, aId);
495
496 rd.add(a);
497 rd.add(m);
498
499 rd.setBreakScore(20);
500
501 List<DiffEntry> entries = rd.compute();
502 assertEquals(2, entries.size());
503 assertSame(m, entries.get(0));
504 assertSame(a, entries.get(1));
505 }
506
507 @Test
508 public void testBreakModify_RejoinIfUnpaired() throws Exception {
509 ObjectId aId = blob("foo");
510 ObjectId bId = blob("bar");
511
512 DiffEntry m = DiffEntry.modify(PATH_A);
513 m.oldId = AbbreviatedObjectId.fromObjectId(aId);
514 m.newId = AbbreviatedObjectId.fromObjectId(bId);
515
516 rd.add(m);
517
518 rd.setBreakScore(101);
519
520 List<DiffEntry> entries = rd.compute();
521 assertEquals(1, entries.size());
522
523 DiffEntry modify = entries.get(0);
524 assertEquals(m.oldPath, modify.oldPath);
525 assertEquals(m.oldId, modify.oldId);
526 assertEquals(m.oldMode, modify.oldMode);
527 assertEquals(m.newPath, modify.newPath);
528 assertEquals(m.newId, modify.newId);
529 assertEquals(m.newMode, modify.newMode);
530 assertEquals(m.changeType, modify.changeType);
531 assertEquals(0, modify.score);
532 }
533
534 @Test
535 public void testExactRename_LargeFile() throws Exception {
536 ObjectId aId = blob("blah\nblah\nfoo");
537
538 DiffEntry a = DiffEntry.add(PATH_A, aId);
539 DiffEntry b = DiffEntry.delete(PATH_Q, aId);
540
541 rd.add(a);
542 rd.add(b);
543
544
545 rd.setBigFileThreshold(10);
546 List<DiffEntry> entries = rd.compute();
547 assertEquals(1, entries.size());
548 assertRename(b, a, 100, entries.get(0));
549 }
550
551 @Test
552 public void testInexactRename_LargeFile() throws Exception {
553 ObjectId aId = blob("blah\nblah\nfoo");
554 ObjectId bId = blob("bla\nblah\nfoo");
555
556 DiffEntry a = DiffEntry.add(PATH_A, aId);
557 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
558
559 rd.add(a);
560 rd.add(b);
561
562 rd.setBigFileThreshold(10);
563
564
565 List<DiffEntry> entries = rd.compute();
566 assertEquals(2, entries.size());
567 assertAdd(PATH_A, aId, FileMode.REGULAR_FILE, entries.get(0));
568 assertDelete(PATH_Q, bId, FileMode.REGULAR_FILE, entries.get(1));
569 }
570
571 @Test
572 public void testExactRenameForBinaryFile_isIdentified() throws Exception {
573 ObjectId aId = blob("a\nb\nc\n\0\0\0\0d\n");
574
575 DiffEntry a = DiffEntry.add(PATH_A, aId);
576 DiffEntry b = DiffEntry.delete(PATH_Q, aId);
577
578 rd.add(a);
579 rd.add(b);
580
581 List<DiffEntry> entries = rd.compute();
582 assertEquals(1, entries.size());
583 assertRename(b, a, 100, entries.get(0));
584 }
585
586 @Test
587 public void testInexactRenameForBinaryFile_identifiedByDefault() throws Exception {
588 ObjectId aId = blob("a\nb\nc\n\0\0\0\0d\n");
589 ObjectId bId = blob("a\nb\nc\n\0\0\0d\n");
590
591 DiffEntry a = DiffEntry.add(PATH_A, aId);
592 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
593
594 rd.add(a);
595 rd.add(b);
596 rd.setRenameScore(40);
597
598 List<DiffEntry> entries = rd.compute();
599 assertEquals(1, entries.size());
600 assertRename(b, a, 50, entries.get(0));
601 }
602
603 @Test
604 public void testInexactRenameForBinaryFile_notIdentifiedIfSkipParameterSet() throws Exception {
605 ObjectId aId = blob("a\nb\nc\n\0\0\0\0d\n");
606 ObjectId bId = blob("a\nb\nc\n\0\0\0d\n");
607
608 DiffEntry a = DiffEntry.add(PATH_A, aId);
609 DiffEntry b = DiffEntry.delete(PATH_Q, bId);
610
611 rd.add(a);
612 rd.add(b);
613 rd.setRenameScore(40);
614 rd.setSkipContentRenamesForBinaryFiles(true);
615
616 List<DiffEntry> entries = rd.compute();
617 assertEquals(2, entries.size());
618 assertAdd(PATH_A, aId, FileMode.REGULAR_FILE, entries.get(0));
619 assertDelete(PATH_Q, bId, FileMode.REGULAR_FILE, entries.get(1));
620 }
621
622 @Test
623 public void testSetRenameScore_IllegalArgs() throws Exception {
624 try {
625 rd.setRenameScore(-1);
626 fail();
627 } catch (IllegalArgumentException e) {
628
629 }
630
631 try {
632 rd.setRenameScore(101);
633 fail();
634 } catch (IllegalArgumentException e) {
635
636 }
637 }
638
639 @Test
640 public void testRenameLimit() throws Exception {
641 ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
642 ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
643 DiffEntry a = DiffEntry.add(PATH_A, aId);
644 DiffEntry b = DiffEntry.delete(PATH_B, bId);
645
646 ObjectId cId = blob("a\nb\nc\nd\n");
647 ObjectId dId = blob("a\nb\nc\n");
648 DiffEntry c = DiffEntry.add(PATH_H, cId);
649 DiffEntry d = DiffEntry.delete(PATH_Q, dId);
650
651 rd.add(a);
652 rd.add(b);
653 rd.add(c);
654 rd.add(d);
655
656 rd.setRenameLimit(1);
657
658 assertTrue(rd.isOverRenameLimit());
659
660 List<DiffEntry> entries = rd.compute();
661 assertEquals(4, entries.size());
662 assertSame(a, entries.get(0));
663 assertSame(b, entries.get(1));
664 assertSame(c, entries.get(2));
665 assertSame(d, entries.get(3));
666 }
667 }