comparison test/org/tmatesoft/hg/test/TestBlame.java @ 557:b9e5ac26dd83

Annotate: Line annotation needs true line position from merged blocks; test-annotate repo updated to show elements from both parents in the merged revision
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Sun, 24 Feb 2013 00:11:40 +0100
parents e55f17a7a195
children 154718ae23ed
comparison
equal deleted inserted replaced
556:e55f17a7a195 557:b9e5ac26dd83
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.PrintStream; 26 import java.io.PrintStream;
26 import java.util.Arrays; 27 import java.util.Arrays;
27 import java.util.LinkedHashSet; 28 import java.util.LinkedHashSet;
28 import java.util.LinkedList; 29 import java.util.LinkedList;
30 import java.util.ListIterator;
31 import java.util.regex.Matcher;
29 import java.util.regex.Pattern; 32 import java.util.regex.Pattern;
30 33
31 import org.junit.Assert; 34 import org.junit.Assert;
32 import org.junit.Rule; 35 import org.junit.Rule;
33 import org.junit.Test; 36 import org.junit.Test;
35 import org.tmatesoft.hg.internal.FileAnnotation; 38 import org.tmatesoft.hg.internal.FileAnnotation;
36 import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor; 39 import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor;
37 import org.tmatesoft.hg.internal.FileAnnotation.LineInspector; 40 import org.tmatesoft.hg.internal.FileAnnotation.LineInspector;
38 import org.tmatesoft.hg.internal.IntVector; 41 import org.tmatesoft.hg.internal.IntVector;
39 import org.tmatesoft.hg.repo.HgBlameFacility; 42 import org.tmatesoft.hg.repo.HgBlameFacility;
40 import org.tmatesoft.hg.repo.HgDataFile;
41 import org.tmatesoft.hg.repo.HgLookup;
42 import org.tmatesoft.hg.repo.HgRepository;
43 import org.tmatesoft.hg.repo.HgBlameFacility.AddBlock; 43 import org.tmatesoft.hg.repo.HgBlameFacility.AddBlock;
44 import org.tmatesoft.hg.repo.HgBlameFacility.Block; 44 import org.tmatesoft.hg.repo.HgBlameFacility.Block;
45 import org.tmatesoft.hg.repo.HgBlameFacility.BlockData; 45 import org.tmatesoft.hg.repo.HgBlameFacility.BlockData;
46 import org.tmatesoft.hg.repo.HgBlameFacility.ChangeBlock; 46 import org.tmatesoft.hg.repo.HgBlameFacility.ChangeBlock;
47 import org.tmatesoft.hg.repo.HgBlameFacility.DeleteBlock; 47 import org.tmatesoft.hg.repo.HgBlameFacility.DeleteBlock;
48 import org.tmatesoft.hg.repo.HgBlameFacility.EqualBlock; 48 import org.tmatesoft.hg.repo.HgBlameFacility.EqualBlock;
49 import org.tmatesoft.hg.repo.HgDataFile;
50 import org.tmatesoft.hg.repo.HgLookup;
51 import org.tmatesoft.hg.repo.HgRepository;
49 52
50 /** 53 /**
51 * 54 *
52 * @author Artem Tikhomirov 55 * @author Artem Tikhomirov
53 * @author TMate Software Ltd. 56 * @author TMate Software Ltd.
54 */ 57 */
55 public class TestBlame { 58 public class TestBlame {
56 59
57 @Rule 60 @Rule
58 public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); 61 public ErrorCollectorExt errorCollector = new ErrorCollectorExt();
62 private ExecHelper eh;
59 63
60 64
61 @Test 65 @Test
62 public void testSingleParentBlame() throws Exception { 66 public void testSingleParentBlame() throws Exception {
63 HgRepository repo = new HgLookup().detectFromWorkingDir(); 67 HgRepository repo = new HgLookup().detectFromWorkingDir();
74 String[] expected = splitLines(gp.result()); 78 String[] expected = splitLines(gp.result());
75 Assert.assertArrayEquals(expected, apiResult); 79 Assert.assertArrayEquals(expected, apiResult);
76 } 80 }
77 81
78 @Test 82 @Test
79 public void testFileAnnotate() throws Exception { 83 public void testFileLineAnnotate1() throws Exception {
80 HgRepository repo = new HgLookup().detectFromWorkingDir(); 84 HgRepository repo = new HgLookup().detectFromWorkingDir();
81 final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java"; 85 final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java";
82 HgDataFile df = repo.getFileNode(fname); 86 HgDataFile df = repo.getFileNode(fname);
83 OutputParser.Stub op = new OutputParser.Stub(); 87 OutputParser.Stub op = new OutputParser.Stub();
84 ExecHelper eh = new ExecHelper(op, null); 88 eh = new ExecHelper(op, null);
85 89
86 for (int startChangeset : new int[] { 539, 541 /*, TIP */}) { 90 for (int startChangeset : new int[] { 539, 541 /*, TIP */}) {
87 FileAnnotateInspector fa = new FileAnnotateInspector(); 91 doLineAnnotateTest(df, startChangeset, op);
88 FileAnnotation.annotate(df, startChangeset, fa); 92 }
89 93 }
90 94
91 op.reset(); 95 private void doLineAnnotateTest(HgDataFile df, int cs, OutputParser.Stub op) throws InterruptedException, IOException {
92 eh.run("hg", "annotate", "-r", startChangeset == TIP ? "tip" : String.valueOf(startChangeset), fname); 96 FileAnnotateInspector fa = new FileAnnotateInspector();
93 97 FileAnnotation.annotate(df, cs, fa);
94 String[] hgAnnotateLines = splitLines(op.result()); 98
95 assertTrue("[sanity]", hgAnnotateLines.length > 0); 99 op.reset();
96 assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, fa.lineRevisions.length); 100 eh.run("hg", "annotate", "-r", cs == TIP ? "tip" : String.valueOf(cs), df.getPath().toString());
97 101
98 for (int i = 0; i < fa.lineRevisions.length; i++) { 102 String[] hgAnnotateLines = splitLines(op.result());
99 int hgAnnotateRevIndex = Integer.parseInt(hgAnnotateLines[i].substring(0, hgAnnotateLines[i].indexOf(':'))); 103 assertTrue("[sanity]", hgAnnotateLines.length > 0);
100 errorCollector.assertEquals(String.format("Revision mismatch for line %d", i+1), hgAnnotateRevIndex, fa.lineRevisions[i]); 104 assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, fa.lineRevisions.length);
101 } 105
106 for (int i = 0; i < fa.lineRevisions.length; i++) {
107 int hgAnnotateRevIndex = Integer.parseInt(hgAnnotateLines[i].substring(0, hgAnnotateLines[i].indexOf(':')));
108 errorCollector.assertEquals(String.format("Revision mismatch for line %d", i+1), hgAnnotateRevIndex, fa.lineRevisions[i]);
109 String hgAnnotateLine = hgAnnotateLines[i].substring(hgAnnotateLines[i].indexOf(':') + 1);
110 String apiLine = fa.line(i).trim();
111 errorCollector.assertEquals(hgAnnotateLine.trim(), apiLine);
112 }
113 }
114
115 @Test
116 public void testFileLineAnnotate2() throws Exception {
117 HgRepository repo = Configuration.get().find("test-annotate");
118 HgDataFile df = repo.getFileNode("file1");
119 OutputParser.Stub op = new OutputParser.Stub();
120 eh = new ExecHelper(op, repo.getWorkingDir());
121 for (int cs : new int[] { 4, 6, TIP/*, 8 FIXME find out how come hg annotate doesn't see re-added line in rev4*/}) {
122 doLineAnnotateTest(df, cs, op);
102 } 123 }
103 } 124 }
104 125
105 @Test 126 @Test
106 public void testComplexHistoryAnnotate() throws Exception { 127 public void testComplexHistoryAnnotate() throws Exception {
109 HgBlameFacility af = new HgBlameFacility(); 130 HgBlameFacility af = new HgBlameFacility();
110 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 131 ByteArrayOutputStream bos = new ByteArrayOutputStream();
111 DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos)); 132 DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos));
112 af.annotate(df, TIP, dump, HgIterateDirection.OldToNew); 133 af.annotate(df, TIP, dump, HgIterateDirection.OldToNew);
113 LinkedList<String> apiResult = new LinkedList<String>(Arrays.asList(splitLines(bos.toString()))); 134 LinkedList<String> apiResult = new LinkedList<String>(Arrays.asList(splitLines(bos.toString())));
135
136 /*
137 * FIXME this is an ugly hack to deal with the way `hg diff -c <mergeRev>` describes the change
138 * and our merge handling approach. For merged revision m, and lines changed both in p1 and p2
139 * we report lines from p2 as pure additions, regardless of intersecting p1 changes (which
140 * are reported as deletions, if no sufficient changed lines in m found)
141 * So, here we try to combine deletion that follows a change (based on identical insertionPoint)
142 * into a single change
143 * To fix, need to find better approach to find out reference info (i.e. `hg diff -c` is flawed in this case,
144 * as it uses first parent only).
145 */
146 Pattern fix = Pattern.compile("@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+) @@");
147 int v1, v2, v3, v4;
148 v1 = v2 = v3 = v4 = -1;
149 for (ListIterator<String> it = apiResult.listIterator(); it.hasNext();) {
150 String n = it.next();
151 Matcher m = fix.matcher(n);
152 if (m.find()) {
153 int d1 = Integer.parseInt(m.group(1));
154 int d2 = Integer.parseInt(m.group(2));
155 int d3 = Integer.parseInt(m.group(3));
156 int d4 = Integer.parseInt(m.group(4));
157 if (v1 == d1 && d4 == 0) {
158 it.previous(); // shift to current element
159 it.previous(); // to real previous
160 it.remove();
161 it.next();
162 it.set(String.format("@@ -%d,%d +%d,%d @@", v1, v2+d2, v3, v4));
163 }
164 v1 = d1;
165 v2 = d2;
166 v3 = d3;
167 v4 = d4;
168 }
169 }
114 170
115 LineGrepOutputParser gp = new LineGrepOutputParser("^@@.+"); 171 LineGrepOutputParser gp = new LineGrepOutputParser("^@@.+");
116 ExecHelper eh = new ExecHelper(gp, repo.getWorkingDir()); 172 ExecHelper eh = new ExecHelper(gp, repo.getWorkingDir());
117 for (int cs : dump.getReportedTargetRevisions()) { 173 for (int cs : dump.getReportedTargetRevisions()) {
118 gp.reset(); 174 gp.reset();
197 253
198 private void ccc() throws Exception { 254 private void ccc() throws Exception {
199 HgRepository repo = new HgLookup().detect("/home/artem/hg/junit-test-repos/test-annotate/"); 255 HgRepository repo = new HgLookup().detect("/home/artem/hg/junit-test-repos/test-annotate/");
200 HgDataFile df = repo.getFileNode("file1"); 256 HgDataFile df = repo.getFileNode("file1");
201 HgBlameFacility af = new HgBlameFacility(); 257 HgBlameFacility af = new HgBlameFacility();
202 DiffOutInspector dump = new DiffOutInspector(System.out); 258 // DiffOutInspector dump = new DiffOutInspector(System.out);
203 dump.needRevisions(true); 259 // dump.needRevisions(true);
204 af.annotate(df, TIP, dump, HgIterateDirection.OldToNew); 260 // af.annotate(df, TIP, dump, HgIterateDirection.OldToNew);
205 System.out.println(); 261 // System.out.println();
206 af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld); 262 // af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld);
207 System.out.println(); 263 // System.out.println();
208 af.annotate(df, TIP, new LineDumpInspector(false), HgIterateDirection.NewToOld); 264 // af.annotate(df, TIP, new LineDumpInspector(false), HgIterateDirection.NewToOld);
209 System.out.println(); 265 // System.out.println();
210 FileAnnotateInspector fa = new FileAnnotateInspector(); 266 FileAnnotateInspector fa = new FileAnnotateInspector();
211 FileAnnotation.annotate(df, TIP, fa); 267 FileAnnotation.annotate(df, TIP, fa); //4,6,TIP
212 for (int i = 0; i < fa.lineRevisions.length; i++) { 268 for (int i = 0; i < fa.lineRevisions.length; i++) {
213 System.out.printf("%d: LINE %d\n", fa.lineRevisions[i], i+1); 269 System.out.printf("%d: %s", fa.lineRevisions[i], fa.line(i) == null ? "null\n" : fa.line(i));
214 } 270 }
215 } 271 }
216 272
217 public static void main(String[] args) throws Exception { 273 public static void main(String[] args) throws Exception {
218 // System.out.println(Arrays.equals(new String[0], splitLines(""))); 274 // System.out.println(Arrays.equals(new String[0], splitLines("")));
310 } 366 }
311 } 367 }
312 368
313 private static class FileAnnotateInspector implements LineInspector { 369 private static class FileAnnotateInspector implements LineInspector {
314 private int[] lineRevisions; 370 private int[] lineRevisions;
371 private String[] lines;
315 372
316 FileAnnotateInspector() { 373 FileAnnotateInspector() {
317 } 374 }
318 375
319 public void line(int lineNumber, int changesetRevIndex, LineDescriptor ld) { 376 public void line(int lineNumber, int changesetRevIndex, BlockData lineContent, LineDescriptor ld) {
320 if (lineRevisions == null) { 377 if (lineRevisions == null) {
321 lineRevisions = new int [ld.totalLines()]; 378 lineRevisions = new int [ld.totalLines()];
322 Arrays.fill(lineRevisions, NO_REVISION); 379 Arrays.fill(lineRevisions, NO_REVISION);
380 lines = new String[ld.totalLines()];
323 } 381 }
324 lineRevisions[lineNumber] = changesetRevIndex; 382 lineRevisions[lineNumber] = changesetRevIndex;
383 lines[lineNumber] = new String(lineContent.asArray());
384 }
385
386 public String line(int i) {
387 return lines[i];
325 } 388 }
326 } 389 }
327 390
328 private static class LineDumpInspector implements HgBlameFacility.BlockInspector { 391 private static class LineDumpInspector implements HgBlameFacility.BlockInspector {
329 392