Mercurial > hg4j
comparison test/org/tmatesoft/hg/test/TestStatus.java @ 442:6865eb742883
Tests for subrepo API, refactor status tests for reuse, better subrepos API
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 27 Apr 2012 20:57:20 +0200 |
parents | cd658b24a620 |
children | 36fd1fd06492 |
comparison
equal
deleted
inserted
replaced
441:2a08466838d3 | 442:6865eb742883 |
---|---|
48 import org.tmatesoft.hg.repo.HgStatusCollector; | 48 import org.tmatesoft.hg.repo.HgStatusCollector; |
49 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; | 49 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; |
50 import org.tmatesoft.hg.util.Path; | 50 import org.tmatesoft.hg.util.Path; |
51 import org.tmatesoft.hg.util.Status; | 51 import org.tmatesoft.hg.util.Status; |
52 | 52 |
53 | |
54 /** | 53 /** |
55 * | 54 * |
56 * @author Artem Tikhomirov | 55 * @author Artem Tikhomirov |
57 * @author TMate Software Ltd. | 56 * @author TMate Software Ltd. |
58 */ | 57 */ |
62 public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); | 61 public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); |
63 | 62 |
64 private HgRepository repo; | 63 private HgRepository repo; |
65 private StatusOutputParser statusParser; | 64 private StatusOutputParser statusParser; |
66 private ExecHelper eh; | 65 private ExecHelper eh; |
66 private StatusReporter sr; | |
67 | 67 |
68 public static void main(String[] args) throws Throwable { | 68 public static void main(String[] args) throws Throwable { |
69 TestStatus test = new TestStatus(); | 69 TestStatus test = new TestStatus(); |
70 test.testLowLevel(); | 70 test.testLowLevel(); |
71 test.testStatusCommand(); | 71 test.testStatusCommand(); |
77 t2.errorCollector.verify(); | 77 t2.errorCollector.verify(); |
78 TestStatus t3 = new TestStatus(new HgLookup().detect("/temp/hg/cpython")); | 78 TestStatus t3 = new TestStatus(new HgLookup().detect("/temp/hg/cpython")); |
79 t3.testDirstateParentOtherThanTipNoUpdate(); | 79 t3.testDirstateParentOtherThanTipNoUpdate(); |
80 t3.errorCollector.verify(); | 80 t3.errorCollector.verify(); |
81 } | 81 } |
82 | 82 |
83 public TestStatus() throws Exception { | 83 public TestStatus() throws Exception { |
84 this(new HgLookup().detectFromWorkingDir()); | 84 this(new HgLookup().detectFromWorkingDir()); |
85 } | 85 } |
86 | 86 |
87 private TestStatus(HgRepository hgRepo) { | 87 private TestStatus(HgRepository hgRepo) { |
88 repo = hgRepo; | 88 repo = hgRepo; |
89 Assume.assumeTrue(!repo.isInvalid()); | 89 Assume.assumeTrue(!repo.isInvalid()); |
90 statusParser = new StatusOutputParser(); | 90 statusParser = new StatusOutputParser(); |
91 eh = new ExecHelper(statusParser, hgRepo.getWorkingDir()); | 91 eh = new ExecHelper(statusParser, hgRepo.getWorkingDir()); |
92 } | 92 sr = new StatusReporter(errorCollector, statusParser); |
93 | 93 } |
94 | |
94 @Test | 95 @Test |
95 public void testLowLevel() throws Exception { | 96 public void testLowLevel() throws Exception { |
96 final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); | 97 final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); |
97 statusParser.reset(); | 98 statusParser.reset(); |
98 eh.run("hg", "status", "-A"); | 99 eh.run("hg", "status", "-A"); |
99 HgStatusCollector.Record r = wcc.status(HgRepository.TIP); | 100 HgStatusCollector.Record r = wcc.status(HgRepository.TIP); |
100 report("hg status -A", r, statusParser); | 101 sr.report("hg status -A", r); |
101 // | 102 // |
102 statusParser.reset(); | 103 statusParser.reset(); |
103 int revision = 3; | 104 int revision = 3; |
104 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); | 105 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); |
105 r = wcc.status(revision); | 106 r = wcc.status(revision); |
106 report("status -A --rev " + revision, r, statusParser); | 107 sr.report("status -A --rev " + revision, r); |
107 // | 108 // |
108 statusParser.reset(); | 109 statusParser.reset(); |
109 eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); | 110 eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); |
110 r = new HgStatusCollector.Record(); | 111 r = new HgStatusCollector.Record(); |
111 new HgStatusCollector(repo).change(revision, r); | 112 new HgStatusCollector(repo).change(revision, r); |
112 report("status -A --change " + revision, r, statusParser); | 113 sr.report("status -A --change " + revision, r); |
113 // | 114 // |
114 statusParser.reset(); | 115 statusParser.reset(); |
115 int rev2 = 80; | 116 int rev2 = 80; |
116 final String range = String.valueOf(revision) + ":" + String.valueOf(rev2); | 117 final String range = String.valueOf(revision) + ":" + String.valueOf(rev2); |
117 eh.run("hg", "status", "-A", "--rev", range); | 118 eh.run("hg", "status", "-A", "--rev", range); |
118 r = new HgStatusCollector(repo).status(revision, rev2); | 119 r = new HgStatusCollector(repo).status(revision, rev2); |
119 report("Status -A -rev " + range, r, statusParser); | 120 sr.report("Status -A -rev " + range, r); |
120 } | 121 } |
121 | 122 |
122 /** | 123 /** |
123 * hg up --rev <earlier rev>; hg status | 124 * hg up --rev <earlier rev>; hg status |
124 * | 125 * |
125 * To check if HgWorkingCopyStatusCollector respects actual working copy parent (takes from dirstate) | 126 * To check if HgWorkingCopyStatusCollector respects actual working copy parent (takes from dirstate) |
126 * and if status is calculated correctly | 127 * and if status is calculated correctly |
127 */ | 128 */ |
128 @Test | 129 @Test |
129 @Ignore("modifies test repository, needs careful configuration") | 130 @Ignore("modifies test repository, needs careful configuration") |
130 public void testDirstateParentOtherThanTipWithUpdate() throws Exception { | 131 public void testDirstateParentOtherThanTipWithUpdate() throws Exception { |
131 int revToUpdate = 238; | 132 int revToUpdate = 238; |
143 final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); | 144 final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); |
144 statusParser.reset(); | 145 statusParser.reset(); |
145 // | 146 // |
146 eh.run("hg", "status", "-A"); | 147 eh.run("hg", "status", "-A"); |
147 HgStatusCollector.Record r = wcc.status(HgRepository.TIP); | 148 HgStatusCollector.Record r = wcc.status(HgRepository.TIP); |
148 report("hg status -A", r, statusParser); | 149 sr.report("hg status -A", r); |
149 // | 150 // |
150 statusParser.reset(); | 151 statusParser.reset(); |
151 int revision = 3; | 152 int revision = 3; |
152 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); | 153 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); |
153 r = wcc.status(revision); | 154 r = wcc.status(revision); |
154 report("status -A --rev " + revision, r, statusParser); | 155 sr.report("status -A --rev " + revision, r); |
155 } | 156 } |
156 | 157 |
157 | |
158 @Test | 158 @Test |
159 public void testStatusCommand() throws Exception { | 159 public void testStatusCommand() throws Exception { |
160 final HgStatusCommand sc = new HgStatusCommand(repo).all(); | 160 final HgStatusCommand sc = new HgStatusCommand(repo).all(); |
161 StatusCollector r; | 161 StatusCollector r; |
162 statusParser.reset(); | 162 statusParser.reset(); |
163 eh.run("hg", "status", "-A"); | 163 eh.run("hg", "status", "-A"); |
164 sc.execute(r = new StatusCollector()); | 164 sc.execute(r = new StatusCollector()); |
165 report("hg status -A", r); | 165 sr.report("hg status -A", r); |
166 // | 166 // |
167 statusParser.reset(); | 167 statusParser.reset(); |
168 int revision = 3; | 168 int revision = 3; |
169 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); | 169 eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); |
170 sc.base(revision).execute(r = new StatusCollector()); | 170 sc.base(revision).execute(r = new StatusCollector()); |
171 report("status -A --rev " + revision, r); | 171 sr.report("status -A --rev " + revision, r); |
172 // | 172 // |
173 statusParser.reset(); | 173 statusParser.reset(); |
174 eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); | 174 eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); |
175 sc.base(TIP).revision(revision).execute(r = new StatusCollector()); | 175 sc.base(TIP).revision(revision).execute(r = new StatusCollector()); |
176 report("status -A --change " + revision, r); | 176 sr.report("status -A --change " + revision, r); |
177 | 177 |
178 // TODO check not -A, but defaults()/custom set of modifications | 178 // TODO check not -A, but defaults()/custom set of modifications |
179 } | 179 } |
180 | 180 |
181 private static class StatusCollector implements HgStatusHandler { | 181 static class StatusCollector implements HgStatusHandler { |
182 private final Map<Kind, List<Path>> kind2names = new TreeMap<Kind, List<Path>>(); | 182 private final Map<Kind, List<Path>> kind2names = new TreeMap<Kind, List<Path>>(); |
183 private final Map<Path, List<Kind>> name2kinds = new TreeMap<Path, List<Kind>>(); | 183 private final Map<Path, List<Kind>> name2kinds = new TreeMap<Path, List<Kind>>(); |
184 private final Map<Path, Status> name2error = new LinkedHashMap<Path, Status>(); | 184 private final Map<Path, Status> name2error = new LinkedHashMap<Path, Status>(); |
185 private final Map<Path, Path> new2oldName = new LinkedHashMap<Path, Path>(); | |
185 | 186 |
186 public void status(HgStatus s) { | 187 public void status(HgStatus s) { |
187 List<Path> l = kind2names.get(s.getKind()); | 188 List<Path> l = kind2names.get(s.getKind()); |
188 if (l == null) { | 189 if (l == null) { |
189 kind2names.put(s.getKind(), l = new LinkedList<Path>()); | 190 kind2names.put(s.getKind(), l = new LinkedList<Path>()); |
190 } | 191 } |
191 l.add(s.getPath()); | 192 l.add(s.getPath()); |
193 if (s.isCopy()) { | |
194 new2oldName.put(s.getPath(), s.getOriginalPath()); | |
195 } | |
192 // | 196 // |
193 List<Kind> k = name2kinds.get(s.getPath()); | 197 List<Kind> k = name2kinds.get(s.getPath()); |
194 if (k == null) { | 198 if (k == null) { |
195 name2kinds.put(s.getPath(), k = new LinkedList<Kind>()); | 199 name2kinds.put(s.getPath(), k = new LinkedList<Kind>()); |
196 } | 200 } |
197 k.add(s.getKind()); | 201 k.add(s.getKind()); |
198 } | 202 } |
199 | 203 |
200 public void error(Path file, Status s) { | 204 public void error(Path file, Status s) { |
201 name2error.put(file, s); | 205 name2error.put(file, s); |
202 } | 206 } |
203 | 207 |
204 public List<Path> get(Kind k) { | 208 public List<Path> get(Kind k) { |
205 List<Path> rv = kind2names.get(k); | 209 List<Path> rv = kind2names.get(k); |
206 return rv == null ? Collections.<Path>emptyList() : rv; | 210 return rv == null ? Collections.<Path> emptyList() : rv; |
207 } | 211 } |
208 | 212 |
209 public List<Kind> get(Path p) { | 213 public List<Kind> get(Path p) { |
210 List<Kind> rv = name2kinds.get(p); | 214 List<Kind> rv = name2kinds.get(p); |
211 return rv == null ? Collections.<Kind>emptyList() : rv; | 215 return rv == null ? Collections.<Kind> emptyList() : rv; |
212 } | 216 } |
213 | 217 |
214 public Map<Path, Status> getErrors() { | 218 public Map<Path, Status> getErrors() { |
215 return name2error; | 219 return name2error; |
220 } | |
221 | |
222 public HgStatusCollector.Record asStatusRecord() { | |
223 HgStatusCollector.Record rv = new HgStatusCollector.Record(); | |
224 for (Path p : get(Modified)) { | |
225 rv.modified(p); | |
226 } | |
227 for (Path p : get(Added)) { | |
228 if (!new2oldName.containsKey(p)) { | |
229 // new files that are result of a copy get reported separately, below | |
230 rv.added(p); | |
231 } | |
232 } | |
233 for (Path p : get(Removed)) { | |
234 rv.removed(p); | |
235 } | |
236 for (Path p : get(Clean)) { | |
237 rv.clean(p); | |
238 } | |
239 for (Path p : get(Ignored)) { | |
240 rv.ignored(p); | |
241 } | |
242 for (Path p : get(Missing)) { | |
243 rv.missing(p); | |
244 } | |
245 for (Path p : get(Unknown)) { | |
246 rv.unknown(p); | |
247 } | |
248 for (Map.Entry<Path, Path> e : new2oldName.entrySet()) { | |
249 rv.copied(e.getValue(), e.getKey()); | |
250 } | |
251 return rv; | |
216 } | 252 } |
217 } | 253 } |
218 | 254 |
219 /* | 255 /* |
220 * status-1/dir/file5 was added in rev 8, scheduled (hg remove file5) for removal, but not yet committed | 256 * status-1/dir/file5 was added in rev 8, scheduled (hg remove file5) for removal, but not yet committed |
230 assertTrue(sc.getErrors().isEmpty()); | 266 assertTrue(sc.getErrors().isEmpty()); |
231 Path file5 = Path.create("dir/file5"); | 267 Path file5 = Path.create("dir/file5"); |
232 // shall not be listed at all | 268 // shall not be listed at all |
233 assertTrue(sc.get(file5).isEmpty()); | 269 assertTrue(sc.get(file5).isEmpty()); |
234 } | 270 } |
235 | 271 |
236 /* | 272 /* |
237 * status-1/file2 is tracked, but later .hgignore got entry to ignore it, file2 got modified | 273 * status-1/file2 is tracked, but later .hgignore got entry to ignore it, file2 got modified |
238 * HG doesn't respect .hgignore for tracked files. | 274 * HG doesn't respect .hgignore for tracked files. |
239 * Now reported as ignored and missing(?!). | 275 * Now reported as ignored and missing(?!). |
240 * Shall be reported as modified. | 276 * Shall be reported as modified. |
251 assertTrue(sc.get(file2).size() == 1); | 287 assertTrue(sc.get(file2).size() == 1); |
252 } | 288 } |
253 | 289 |
254 /* | 290 /* |
255 * status/dir/file4, added in rev 3, has been scheduled for removal (hg remove -Af file4), but still there in the WC. | 291 * status/dir/file4, added in rev 3, has been scheduled for removal (hg remove -Af file4), but still there in the WC. |
256 * Shall be reported as Removed, when comparing against rev 3 | 292 * Shall be reported as Removed, when comparing against rev 3 |
257 * (despite both rev 3 and WC's parent has file4, there are different paths in the code for wc against parent and wc against rev) | 293 * (despite both rev 3 and WC's parent has file4, there are different paths in the code for wc against parent and wc against rev) |
258 */ | 294 */ |
259 @Test | 295 @Test |
260 public void testMarkedRemovedButStillInWC() throws Exception { | 296 public void testMarkedRemovedButStillInWC() throws Exception { |
261 repo = Configuration.get().find("status-1"); | 297 repo = Configuration.get().find("status-1"); |
262 HgStatusCommand cmd = new HgStatusCommand(repo); | 298 HgStatusCommand cmd = new HgStatusCommand(repo); |
278 assertTrue(sc.getErrors().isEmpty()); | 314 assertTrue(sc.getErrors().isEmpty()); |
279 assertTrue(sc.get(file4).isEmpty()); | 315 assertTrue(sc.get(file4).isEmpty()); |
280 } | 316 } |
281 | 317 |
282 /* | 318 /* |
283 * status-1/dir/file3 tracked, listed in .hgignore since rev 4, removed (hg remove file3) from repo and WC | 319 * status-1/dir/file3 tracked, listed in .hgignore since rev 4, removed (hg remove file3) from repo and WC |
284 * (but entry in .hgignore left) in revision 5, and new file3 got created in WC. | 320 * (but entry in .hgignore left) in revision 5, and new file3 got created in WC. |
285 * Shall be reported as ignored when comparing against WC's parent, | 321 * Shall be reported as ignored when comparing against WC's parent, |
286 * and both ignored and removed when comparing against revision 3 | 322 * and both ignored and removed when comparing against revision 3 |
287 */ | 323 */ |
288 @Test | 324 @Test |
289 public void testRemovedIgnoredInWC() throws Exception { | 325 public void testRemovedIgnoredInWC() throws Exception { |
290 // check branch !known, ignored | 326 // check branch !known, ignored |
291 repo = Configuration.get().find("status-1"); | 327 repo = Configuration.get().find("status-1"); |
315 | 351 |
316 } | 352 } |
317 | 353 |
318 /* | 354 /* |
319 * status/file1 was removed in cset 2. New file with the same name in the WC. | 355 * status/file1 was removed in cset 2. New file with the same name in the WC. |
320 * Shall report 2 statuses (as cmdline hg does): unknown and removed when comparing against that revision. | 356 * Shall report 2 statuses (as cmdline hg does): unknown and removed when comparing against that revision. |
321 */ | 357 */ |
322 @Test | 358 @Test |
323 public void testNewFileWithSameNameAsDeletedOld() throws Exception { | 359 public void testNewFileWithSameNameAsDeletedOld() throws Exception { |
324 // check branch !known, !ignored (=> unknown) | 360 // check branch !known, !ignored (=> unknown) |
325 repo = Configuration.get().find("status-1"); | 361 repo = Configuration.get().find("status-1"); |
337 cmd.base(2).execute(sc = new StatusCollector()); | 373 cmd.base(2).execute(sc = new StatusCollector()); |
338 assertTrue(sc.getErrors().isEmpty()); | 374 assertTrue(sc.getErrors().isEmpty()); |
339 assertTrue(sc.get(file1).contains(Unknown)); | 375 assertTrue(sc.get(file1).contains(Unknown)); |
340 assertTrue(sc.get(file1).size() == 1); | 376 assertTrue(sc.get(file1).size() == 1); |
341 } | 377 } |
342 | 378 |
343 @Test | 379 @Test |
344 public void testSubTreeStatus() throws Exception { | 380 public void testSubTreeStatus() throws Exception { |
345 repo = Configuration.get().find("status-1"); | 381 repo = Configuration.get().find("status-1"); |
346 HgStatusCommand cmd = new HgStatusCommand(repo); | 382 HgStatusCommand cmd = new HgStatusCommand(repo); |
347 StatusCollector sc = new StatusCollector(); | 383 StatusCollector sc = new StatusCollector(); |
371 assertTrue(sc.get(Modified).isEmpty()); | 407 assertTrue(sc.get(Modified).isEmpty()); |
372 assertTrue(sc.get(Added).isEmpty()); | 408 assertTrue(sc.get(Added).isEmpty()); |
373 assertTrue(sc.get(Ignored).size() == 1); | 409 assertTrue(sc.get(Ignored).size() == 1); |
374 assertTrue(sc.get(Removed).size() == 2); | 410 assertTrue(sc.get(Removed).size() == 2); |
375 } | 411 } |
376 | 412 |
377 | |
378 @Test | 413 @Test |
379 public void testSpecificFileStatus() throws Exception { | 414 public void testSpecificFileStatus() throws Exception { |
380 repo = Configuration.get().find("status-1"); | 415 repo = Configuration.get().find("status-1"); |
381 // files only | 416 // files only |
382 final Path file2 = Path.create("file2"); | 417 final Path file2 = Path.create("file2"); |
411 assertTrue(r.getCopied().isEmpty()); | 446 assertTrue(r.getCopied().isEmpty()); |
412 assertTrue(r.getIgnored().contains(file3)); | 447 assertTrue(r.getIgnored().contains(file3)); |
413 assertTrue(r.getIgnored().size() == 1); | 448 assertTrue(r.getIgnored().size() == 1); |
414 assertTrue(r.getModified().isEmpty()); | 449 assertTrue(r.getModified().isEmpty()); |
415 } | 450 } |
416 | 451 |
417 @Test | 452 @Test |
418 public void testSameResultDirectPathVsMatcher() throws Exception { | 453 public void testSameResultDirectPathVsMatcher() throws Exception { |
419 repo = Configuration.get().find("status-1"); | 454 repo = Configuration.get().find("status-1"); |
420 final Path file3 = Path.create("dir/file3"); | 455 final Path file3 = Path.create("dir/file3"); |
421 final Path file5 = Path.create("dir/file5"); | 456 final Path file5 = Path.create("dir/file5"); |
422 | 457 |
423 HgWorkingCopyStatusCollector sc = HgWorkingCopyStatusCollector.create(repo, file3, file5); | 458 HgWorkingCopyStatusCollector sc = HgWorkingCopyStatusCollector.create(repo, file3, file5); |
424 HgStatusCollector.Record r; | 459 HgStatusCollector.Record r; |
425 sc.walk(WORKING_COPY, r = new HgStatusCollector.Record()); | 460 sc.walk(WORKING_COPY, r = new HgStatusCollector.Record()); |
426 assertTrue(r.getRemoved().contains(file5)); | 461 assertTrue(r.getRemoved().contains(file5)); |
427 assertTrue(r.getIgnored().contains(file3)); | 462 assertTrue(r.getIgnored().contains(file3)); |
430 sc = HgWorkingCopyStatusCollector.create(repo, new PathGlobMatcher(file3.toString(), file5.toString())); | 465 sc = HgWorkingCopyStatusCollector.create(repo, new PathGlobMatcher(file3.toString(), file5.toString())); |
431 sc.walk(WORKING_COPY, r = new HgStatusCollector.Record()); | 466 sc.walk(WORKING_COPY, r = new HgStatusCollector.Record()); |
432 assertTrue(r.getRemoved().contains(file5)); | 467 assertTrue(r.getRemoved().contains(file5)); |
433 assertTrue(r.getIgnored().contains(file3)); | 468 assertTrue(r.getIgnored().contains(file3)); |
434 } | 469 } |
435 | 470 |
436 @Test | 471 @Test |
437 public void testScopeInHistoricalStatus() throws Exception { | 472 public void testScopeInHistoricalStatus() throws Exception { |
438 repo = Configuration.get().find("status-1"); | 473 repo = Configuration.get().find("status-1"); |
439 HgStatusCommand cmd = new HgStatusCommand(repo); | 474 HgStatusCommand cmd = new HgStatusCommand(repo); |
440 cmd.base(3).revision(8).all(); | 475 cmd.base(3).revision(8).all(); |
457 assertTrue(sc.get(file5).contains(Added)); | 492 assertTrue(sc.get(file5).contains(Added)); |
458 assertTrue(sc.get(file5).size() == 1); | 493 assertTrue(sc.get(file5).size() == 1); |
459 assertTrue(sc.get(Added).size() == 1); | 494 assertTrue(sc.get(Added).size() == 1); |
460 | 495 |
461 } | 496 } |
462 | 497 |
463 /** | 498 /** |
464 * Issue 22 | 499 * Issue 22 |
465 */ | 500 */ |
466 @Test | 501 @Test |
467 public void testOnEmptyRepositoryWithAllFilesDeleted() throws Exception { | 502 public void testOnEmptyRepositoryWithAllFilesDeleted() throws Exception { |
471 StatusCollector sc = new StatusCollector(); | 506 StatusCollector sc = new StatusCollector(); |
472 cmd.execute(sc); | 507 cmd.execute(sc); |
473 // shall pass without exception | 508 // shall pass without exception |
474 assertTrue(sc.getErrors().isEmpty()); | 509 assertTrue(sc.getErrors().isEmpty()); |
475 for (HgStatus.Kind k : HgStatus.Kind.values()) { | 510 for (HgStatus.Kind k : HgStatus.Kind.values()) { |
476 assertTrue("Kind " + k.name() + " shall be empty",sc.get(k).isEmpty()); | 511 assertTrue("Kind " + k.name() + " shall be empty", sc.get(k).isEmpty()); |
477 } | 512 } |
478 } | 513 } |
479 | 514 |
480 /** | 515 /** |
481 * Issue 22, two subsequent commits that remove all repository files, each in a different branch. | 516 * Issue 22, two subsequent commits that remove all repository files, each in a different branch. |
482 * Here's excerpt from my RevlogWriter utility: | 517 * Here's excerpt from my RevlogWriter utility: |
518 * | |
483 * <pre> | 519 * <pre> |
484 * final List<String> filesList = Collections.singletonList("file1"); | 520 * final List<String> filesList = Collections.singletonList("file1"); |
485 * // | 521 * // |
486 * file1.writeUncompressed(-1, -1, 0, 0, "garbage".getBytes()); | 522 * file1.writeUncompressed(-1, -1, 0, 0, "garbage".getBytes()); |
487 * // | 523 * // |
488 * ManifestBuilder mb = new ManifestBuilder(); | 524 * ManifestBuilder mb = new ManifestBuilder(); |
489 * mb.reset().add("file1", file1.getRevision(0)); | 525 * mb.reset().add("file1", file1.getRevision(0)); |
490 * manifest.writeUncompressed(-1, -1, 0, 0, mb.build()); // manifest revision 0 | 526 * manifest.writeUncompressed(-1, -1, 0, 0, mb.build()); // manifest revision 0 |
491 * final byte[] cset1 = buildChangelogEntry(manifest.getRevision(0), Collections.<String, String>emptyMap(), filesList, "Add a file"); | 527 * final byte[] cset1 = buildChangelogEntry(manifest.getRevision(0), Collections.<String, String>emptyMap(), filesList, "Add a file"); |
492 * changelog.writeUncompressed(-1, -1, 0, 0, cset1); | 528 * changelog.writeUncompressed(-1, -1, 0, 0, cset1); |
493 * // | 529 * // |
494 * // pretend we delete all files in a branch 1 | 530 * // pretend we delete all files in a branch 1 |
495 * manifest.writeUncompressed(0, -1, 1, 1, new byte[0]); // manifest revision 1 | 531 * manifest.writeUncompressed(0, -1, 1, 1, new byte[0]); // manifest revision 1 |
496 * final byte[] cset2 = buildChangelogEntry(manifest.getRevision(1), Collections.singletonMap("branch", "delete-all-1"), filesList, "Delete all files in a first branch"); | 532 * final byte[] cset2 = buildChangelogEntry(manifest.getRevision(1), Collections.singletonMap("branch", "delete-all-1"), filesList, "Delete all files in a first branch"); |
497 * changelog.writeUncompressed(0, -1, 1, 1, cset2); | 533 * changelog.writeUncompressed(0, -1, 1, 1, cset2); |
498 * // | 534 * // |
499 * // pretend we delete all files in a branch 2 (which is based on revision 0, same as branch 1) | 535 * // pretend we delete all files in a branch 2 (which is based on revision 0, same as branch 1) |
500 * manifest.writeUncompressed(1, -1, 1 /*!!! here comes baseRevision != index * /, 2, new byte[0]); // manifest revision 2 | 536 * manifest.writeUncompressed(1, -1, 1 /*!!! here comes baseRevision != index * /, 2, new byte[0]); // manifest revision 2 |
501 * final byte[] cset3 = buildChangelogEntry(manifest.getRevision(2), Collections.singletonMap("branch", "delete-all-2"), filesList, "Again delete all files but in another branch"); | 537 * final byte[] cset3 = buildChangelogEntry(manifest.getRevision(2), Collections.singletonMap("branch", "delete-all-2"), filesList, "Again delete all files but in another branch"); |
502 * changelog.writeUncompressed(0, -1, 2, 2, cset3); | 538 * changelog.writeUncompressed(0, -1, 2, 2, cset3); |
503 * </pre> | 539 * </pre> |
504 */ | 540 */ |
505 @Test | 541 @Test |
506 public void testOnEmptyRepositoryWithAllFilesDeletedInBranch() throws Exception { | 542 public void testOnEmptyRepositoryWithAllFilesDeletedInBranch() throws Exception { |
507 repo = Configuration.get().find("status-3"); | 543 repo = Configuration.get().find("status-3"); |
508 HgStatusCommand cmd = new HgStatusCommand(repo); | 544 HgStatusCommand cmd = new HgStatusCommand(repo); |
510 StatusCollector sc = new StatusCollector(); | 546 StatusCollector sc = new StatusCollector(); |
511 cmd.execute(sc); | 547 cmd.execute(sc); |
512 // shall pass without exception | 548 // shall pass without exception |
513 assertTrue(sc.getErrors().isEmpty()); | 549 assertTrue(sc.getErrors().isEmpty()); |
514 for (HgStatus.Kind k : HgStatus.Kind.values()) { | 550 for (HgStatus.Kind k : HgStatus.Kind.values()) { |
515 assertTrue("Kind " + k.name() + " shall be empty",sc.get(k).isEmpty()); | 551 assertTrue("Kind " + k.name() + " shall be empty", sc.get(k).isEmpty()); |
516 } | 552 } |
517 } | 553 } |
518 | 554 |
519 /** | 555 /** |
520 * Issue 23: HgInvalidRevisionException for svn imported repository (changeset 0 references nullid manifest) | 556 * Issue 23: HgInvalidRevisionException for svn imported repository (changeset 0 references nullid manifest) |
521 */ | 557 */ |
522 @Test | 558 @Test |
523 public void testImportedRepoWithOddManifestRevisions() throws Exception { | 559 public void testImportedRepoWithOddManifestRevisions() throws Exception { |
524 repo = Configuration.get().find("status-4"); | 560 repo = Configuration.get().find("status-4"); |
525 HgStatusCommand cmd = new HgStatusCommand(repo); | 561 HgStatusCommand cmd = new HgStatusCommand(repo); |
527 StatusCollector sc = new StatusCollector(); | 563 StatusCollector sc = new StatusCollector(); |
528 cmd.execute(sc); | 564 cmd.execute(sc); |
529 // shall pass without exception | 565 // shall pass without exception |
530 assertTrue(sc.getErrors().isEmpty()); | 566 assertTrue(sc.getErrors().isEmpty()); |
531 } | 567 } |
532 | 568 |
533 /** | 569 /** |
534 * Issue 24: IllegalArgumentException in FilterDataAccess | 570 * Issue 24: IllegalArgumentException in FilterDataAccess |
535 * There were two related defects in RevlogStream | 571 * There were two related defects in RevlogStream |
536 * a) for compressedLen == 0, a byte was read and FilterDataAccess (of length 0, but it didn't help too much) was created - first byte happen to be 0. | 572 * a) for compressedLen == 0, a byte was read and FilterDataAccess (of length 0, but it didn't help too much) was created - first byte happen to be 0. |
537 * Patch was not applied (userDataAccess.isEmpty() check thanks to Issue 22) | 573 * Patch was not applied (userDataAccess.isEmpty() check thanks to Issue 22) |
538 * b) That FilterDataAccess (with 0 size represents patch more or less relevantly, but didn't represent actual revision) get successfully | 574 * b) That FilterDataAccess (with 0 size represents patch more or less relevantly, but didn't represent actual revision) get successfully |
539 * reassigned as lastUserData for the next iteration. And at the next step attempt to apply patch recorded in the next revision failed | 575 * reassigned as lastUserData for the next iteration. And at the next step attempt to apply patch recorded in the next revision failed |
540 * because baseRevisionData is 0 length FilterDataAccess | 576 * because baseRevisionData is 0 length FilterDataAccess |
541 * | 577 * |
542 * Same applies for | 578 * Same applies for |
543 * Issue 25: IOException: Underflow. Rewind past end of the slice in InflaterDataAccess | 579 * Issue 25: IOException: Underflow. Rewind past end of the slice in InflaterDataAccess |
544 * with the difference in separate .i and .d (thus not 0 but 'x' first byte was read) | 580 * with the difference in separate .i and .d (thus not 0 but 'x' first byte was read) |
545 * | 581 * |
546 * Sample: | 582 * Sample: |
547 * status-5/file1 has 3 revisions, second is zero-length patch: | 583 * status-5/file1 has 3 revisions, second is zero-length patch: |
548 * Index Offset Packed Actual Base Rev | 584 * Index Offset Packed Actual Base Rev |
549 * 0: 0 8 7 0 | 585 * 0: 0 8 7 0 |
550 * DATA | 586 * DATA |
551 * 1: 8 0 7 0 | 587 * 1: 8 0 7 0 |
552 * NO DATA | 588 * NO DATA |
553 * 2: 8 14 6 0 | 589 * 2: 8 14 6 0 |
554 * PATCH | 590 * PATCH |
555 */ | 591 */ |
556 @Test | 592 @Test |
557 public void testZeroLengthPatchAgainstNonEmptyBaseRev() throws Exception{ | 593 public void testZeroLengthPatchAgainstNonEmptyBaseRev() throws Exception { |
558 repo = Configuration.get().find("status-5"); | 594 repo = Configuration.get().find("status-5"); |
559 // pretend we modified files in the working copy | 595 // pretend we modified files in the working copy |
560 // for HgWorkingCopyStatusCollector to go and retrieve its content from repository | 596 // for HgWorkingCopyStatusCollector to go and retrieve its content from repository |
561 File f1 = new File(repo.getWorkingDir(), "file1"); | 597 File f1 = new File(repo.getWorkingDir(), "file1"); |
562 f1.setLastModified(System.currentTimeMillis()); | 598 f1.setLastModified(System.currentTimeMillis()); |
567 cmd.all(); | 603 cmd.all(); |
568 StatusCollector sc = new StatusCollector(); | 604 StatusCollector sc = new StatusCollector(); |
569 cmd.execute(sc); | 605 cmd.execute(sc); |
570 // shall pass without exception | 606 // shall pass without exception |
571 // | 607 // |
572 for (Map.Entry<Path,Status> e : sc.getErrors().entrySet()) { | 608 for (Map.Entry<Path, Status> e : sc.getErrors().entrySet()) { |
573 System.out.printf("%s : (%s %s)\n", e.getKey(), e.getValue().getKind(), e.getValue().getMessage()); | 609 System.out.printf("%s : (%s %s)\n", e.getKey(), e.getValue().getKind(), e.getValue().getMessage()); |
574 } | 610 } |
575 assertTrue(sc.getErrors().isEmpty()); | 611 assertTrue(sc.getErrors().isEmpty()); |
576 } | 612 } |
577 | 613 |
578 /** | 614 /** |
579 * Issue 26: UnsupportedOperationException when patching empty base revision | 615 * Issue 26: UnsupportedOperationException when patching empty base revision |
580 * | 616 * |
581 * Sample: | 617 * Sample: |
582 * status-5/file2 has 3 revisions, second is patch (complete revision content in a form of the patch) for empty base revision: | 618 * status-5/file2 has 3 revisions, second is patch (complete revision content in a form of the patch) for empty base revision: |
583 * Index Offset Packed Actual Base Rev | 619 * Index Offset Packed Actual Base Rev |
584 * 0: 0 0 0 0 | 620 * 0: 0 0 0 0 |
585 * NO DATA | 621 * NO DATA |
586 * 1: 0 20 7 0 | 622 * 1: 0 20 7 0 |
587 * PATCH: 0..0, 7:garbage | 623 * PATCH: 0..0, 7:garbage |
588 * 2: 20 16 7 0 | 624 * 2: 20 16 7 0 |
589 */ | 625 */ |
590 @Test | 626 @Test |
591 public void testPatchZeroLengthBaseRevision() throws Exception { | 627 public void testPatchZeroLengthBaseRevision() throws Exception { |
592 repo = Configuration.get().find("status-5"); | 628 repo = Configuration.get().find("status-5"); |
593 // touch the file to force content retrieval | 629 // touch the file to force content retrieval |
598 cmd.all(); | 634 cmd.all(); |
599 StatusCollector sc = new StatusCollector(); | 635 StatusCollector sc = new StatusCollector(); |
600 cmd.execute(sc); | 636 cmd.execute(sc); |
601 // shall pass without exception | 637 // shall pass without exception |
602 // | 638 // |
603 for (Map.Entry<Path,Status> e : sc.getErrors().entrySet()) { | 639 for (Map.Entry<Path, Status> e : sc.getErrors().entrySet()) { |
604 System.out.printf("%s : (%s %s)\n", e.getKey(), e.getValue().getKind(), e.getValue().getMessage()); | 640 System.out.printf("%s : (%s %s)\n", e.getKey(), e.getValue().getKind(), e.getValue().getMessage()); |
605 } | 641 } |
606 assertTrue(sc.getErrors().isEmpty()); | 642 assertTrue(sc.getErrors().isEmpty()); |
607 } | 643 } |
608 | 644 |
609 | |
610 /* | 645 /* |
611 * With warm-up of previous tests, 10 runs, time in milliseconds | 646 * With warm-up of previous tests, 10 runs, time in milliseconds |
612 * 'hg status -A': Native client total 953 (95 per run), Java client 94 (9) | 647 * 'hg status -A': Native client total 953 (95 per run), Java client 94 (9) |
613 * 'hg status -A --rev 3:80': Native client total 1828 (182 per run), Java client 235 (23) | 648 * 'hg status -A --rev 3:80': Native client total 1828 (182 per run), Java client 235 (23) |
614 * 'hg log --debug', 10 runs: Native client total 1766 (176 per run), Java client 78 (7) | 649 * 'hg log --debug', 10 runs: Native client total 1766 (176 per run), Java client 78 (7) |
615 * | 650 * |
616 * 18.02.2011 | 651 * 18.02.2011 |
617 * 'hg status -A --rev 3:80', 10 runs: Native client total 2000 (200 per run), Java client 250 (25) | 652 * 'hg status -A --rev 3:80', 10 runs: Native client total 2000 (200 per run), Java client 250 (25) |
618 * 'hg log --debug', 10 runs: Native client total 2297 (229 per run), Java client 125 (12) | 653 * 'hg log --debug', 10 runs: Native client total 2297 (229 per run), Java client 125 (12) |
619 * | 654 * |
620 * 9.3.2011 (DataAccess instead of byte[] in ReflogStream.Inspector | 655 * 9.3.2011 (DataAccess instead of byte[] in ReflogStream.Inspector |
621 * 'hg status -A', 10 runs: Native client total 1516 (151 per run), Java client 219 (21) | 656 * 'hg status -A', 10 runs: Native client total 1516 (151 per run), Java client 219 (21) |
622 * 'hg status -A --rev 3:80', 10 runs: Native client total 1875 (187 per run), Java client 3187 (318) (!!! ???) | 657 * 'hg status -A --rev 3:80', 10 runs: Native client total 1875 (187 per run), Java client 3187 (318) (!!! ???) |
623 * 'hg log --debug', 10 runs: Native client total 2484 (248 per run), Java client 344 (34) | 658 * 'hg log --debug', 10 runs: Native client total 2484 (248 per run), Java client 344 (34) |
624 */ | 659 */ |
625 public void testPerformance() throws Exception { | 660 public void testPerformance() throws Exception { |
626 final int runs = 10; | 661 final int runs = 10; |
627 final long start1 = System.currentTimeMillis(); | 662 final long start1 = System.currentTimeMillis(); |
628 for (int i = 0; i < runs; i++) { | 663 for (int i = 0; i < runs; i++) { |
633 for (int i = 0; i < runs; i++) { | 668 for (int i = 0; i < runs; i++) { |
634 StatusCollector r = new StatusCollector(); | 669 StatusCollector r = new StatusCollector(); |
635 new HgStatusCommand(repo).all().base(3).revision(80).execute(r); | 670 new HgStatusCommand(repo).all().base(3).revision(80).execute(r); |
636 } | 671 } |
637 final long end = System.currentTimeMillis(); | 672 final long end = System.currentTimeMillis(); |
638 System.out.printf("'hg status -A --rev 3:80', %d runs: Native client total %d (%d per run), Java client %d (%d)\n", runs, start2-start1, (start2-start1)/runs, end-start2, (end-start2)/runs); | 673 System.out.printf("'hg status -A --rev 3:80', %d runs: Native client total %d (%d per run), Java client %d (%d)\n", runs, start2 - start1, (start2 - start1) / runs, end - start2, |
674 (end - start2) / runs); | |
639 } | 675 } |
640 | 676 |
641 private void report(String what, StatusCollector r) { | 677 static class StatusReporter { |
642 assertTrue(r.getErrors().isEmpty()); | 678 private final StatusOutputParser statusParser; |
643 reportNotEqual(what + "#MODIFIED", r.get(Modified), statusParser.getModified()); | 679 private final ErrorCollectorExt errorCollector; |
644 reportNotEqual(what + "#ADDED", r.get(Added), statusParser.getAdded()); | 680 |
645 reportNotEqual(what + "#REMOVED", r.get(Removed), statusParser.getRemoved()); | 681 public StatusReporter(ErrorCollectorExt ec, StatusOutputParser sp) { |
646 reportNotEqual(what + "#CLEAN", r.get(Clean), statusParser.getClean()); | 682 errorCollector = ec; |
647 reportNotEqual(what + "#IGNORED", r.get(Ignored), statusParser.getIgnored()); | 683 statusParser = sp; |
648 reportNotEqual(what + "#MISSING", r.get(Missing), statusParser.getMissing()); | 684 } |
649 reportNotEqual(what + "#UNKNOWN", r.get(Unknown), statusParser.getUnknown()); | 685 |
650 // TODO test copies | 686 public void report(String what, StatusCollector r) { |
651 } | 687 errorCollector.assertTrue(what, r.getErrors().isEmpty()); |
652 | 688 report(what, r.asStatusRecord()); |
653 private void report(String what, HgStatusCollector.Record r, StatusOutputParser statusParser) { | 689 } |
654 reportNotEqual(what + "#MODIFIED", r.getModified(), statusParser.getModified()); | 690 |
655 reportNotEqual(what + "#ADDED", r.getAdded(), statusParser.getAdded()); | 691 public void report(String what, HgStatusCollector.Record r) { |
656 reportNotEqual(what + "#REMOVED", r.getRemoved(), statusParser.getRemoved()); | 692 reportNotEqual(what + "#MODIFIED", r.getModified(), statusParser.getModified()); |
657 reportNotEqual(what + "#CLEAN", r.getClean(), statusParser.getClean()); | 693 reportNotEqual(what + "#ADDED", r.getAdded(), statusParser.getAdded()); |
658 reportNotEqual(what + "#IGNORED", r.getIgnored(), statusParser.getIgnored()); | 694 reportNotEqual(what + "#REMOVED", r.getRemoved(), statusParser.getRemoved()); |
659 reportNotEqual(what + "#MISSING", r.getMissing(), statusParser.getMissing()); | 695 reportNotEqual(what + "#CLEAN", r.getClean(), statusParser.getClean()); |
660 reportNotEqual(what + "#UNKNOWN", r.getUnknown(), statusParser.getUnknown()); | 696 reportNotEqual(what + "#IGNORED", r.getIgnored(), statusParser.getIgnored()); |
661 List<Path> copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); | 697 reportNotEqual(what + "#MISSING", r.getMissing(), statusParser.getMissing()); |
662 HashMap<Path, String> copyDiff = new HashMap<Path,String>(); | 698 reportNotEqual(what + "#UNKNOWN", r.getUnknown(), statusParser.getUnknown()); |
663 if (copiedKeyDiff.isEmpty()) { | 699 List<Path> copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); |
664 for (Path jk : r.getCopied().keySet()) { | 700 HashMap<Path, String> copyDiff = new HashMap<Path, String>(); |
665 Path jv = r.getCopied().get(jk); | 701 if (copiedKeyDiff.isEmpty()) { |
666 if (statusParser.getCopied().containsKey(jk)) { | 702 for (Path jk : r.getCopied().keySet()) { |
667 Path cmdv = statusParser.getCopied().get(jk); | 703 Path jv = r.getCopied().get(jk); |
668 if (!jv.equals(cmdv)) { | 704 if (statusParser.getCopied().containsKey(jk)) { |
669 copyDiff.put(jk, jv + " instead of " + cmdv); | 705 Path cmdv = statusParser.getCopied().get(jk); |
706 if (!jv.equals(cmdv)) { | |
707 copyDiff.put(jk, jv + " instead of " + cmdv); | |
708 } | |
709 } else { | |
710 copyDiff.put(jk, "ERRONEOUSLY REPORTED IN JAVA"); | |
670 } | 711 } |
712 } | |
713 } | |
714 errorCollector.checkThat(what + "#Non-matching 'copied' keys: ", copiedKeyDiff, equalTo(Collections.<Path> emptyList())); | |
715 errorCollector.checkThat(what + "#COPIED", copyDiff, equalTo(Collections.<Path, String> emptyMap())); | |
716 } | |
717 | |
718 private <T extends Comparable<? super T>> void reportNotEqual(String what, Collection<T> l1, Collection<T> l2) { | |
719 // List<T> diff = difference(l1, l2); | |
720 // errorCollector.checkThat(what, diff, equalTo(Collections.<T>emptyList())); | |
721 ArrayList<T> sl1 = new ArrayList<T>(l1); | |
722 Collections.sort(sl1); | |
723 ArrayList<T> sl2 = new ArrayList<T>(l2); | |
724 Collections.sort(sl2); | |
725 errorCollector.checkThat(what, sl1, equalTo(sl2)); | |
726 } | |
727 | |
728 public static <T> List<T> difference(Collection<T> l1, Collection<T> l2) { | |
729 LinkedList<T> result = new LinkedList<T>(l2); | |
730 for (T t : l1) { | |
731 if (l2.contains(t)) { | |
732 result.remove(t); | |
671 } else { | 733 } else { |
672 copyDiff.put(jk, "ERRONEOUSLY REPORTED IN JAVA"); | 734 result.add(t); |
673 } | 735 } |
674 } | 736 } |
675 } | 737 return result; |
676 errorCollector.checkThat(what + "#Non-matching 'copied' keys: ", copiedKeyDiff, equalTo(Collections.<Path>emptyList())); | 738 } |
677 errorCollector.checkThat(what + "#COPIED", copyDiff, equalTo(Collections.<Path,String>emptyMap())); | |
678 } | |
679 | |
680 private <T extends Comparable<? super T>> void reportNotEqual(String what, Collection<T> l1, Collection<T> l2) { | |
681 // List<T> diff = difference(l1, l2); | |
682 // errorCollector.checkThat(what, diff, equalTo(Collections.<T>emptyList())); | |
683 ArrayList<T> sl1 = new ArrayList<T>(l1); | |
684 Collections.sort(sl1); | |
685 ArrayList<T> sl2 = new ArrayList<T>(l2); | |
686 Collections.sort(sl2); | |
687 errorCollector.checkThat(what, sl1, equalTo(sl2)); | |
688 } | |
689 | |
690 private static <T> List<T> difference(Collection<T> l1, Collection<T> l2) { | |
691 LinkedList<T> result = new LinkedList<T>(l2); | |
692 for (T t : l1) { | |
693 if (l2.contains(t)) { | |
694 result.remove(t); | |
695 } else { | |
696 result.add(t); | |
697 } | |
698 } | |
699 return result; | |
700 } | 739 } |
701 } | 740 } |