Mercurial > hg4j
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 |