Mercurial > hg4j
comparison test/org/tmatesoft/hg/test/TestBlame.java @ 545:15b406c7cd9d
First round of annotate file is functional
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 15 Feb 2013 22:15:13 +0100 |
| parents | 7f5998a9619d |
| children | cd78e8b9d7bc |
comparison
equal
deleted
inserted
replaced
| 544:7f5998a9619d | 545:15b406c7cd9d |
|---|---|
| 14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.test; | 17 package org.tmatesoft.hg.test; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; | |
| 20 | |
| 19 import java.io.ByteArrayOutputStream; | 21 import java.io.ByteArrayOutputStream; |
| 20 import java.io.PrintStream; | 22 import java.io.PrintStream; |
| 21 import java.util.Arrays; | 23 import java.util.Arrays; |
| 24 import java.util.LinkedList; | |
| 22 import java.util.regex.Pattern; | 25 import java.util.regex.Pattern; |
| 23 | 26 |
| 24 import org.junit.Assert; | 27 import org.junit.Assert; |
| 25 import org.junit.Test; | 28 import org.junit.Test; |
| 26 import org.tmatesoft.hg.internal.AnnotateFacility; | 29 import org.tmatesoft.hg.internal.AnnotateFacility; |
| 27 import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock; | 30 import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock; |
| 28 import org.tmatesoft.hg.internal.AnnotateFacility.Block; | |
| 29 import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock; | 31 import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock; |
| 30 import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock; | 32 import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock; |
| 33 import org.tmatesoft.hg.internal.AnnotateFacility.EqualBlock; | |
| 31 import org.tmatesoft.hg.internal.IntMap; | 34 import org.tmatesoft.hg.internal.IntMap; |
| 35 import org.tmatesoft.hg.internal.IntVector; | |
| 32 import org.tmatesoft.hg.repo.HgDataFile; | 36 import org.tmatesoft.hg.repo.HgDataFile; |
| 33 import org.tmatesoft.hg.repo.HgLookup; | 37 import org.tmatesoft.hg.repo.HgLookup; |
| 34 import org.tmatesoft.hg.repo.HgRepository; | 38 import org.tmatesoft.hg.repo.HgRepository; |
| 35 | 39 |
| 36 /** | 40 /** |
| 56 String[] apiResult = splitLines(bos.toString()); | 60 String[] apiResult = splitLines(bos.toString()); |
| 57 String[] expected = splitLines(gp.result()); | 61 String[] expected = splitLines(gp.result()); |
| 58 Assert.assertArrayEquals(expected, apiResult); | 62 Assert.assertArrayEquals(expected, apiResult); |
| 59 } | 63 } |
| 60 | 64 |
| 65 @Test | |
| 66 public void testFileAnnotate() throws Exception { | |
| 67 HgRepository repo = new HgLookup().detectFromWorkingDir(); | |
| 68 final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java"; | |
| 69 final int checkChangeset = 539; | |
| 70 HgDataFile df = repo.getFileNode(fname); | |
| 71 AnnotateFacility af = new AnnotateFacility(); | |
| 72 System.out.println("536 -> 539"); | |
| 73 af.annotateChange(df, checkChangeset, new DiffOutInspector(System.out)); | |
| 74 System.out.println("531 -> 536"); | |
| 75 af.annotateChange(df, 536, new DiffOutInspector(System.out)); | |
| 76 System.out.println(" -1 -> 531"); | |
| 77 af.annotateChange(df, 531, new DiffOutInspector(System.out)); | |
| 78 | |
| 79 FileAnnotation fa = new FileAnnotation(); | |
| 80 af.annotateChange(df, checkChangeset, fa); | |
| 81 af.annotateChange(df, 536, fa); | |
| 82 af.annotateChange(df, 531, fa); | |
| 83 for (int i = 0; i < fa.lineRevisions.length; i++) { | |
| 84 System.out.printf("%3d: %d\n", fa.lineRevisions[i], i+1); | |
| 85 } | |
| 86 } | |
| 87 | |
| 61 private static String[] splitLines(CharSequence seq) { | 88 private static String[] splitLines(CharSequence seq) { |
| 62 int lineCount = 0; | 89 int lineCount = 0; |
| 63 for (int i = 0, x = seq.length(); i < x; i++) { | 90 for (int i = 0, x = seq.length(); i < x; i++) { |
| 64 if (seq.charAt(i) == '\n') { | 91 if (seq.charAt(i) == '\n') { |
| 65 lineCount++; | 92 lineCount++; |
| 99 } | 126 } |
| 100 } | 127 } |
| 101 } | 128 } |
| 102 | 129 |
| 103 public static void main(String[] args) throws Exception { | 130 public static void main(String[] args) throws Exception { |
| 104 System.out.println(Arrays.equals(new String[0], splitLines(""))); | 131 // System.out.println(Arrays.equals(new String[0], splitLines(""))); |
| 105 System.out.println(Arrays.equals(new String[] { "abc" }, splitLines("abc"))); | 132 // System.out.println(Arrays.equals(new String[] { "abc" }, splitLines("abc"))); |
| 106 new TestBlame().testSingleParentBlame(); | 133 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc"))); |
| 134 // System.out.println(Arrays.equals(new String[] { "a", "bc" }, splitLines("a\nbc\n"))); | |
| 135 new TestBlame().testFileAnnotate(); | |
| 107 } | 136 } |
| 108 | 137 |
| 109 static class DiffOutInspector implements AnnotateFacility.Inspector { | 138 static class DiffOutInspector implements AnnotateFacility.Inspector { |
| 110 private final PrintStream out; | 139 private final PrintStream out; |
| 111 | 140 |
| 112 DiffOutInspector(PrintStream ps) { | 141 DiffOutInspector(PrintStream ps) { |
| 113 out = ps; | 142 out = ps; |
| 114 } | 143 } |
| 115 | 144 |
| 116 public void same(Block block) { | 145 public void same(EqualBlock block) { |
| 117 // nothing | 146 // nothing |
| 118 } | 147 } |
| 119 | 148 |
| 120 public void deleted(DeleteBlock block) { | 149 public void deleted(DeleteBlock block) { |
| 121 out.printf("@@ -%d,%d +%d,0 @@\n", block.firstRemovedLine() + 1, block.totalRemovedLines(), block.removedAt()); | 150 out.printf("@@ -%d,%d +%d,0 @@\n", block.firstRemovedLine() + 1, block.totalRemovedLines(), block.removedAt()); |
| 169 } | 198 } |
| 170 lineStart = ++lineEnd; | 199 lineStart = ++lineEnd; |
| 171 } while (lineStart < seq.length()); | 200 } while (lineStart < seq.length()); |
| 172 } | 201 } |
| 173 } | 202 } |
| 203 | |
| 204 private static class FileAnnotation implements AnnotateFacility.InspectorEx { | |
| 205 private int[] lineRevisions; | |
| 206 private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>(); | |
| 207 private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>(); | |
| 208 // keeps <startSeq1, startSeq2, len> of equal blocks | |
| 209 // XXX smth like IntSliceVector to access triples (or slices of any size, in fact) | |
| 210 // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice) | |
| 211 // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2) | |
| 212 private IntVector identical = new IntVector(20*3, 2*3); | |
| 213 private IntVector newIdentical = new IntVector(20*3, 2*3); | |
| 214 | |
| 215 public FileAnnotation() { | |
| 216 } | |
| 217 | |
| 218 public void start(int originLineCount, int targetLineCount) { | |
| 219 if (lineRevisions == null) { | |
| 220 lineRevisions = new int [targetLineCount]; | |
| 221 Arrays.fill(lineRevisions, NO_REVISION); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 // private static void ppp(IntVector v) { | |
| 226 // for (int i = 0; i < v.size(); i+= 3) { | |
| 227 // int len = v.get(i+2); | |
| 228 // System.out.printf("[%d..%d) == [%d..%d); ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len); | |
| 229 // } | |
| 230 // System.out.println(); | |
| 231 // } | |
| 232 | |
| 233 public void done() { | |
| 234 if (identical.size() > 0) { | |
| 235 // update line numbers of the intermediate target to point to ultimate target's line numbers | |
| 236 IntVector v = new IntVector(identical.size(), 2*3); | |
| 237 for (int i = 0; i < newIdentical.size(); i+= 3) { | |
| 238 int originLine = newIdentical.get(i); | |
| 239 int targetLine = newIdentical.get(i+1); | |
| 240 int length = newIdentical.get(i+2); | |
| 241 int startTargetLine = -1, startOriginLine = -1, c = 0; | |
| 242 for (int j = 0; j < length; j++) { | |
| 243 int lnInFinal = mapLineIndex(targetLine + j); | |
| 244 if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) { | |
| 245 // the line is not among "same" in ultimate origin | |
| 246 // or belongs to another/next "same" chunk | |
| 247 if (startOriginLine == -1) { | |
| 248 continue; | |
| 249 } | |
| 250 v.add(startOriginLine); | |
| 251 v.add(startTargetLine); | |
| 252 v.add(c); | |
| 253 c = 0; | |
| 254 startOriginLine = startTargetLine = -1; | |
| 255 // fall-through to check if it's not complete miss but a next chunk | |
| 256 } | |
| 257 if (lnInFinal != -1) { | |
| 258 if (startOriginLine == -1) { | |
| 259 startOriginLine = originLine + j; | |
| 260 startTargetLine = lnInFinal; | |
| 261 c = 1; | |
| 262 } else { | |
| 263 assert lnInFinal == startTargetLine + c; | |
| 264 c++; | |
| 265 } | |
| 266 } | |
| 267 } | |
| 268 if (startOriginLine != -1) { | |
| 269 assert c > 0; | |
| 270 v.add(startOriginLine); | |
| 271 v.add(startTargetLine); | |
| 272 v.add(c); | |
| 273 } | |
| 274 } | |
| 275 newIdentical.clear(); | |
| 276 identical = v; | |
| 277 } else { | |
| 278 IntVector li = newIdentical; | |
| 279 newIdentical = identical; | |
| 280 identical = li; | |
| 281 } | |
| 282 LinkedList<DeleteBlock> ld = newDeleted; | |
| 283 deleted.clear(); | |
| 284 newDeleted = deleted; | |
| 285 deleted = ld; | |
| 286 } | |
| 287 | |
| 288 public void same(EqualBlock block) { | |
| 289 newIdentical.add(block.originStart()); | |
| 290 newIdentical.add(block.targetStart()); | |
| 291 newIdentical.add(block.length()); | |
| 292 } | |
| 293 | |
| 294 public void added(AddBlock block) { | |
| 295 for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) { | |
| 296 int lnInFinal = mapLineIndex(ln); | |
| 297 if (lnInFinal != -1 && historyUnknown(lnInFinal)) { | |
| 298 lineRevisions[lnInFinal] = block.targetChangesetIndex(); | |
| 299 } | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 public void changed(ChangeBlock block) { | |
| 304 deleted(block); | |
| 305 added(block); | |
| 306 } | |
| 307 | |
| 308 public void deleted(DeleteBlock block) { | |
| 309 newDeleted.add(block); | |
| 310 } | |
| 311 | |
| 312 private boolean historyUnknown(int lineNumber) { | |
| 313 return lineRevisions[lineNumber] == NO_REVISION; | |
| 314 } | |
| 315 | |
| 316 private boolean isDeleted(int line) { | |
| 317 for (DeleteBlock b : deleted) { | |
| 318 if (b.firstRemovedLine() > line) { | |
| 319 break; | |
| 320 } | |
| 321 // line >= b.firstRemovedLine | |
| 322 if (b.firstRemovedLine() + b.totalRemovedLines() > line) { | |
| 323 return true; | |
| 324 } | |
| 325 } | |
| 326 return false; | |
| 327 } | |
| 328 | |
| 329 // map target lines to the lines of the revision being annotated (the one that came first) | |
| 330 private int mapLineIndex(int ln) { | |
| 331 if (isDeleted(ln)) { | |
| 332 return -1; | |
| 333 } | |
| 334 if (identical.isEmpty()) { | |
| 335 return ln; | |
| 336 } | |
| 337 for (int i = 0; i < identical.size(); i += 3) { | |
| 338 final int originStart = identical.get(i); | |
| 339 if (originStart > ln) { | |
| 340 assert false; | |
| 341 return -1; | |
| 342 } | |
| 343 // ln >= b.originStart | |
| 344 final int length = identical.get(i+2); | |
| 345 if (originStart + length > ln) { | |
| 346 int targetStart = identical.get(i+1); | |
| 347 return targetStart + (ln - originStart); | |
| 348 } | |
| 349 } | |
| 350 assert false; | |
| 351 return -1; | |
| 352 } | |
| 353 } | |
| 174 } | 354 } |
