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