comparison test/org/tmatesoft/hg/test/TestBlame.java @ 570:36853bb80a35

Tests for HgAnnotateCommand with follow/no-follow option
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 11 Apr 2013 16:07:17 +0200
parents 8ed4f4f4f0a6
children e49f9d9513fa
comparison
equal deleted inserted replaced
569:c4fd1037bc6f 570:36853bb80a35
20 import static org.junit.Assert.assertTrue; 20 import static org.junit.Assert.assertTrue;
21 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; 21 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
22 import static org.tmatesoft.hg.repo.HgRepository.TIP; 22 import static org.tmatesoft.hg.repo.HgRepository.TIP;
23 23
24 import java.io.ByteArrayOutputStream; 24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException; 25 import java.io.File;
26 import java.io.PrintStream; 26 import java.io.PrintStream;
27 import java.util.ArrayList;
27 import java.util.Arrays; 28 import java.util.Arrays;
28 import java.util.LinkedHashSet; 29 import java.util.LinkedHashSet;
29 import java.util.LinkedList; 30 import java.util.LinkedList;
31 import java.util.List;
30 import java.util.ListIterator; 32 import java.util.ListIterator;
31 import java.util.regex.Matcher; 33 import java.util.regex.Matcher;
32 import java.util.regex.Pattern; 34 import java.util.regex.Pattern;
33 35
34 import org.junit.Assert; 36 import org.junit.Assert;
35 import org.junit.Rule; 37 import org.junit.Rule;
36 import org.junit.Test; 38 import org.junit.Test;
39 import org.tmatesoft.hg.core.HgAnnotateCommand;
40 import org.tmatesoft.hg.core.HgAnnotateCommand.LineInfo;
37 import org.tmatesoft.hg.core.HgCallbackTargetException; 41 import org.tmatesoft.hg.core.HgCallbackTargetException;
38 import org.tmatesoft.hg.core.HgIterateDirection; 42 import org.tmatesoft.hg.core.HgIterateDirection;
43 import org.tmatesoft.hg.core.HgRepoFacade;
39 import org.tmatesoft.hg.internal.FileAnnotation; 44 import org.tmatesoft.hg.internal.FileAnnotation;
40 import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor; 45 import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor;
41 import org.tmatesoft.hg.internal.FileAnnotation.LineInspector; 46 import org.tmatesoft.hg.internal.FileAnnotation.LineInspector;
42 import org.tmatesoft.hg.internal.IntVector; 47 import org.tmatesoft.hg.internal.IntVector;
43 import org.tmatesoft.hg.repo.HgBlameFacility; 48 import org.tmatesoft.hg.repo.HgBlameFacility;
48 import org.tmatesoft.hg.repo.HgBlameFacility.DeleteBlock; 53 import org.tmatesoft.hg.repo.HgBlameFacility.DeleteBlock;
49 import org.tmatesoft.hg.repo.HgBlameFacility.EqualBlock; 54 import org.tmatesoft.hg.repo.HgBlameFacility.EqualBlock;
50 import org.tmatesoft.hg.repo.HgDataFile; 55 import org.tmatesoft.hg.repo.HgDataFile;
51 import org.tmatesoft.hg.repo.HgLookup; 56 import org.tmatesoft.hg.repo.HgLookup;
52 import org.tmatesoft.hg.repo.HgRepository; 57 import org.tmatesoft.hg.repo.HgRepository;
58 import org.tmatesoft.hg.util.Path;
53 59
54 /** 60 /**
55 * 61 *
56 * @author Artem Tikhomirov 62 * @author Artem Tikhomirov
57 * @author TMate Software Ltd. 63 * @author TMate Software Ltd.
58 */ 64 */
59 public class TestBlame { 65 public class TestBlame {
60 66
61 @Rule 67 @Rule
62 public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); 68 public ErrorCollectorExt errorCollector = new ErrorCollectorExt();
63 private ExecHelper eh;
64 69
65 70
66 @Test 71 @Test
67 public void testSingleParentBlame() throws Exception { 72 public void testSingleParentBlame() throws Exception {
68 HgRepository repo = new HgLookup().detectFromWorkingDir(); 73 HgRepository repo = new HgLookup().detectFromWorkingDir();
83 @Test 88 @Test
84 public void testFileLineAnnotate1() throws Exception { 89 public void testFileLineAnnotate1() throws Exception {
85 HgRepository repo = new HgLookup().detectFromWorkingDir(); 90 HgRepository repo = new HgLookup().detectFromWorkingDir();
86 final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java"; 91 final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java";
87 HgDataFile df = repo.getFileNode(fname); 92 HgDataFile df = repo.getFileNode(fname);
88 OutputParser.Stub op = new OutputParser.Stub(); 93 AnnotateRunner ar = new AnnotateRunner(df.getPath(), null);
89 eh = new ExecHelper(op, null); 94
90 95 for (int cs : new int[] { 539, 541 /*, TIP */}) {
91 for (int startChangeset : new int[] { 539, 541 /*, TIP */}) { 96 ar.run(cs, false);
92 doLineAnnotateTest(df, startChangeset, op); 97 FileAnnotateInspector fa = new FileAnnotateInspector();
93 } 98 FileAnnotation.annotate(df, cs, fa);
94 } 99 doAnnotateLineCheck(cs, ar.getLines(), Arrays.asList(fa.lineRevisions), Arrays.asList(fa.lines));
95
96 private void doLineAnnotateTest(HgDataFile df, int cs, OutputParser.Stub op) throws HgCallbackTargetException, InterruptedException, IOException {
97 FileAnnotateInspector fa = new FileAnnotateInspector();
98 FileAnnotation.annotate(df, cs, fa);
99
100 op.reset();
101 eh.run("hg", "annotate", "--no-follow", "-r", cs == TIP ? "tip" : String.valueOf(cs), df.getPath().toString());
102
103 String[] hgAnnotateLines = splitLines(op.result());
104 assertTrue("[sanity]", hgAnnotateLines.length > 0);
105 assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, fa.lineRevisions.length);
106
107 for (int i = 0; i < fa.lineRevisions.length; i++) {
108 int hgAnnotateRevIndex = Integer.parseInt(hgAnnotateLines[i].substring(0, hgAnnotateLines[i].indexOf(':')).trim());
109 errorCollector.assertEquals(String.format("Revision mismatch for line %d (annotating rev: %d)", i+1, cs), hgAnnotateRevIndex, fa.lineRevisions[i]);
110 String hgAnnotateLine = hgAnnotateLines[i].substring(hgAnnotateLines[i].indexOf(':') + 1);
111 String apiLine = fa.line(i).trim();
112 errorCollector.assertEquals(hgAnnotateLine.trim(), apiLine);
113 } 100 }
114 } 101 }
115 102
116 @Test 103 @Test
117 public void testFileLineAnnotate2() throws Exception { 104 public void testFileLineAnnotate2() throws Exception {
118 HgRepository repo = Configuration.get().find("test-annotate"); 105 HgRepository repo = Configuration.get().find("test-annotate");
119 HgDataFile df = repo.getFileNode("file1"); 106 HgDataFile df = repo.getFileNode("file1");
120 OutputParser.Stub op = new OutputParser.Stub(); 107 AnnotateRunner ar = new AnnotateRunner(df.getPath(), repo.getWorkingDir());
121 eh = new ExecHelper(op, repo.getWorkingDir()); 108
122 for (int cs : new int[] { 4, 6 /*, 8 see below*/, TIP}) { 109 for (int cs : new int[] { 4, 6 /*, 8 see below*/, TIP}) {
123 doLineAnnotateTest(df, cs, op); 110 ar.run(cs, false);
111 FileAnnotateInspector fa = new FileAnnotateInspector();
112 FileAnnotation.annotate(df, cs, fa);
113 doAnnotateLineCheck(cs, ar.getLines(), Arrays.asList(fa.lineRevisions), Arrays.asList(fa.lines));
124 } 114 }
125 /*`hg annotate -r 8` and HgBlameFacility give different result 115 /*`hg annotate -r 8` and HgBlameFacility give different result
126 * for "r0, line 5" line, which was deleted in rev2 and restored back in 116 * for "r0, line 5" line, which was deleted in rev2 and restored back in
127 * rev4 (both in default branch), while branch with r3 and r6 kept the line intact. 117 * rev4 (both in default branch), while branch with r3 and r6 kept the line intact.
128 * HgBlame reports rev4 for the line, `hg annotate` gives original, rev0. 118 * HgBlame reports rev4 for the line, `hg annotate` gives original, rev0.
193 } 183 }
194 } 184 }
195 } 185 }
196 errorCollector.assertTrue(String.format("Annotate API reported excessive diff: %s ", apiResult.toString()), apiResult.isEmpty()); 186 errorCollector.assertTrue(String.format("Annotate API reported excessive diff: %s ", apiResult.toString()), apiResult.isEmpty());
197 } 187 }
188
189 @Test
190 public void testAnnotateCmdFollowNoFollow() throws Exception {
191 HgRepoFacade hgRepoFacade = new HgRepoFacade();
192 HgRepository repo = Configuration.get().find("test-annotate2");
193 hgRepoFacade.init(repo);
194 HgAnnotateCommand cmd = hgRepoFacade.createAnnotateCommand();
195 final Path fname = Path.create("file1b.txt");
196 final int changeset = TIP;
197 AnnotateInspector ai = new AnnotateInspector();
198
199 cmd.changeset(changeset);
200 // follow
201 cmd.file(fname);
202 cmd.execute(ai);
203 AnnotateRunner ar = new AnnotateRunner(fname, repo.getWorkingDir());
204 ar.run(changeset, true);
205 doAnnotateLineCheck(changeset, ar.getLines(), ai.changesets, ai.lines);
206
207 // no follow
208 cmd.file(fname, false);
209 ai = new AnnotateInspector();
210 cmd.execute(ai);
211 ar.run(changeset, false);
212 doAnnotateLineCheck(changeset, ar.getLines(), ai.changesets, ai.lines);
213 }
214
215 private void doAnnotateLineCheck(int cs, String[] hgAnnotateLines, List<Integer> cmdChangesets, List<String> cmdLines) {
216 assertTrue("[sanity]", hgAnnotateLines.length > 0);
217 assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, cmdLines.size());
218
219 for (int i = 0; i < cmdChangesets.size(); i++) {
220 int hgAnnotateRevIndex = Integer.parseInt(hgAnnotateLines[i].substring(0, hgAnnotateLines[i].indexOf(':')).trim());
221 errorCollector.assertEquals(String.format("Revision mismatch for line %d (annotating rev: %d)", i+1, cs), hgAnnotateRevIndex, cmdChangesets.get(i));
222 String hgAnnotateLine = hgAnnotateLines[i].substring(hgAnnotateLines[i].indexOf(':') + 1);
223 String apiLine = cmdLines.get(i).trim();
224 errorCollector.assertEquals(hgAnnotateLine.trim(), apiLine);
225 }
226 }
227
228 // FIXME HgWorkingCopyStatusCollector (and HgStatusCollector), with their ancestors (rev 59/69) have examples
229 // of *incorrect* assignment of common lines (like "}") - our impl doesn't process common lines in any special way
230 // while original diff lib does. Would be nice to behave as close to original, as possible.
198 231
199 private static String[] splitLines(CharSequence seq) { 232 private static String[] splitLines(CharSequence seq) {
200 int lineCount = 0; 233 int lineCount = 0;
201 for (int i = 0, x = seq.length(); i < x; i++) { 234 for (int i = 0, x = seq.length(); i < x; i++) {
202 if (seq.charAt(i) == '\n') { 235 if (seq.charAt(i) == '\n') {
296 public static void main(String[] args) throws Throwable { 329 public static void main(String[] args) throws Throwable {
297 // System.out.println(Arrays.equals(new String[0], splitLines(""))); 330 // System.out.println(Arrays.equals(new String[0], splitLines("")));
298 // System.out.println(Arrays.equals(new String[] { "abc" }, splitLines("abc"))); 331 // System.out.println(Arrays.equals(new String[] { "abc" }, splitLines("abc")));
299 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc"))); 332 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc")));
300 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc\n"))); 333 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc\n")));
301 new TestBlame().ccc(); 334 TestBlame tt = new TestBlame();
335 tt.ccc();
302 } 336 }
303 337
304 private static class DiffOutInspector implements HgBlameFacility.Inspector { 338 private static class DiffOutInspector implements HgBlameFacility.Inspector {
305 private final PrintStream out; 339 private final PrintStream out;
306 private boolean dumpRevs; 340 private boolean dumpRevs;
388 } while (lineStart < seq.length()); 422 } while (lineStart < seq.length());
389 } 423 }
390 } 424 }
391 425
392 private static class FileAnnotateInspector implements LineInspector { 426 private static class FileAnnotateInspector implements LineInspector {
393 private int[] lineRevisions; 427 private Integer[] lineRevisions;
394 private String[] lines; 428 private String[] lines;
395 429
396 FileAnnotateInspector() { 430 FileAnnotateInspector() {
397 } 431 }
398 432
399 public void line(int lineNumber, int changesetRevIndex, BlockData lineContent, LineDescriptor ld) { 433 public void line(int lineNumber, int changesetRevIndex, BlockData lineContent, LineDescriptor ld) {
400 if (lineRevisions == null) { 434 if (lineRevisions == null) {
401 lineRevisions = new int [ld.totalLines()]; 435 lineRevisions = new Integer[ld.totalLines()];
402 Arrays.fill(lineRevisions, NO_REVISION); 436 Arrays.fill(lineRevisions, NO_REVISION);
403 lines = new String[ld.totalLines()]; 437 lines = new String[ld.totalLines()];
404 } 438 }
405 lineRevisions[lineNumber] = changesetRevIndex; 439 lineRevisions[lineNumber] = changesetRevIndex;
406 lines[lineNumber] = new String(lineContent.asArray()); 440 lines[lineNumber] = new String(lineContent.asArray());
449 String content = new String(lines.asArray()); 483 String content = new String(lines.asArray());
450 System.out.printf("%3d:%s:[%d..%d):\n%s", cset, marker, first, first+length, content); 484 System.out.printf("%3d:%s:[%d..%d):\n%s", cset, marker, first, first+length, content);
451 } 485 }
452 } 486 }
453 } 487 }
488
489 static class AnnotateInspector implements HgAnnotateCommand.Inspector {
490 private int lineNumber = 1;
491 public final ArrayList<String> lines = new ArrayList<String>();
492 public final ArrayList<Integer> changesets = new ArrayList<Integer>();
493
494 public void next(LineInfo lineInfo) throws HgCallbackTargetException {
495 Assert.assertEquals(lineInfo.getLineNumber(), lineNumber);
496 lineNumber++;
497 lines.add(new String(lineInfo.getContent()));
498 changesets.add(lineInfo.getChangesetIndex());
499 }
500 }
501
502 private static class AnnotateRunner {
503 private final ExecHelper eh;
504 private final OutputParser.Stub op;
505 private final Path file;
506
507 public AnnotateRunner(Path filePath, File repoDir) {
508 file = filePath;
509 op = new OutputParser.Stub();
510 eh = new ExecHelper(op, repoDir);
511 }
512
513 public void run(int cset, boolean follow) throws Exception {
514 op.reset();
515 ArrayList<String> args = new ArrayList<String>();
516 args.add("hg");
517 args.add("annotate");
518 args.add("-r");
519 args.add(cset == TIP ? "tip" : String.valueOf(cset));
520 if (!follow) {
521 args.add("--no-follow");
522 }
523 args.add(file.toString());
524 eh.run(args);
525 }
526
527 public String[] getLines() {
528 return splitLines(op.result());
529 }
530 }
454 } 531 }