tikhomirov@61: /* tikhomirov@66: * Copyright (c) 2011 TMate Software Ltd tikhomirov@66: * tikhomirov@66: * This program is free software; you can redistribute it and/or modify tikhomirov@66: * it under the terms of the GNU General Public License as published by tikhomirov@66: * the Free Software Foundation; version 2 of the License. tikhomirov@66: * tikhomirov@66: * This program is distributed in the hope that it will be useful, tikhomirov@66: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@66: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@66: * GNU General Public License for more details. tikhomirov@66: * tikhomirov@66: * For information on how to redistribute this software under tikhomirov@66: * the terms of a license other than GNU General Public License tikhomirov@102: * contact TMate Software at support@hg4j.com tikhomirov@61: */ tikhomirov@66: package org.tmatesoft.hg.test; tikhomirov@61: tikhomirov@229: import static org.hamcrest.CoreMatchers.equalTo; tikhomirov@229: import static org.junit.Assert.assertEquals; tikhomirov@224: import static org.junit.Assert.assertTrue; tikhomirov@127: import static org.tmatesoft.hg.core.HgStatus.Kind.*; tikhomirov@74: import static org.tmatesoft.hg.repo.HgRepository.TIP; tikhomirov@68: tikhomirov@282: import java.util.ArrayList; tikhomirov@61: import java.util.Collection; tikhomirov@103: import java.util.Collections; tikhomirov@75: import java.util.HashMap; tikhomirov@360: import java.util.LinkedHashMap; tikhomirov@61: import java.util.LinkedList; tikhomirov@61: import java.util.List; tikhomirov@109: import java.util.Map; tikhomirov@109: import java.util.TreeMap; tikhomirov@61: tikhomirov@101: import org.junit.Assume; tikhomirov@282: import org.junit.Ignore; tikhomirov@103: import org.junit.Rule; tikhomirov@101: import org.junit.Test; tikhomirov@127: import org.tmatesoft.hg.core.HgStatus; tikhomirov@224: import org.tmatesoft.hg.core.HgStatus.Kind; tikhomirov@131: import org.tmatesoft.hg.core.HgStatusCommand; tikhomirov@360: import org.tmatesoft.hg.core.HgStatusHandler; tikhomirov@229: import org.tmatesoft.hg.internal.PathGlobMatcher; tikhomirov@101: import org.tmatesoft.hg.repo.HgLookup; tikhomirov@74: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@94: import org.tmatesoft.hg.repo.HgStatusCollector; tikhomirov@94: import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; tikhomirov@133: import org.tmatesoft.hg.util.Path; tikhomirov@360: import org.tmatesoft.hg.util.Status; tikhomirov@68: tikhomirov@61: tikhomirov@61: /** tikhomirov@76: * tikhomirov@66: * @author Artem Tikhomirov tikhomirov@66: * @author TMate Software Ltd. tikhomirov@61: */ tikhomirov@61: public class TestStatus { tikhomirov@61: tikhomirov@103: @Rule tikhomirov@103: public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); tikhomirov@103: tikhomirov@224: private HgRepository repo; tikhomirov@66: private StatusOutputParser statusParser; tikhomirov@66: private ExecHelper eh; tikhomirov@66: tikhomirov@103: public static void main(String[] args) throws Throwable { tikhomirov@101: TestStatus test = new TestStatus(); tikhomirov@66: test.testLowLevel(); tikhomirov@99: test.testStatusCommand(); tikhomirov@100: test.testPerformance(); tikhomirov@103: test.errorCollector.verify(); tikhomirov@282: // tikhomirov@282: TestStatus t2 = new TestStatus(new HgLookup().detect("/temp/hg/hg4j-merging/hg4j")); tikhomirov@283: t2.testDirstateParentOtherThanTipWithUpdate(); tikhomirov@282: t2.errorCollector.verify(); tikhomirov@282: TestStatus t3 = new TestStatus(new HgLookup().detect("/temp/hg/cpython")); tikhomirov@283: t3.testDirstateParentOtherThanTipNoUpdate(); tikhomirov@282: t3.errorCollector.verify(); tikhomirov@66: } tikhomirov@66: tikhomirov@101: public TestStatus() throws Exception { tikhomirov@101: this(new HgLookup().detectFromWorkingDir()); tikhomirov@101: } tikhomirov@101: tikhomirov@101: private TestStatus(HgRepository hgRepo) { tikhomirov@66: repo = hgRepo; tikhomirov@101: Assume.assumeTrue(!repo.isInvalid()); tikhomirov@66: statusParser = new StatusOutputParser(); tikhomirov@282: eh = new ExecHelper(statusParser, hgRepo.getWorkingDir()); tikhomirov@66: } tikhomirov@66: tikhomirov@101: @Test tikhomirov@66: public void testLowLevel() throws Exception { tikhomirov@94: final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); tikhomirov@68: statusParser.reset(); tikhomirov@61: eh.run("hg", "status", "-A"); tikhomirov@94: HgStatusCollector.Record r = wcc.status(HgRepository.TIP); tikhomirov@62: report("hg status -A", r, statusParser); tikhomirov@62: // tikhomirov@62: statusParser.reset(); tikhomirov@62: int revision = 3; tikhomirov@62: eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); tikhomirov@62: r = wcc.status(revision); tikhomirov@62: report("status -A --rev " + revision, r, statusParser); tikhomirov@62: // tikhomirov@62: statusParser.reset(); tikhomirov@62: eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); tikhomirov@94: r = new HgStatusCollector.Record(); tikhomirov@94: new HgStatusCollector(repo).change(revision, r); tikhomirov@62: report("status -A --change " + revision, r, statusParser); tikhomirov@88: // tikhomirov@88: statusParser.reset(); tikhomirov@88: int rev2 = 80; tikhomirov@88: final String range = String.valueOf(revision) + ":" + String.valueOf(rev2); tikhomirov@88: eh.run("hg", "status", "-A", "--rev", range); tikhomirov@94: r = new HgStatusCollector(repo).status(revision, rev2); tikhomirov@88: report("Status -A -rev " + range, r, statusParser); tikhomirov@62: } tikhomirov@282: tikhomirov@282: /** tikhomirov@282: * hg up --rev ; hg status tikhomirov@282: * tikhomirov@282: * To check if HgWorkingCopyStatusCollector respects actual working copy parent (takes from dirstate) tikhomirov@282: * and if status is calculated correctly tikhomirov@282: */ tikhomirov@282: @Test tikhomirov@282: @Ignore("modifies test repository, needs careful configuration") tikhomirov@283: public void testDirstateParentOtherThanTipWithUpdate() throws Exception { tikhomirov@283: int revToUpdate = 238; tikhomirov@283: try { tikhomirov@283: eh.run("hg", "up", "--rev", String.valueOf(revToUpdate)); tikhomirov@283: testDirstateParentOtherThanTipNoUpdate(); tikhomirov@283: } finally { tikhomirov@283: eh.run("hg", "up"); tikhomirov@283: } tikhomirov@283: } tikhomirov@283: tikhomirov@283: @Test tikhomirov@283: @Ignore("needs configuration as it requires special repository") tikhomirov@283: public void testDirstateParentOtherThanTipNoUpdate() throws Exception { tikhomirov@282: final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); tikhomirov@282: statusParser.reset(); tikhomirov@283: // tikhomirov@283: eh.run("hg", "status", "-A"); tikhomirov@283: HgStatusCollector.Record r = wcc.status(HgRepository.TIP); tikhomirov@283: report("hg status -A", r, statusParser); tikhomirov@283: // tikhomirov@283: statusParser.reset(); tikhomirov@283: int revision = 3; tikhomirov@283: eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); tikhomirov@283: r = wcc.status(revision); tikhomirov@283: report("status -A --rev " + revision, r, statusParser); tikhomirov@282: } tikhomirov@282: tikhomirov@62: tikhomirov@101: @Test tikhomirov@66: public void testStatusCommand() throws Exception { tikhomirov@131: final HgStatusCommand sc = new HgStatusCommand(repo).all(); tikhomirov@109: StatusCollector r; tikhomirov@68: statusParser.reset(); tikhomirov@68: eh.run("hg", "status", "-A"); tikhomirov@109: sc.execute(r = new StatusCollector()); tikhomirov@109: report("hg status -A", r); tikhomirov@68: // tikhomirov@68: statusParser.reset(); tikhomirov@68: int revision = 3; tikhomirov@68: eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); tikhomirov@109: sc.base(revision).execute(r = new StatusCollector()); tikhomirov@109: report("status -A --rev " + revision, r); tikhomirov@68: // tikhomirov@68: statusParser.reset(); tikhomirov@68: eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); tikhomirov@109: sc.base(TIP).revision(revision).execute(r = new StatusCollector()); tikhomirov@109: report("status -A --change " + revision, r); tikhomirov@68: tikhomirov@68: // TODO check not -A, but defaults()/custom set of modifications tikhomirov@66: } tikhomirov@66: tikhomirov@360: private static class StatusCollector implements HgStatusHandler { tikhomirov@224: private final Map> kind2names = new TreeMap>(); tikhomirov@224: private final Map> name2kinds = new TreeMap>(); tikhomirov@360: private final Map name2error = new LinkedHashMap(); tikhomirov@109: tikhomirov@109: public void handleStatus(HgStatus s) { tikhomirov@224: List l = kind2names.get(s.getKind()); tikhomirov@109: if (l == null) { tikhomirov@224: kind2names.put(s.getKind(), l = new LinkedList()); tikhomirov@109: } tikhomirov@109: l.add(s.getPath()); tikhomirov@224: // tikhomirov@224: List k = name2kinds.get(s.getPath()); tikhomirov@224: if (k == null) { tikhomirov@224: name2kinds.put(s.getPath(), k = new LinkedList()); tikhomirov@224: } tikhomirov@224: k.add(s.getKind()); tikhomirov@109: } tikhomirov@109: tikhomirov@360: public void handleError(Path file, Status s) { tikhomirov@360: name2error.put(file, s); tikhomirov@360: } tikhomirov@360: tikhomirov@109: public List get(Kind k) { tikhomirov@224: List rv = kind2names.get(k); tikhomirov@224: return rv == null ? Collections.emptyList() : rv; tikhomirov@224: } tikhomirov@224: tikhomirov@224: public List get(Path p) { tikhomirov@224: List rv = name2kinds.get(p); tikhomirov@224: return rv == null ? Collections.emptyList() : rv; tikhomirov@109: } tikhomirov@360: tikhomirov@360: public Map getErrors() { tikhomirov@360: return name2error; tikhomirov@360: } tikhomirov@109: } tikhomirov@224: tikhomirov@224: /* tikhomirov@224: * status-1/dir/file5 was added in rev 8, scheduled (hg remove file5) for removal, but not yet committed tikhomirov@224: * Erroneously reported extra REMOVED file (the one added and removed in between). Shall not tikhomirov@224: */ tikhomirov@224: @Test tikhomirov@224: public void testRemovedAgainstBaseWithoutIt() throws Exception { tikhomirov@224: // check very end of WCStatusCollector, foreach left knownEntry, collect == null || baseRevFiles.contains() tikhomirov@224: repo = Configuration.get().find("status-1"); tikhomirov@224: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@224: StatusCollector sc = new StatusCollector(); tikhomirov@224: cmd.all().base(7).execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: Path file5 = Path.create("dir/file5"); tikhomirov@224: // shall not be listed at all tikhomirov@224: assertTrue(sc.get(file5).isEmpty()); tikhomirov@224: } tikhomirov@109: tikhomirov@224: /* tikhomirov@224: * status-1/file2 is tracked, but later .hgignore got entry to ignore it, file2 got modified tikhomirov@224: * HG doesn't respect .hgignore for tracked files. tikhomirov@224: * Now reported as ignored and missing(?!). tikhomirov@224: * Shall be reported as modified. tikhomirov@224: */ tikhomirov@224: @Test tikhomirov@224: public void testTrackedModifiedIgnored() throws Exception { tikhomirov@224: repo = Configuration.get().find("status-1"); tikhomirov@224: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@224: StatusCollector sc = new StatusCollector(); tikhomirov@224: cmd.all().execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: final Path file2 = Path.create("file2"); tikhomirov@224: assertTrue(sc.get(file2).contains(Modified)); tikhomirov@224: assertTrue(sc.get(file2).size() == 1); tikhomirov@224: } tikhomirov@76: tikhomirov@224: /* tikhomirov@224: * status/dir/file4, added in rev 3, has been scheduled for removal (hg remove -Af file4), but still there in the WC. tikhomirov@224: * Shall be reported as Removed, when comparing against rev 3 tikhomirov@224: * (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) tikhomirov@224: */ tikhomirov@224: @Test tikhomirov@224: public void testMarkedRemovedButStillInWC() throws Exception { tikhomirov@224: repo = Configuration.get().find("status-1"); tikhomirov@224: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@224: StatusCollector sc = new StatusCollector(); tikhomirov@224: cmd.all().execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: Path file4 = Path.create("dir/file4"); tikhomirov@224: assertTrue(sc.get(file4).contains(Removed)); tikhomirov@224: assertTrue(sc.get(file4).size() == 1); tikhomirov@224: // tikhomirov@224: // different code path (collect != null) tikhomirov@224: cmd.base(3).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file4).contains(Removed)); tikhomirov@224: assertTrue(sc.get(file4).size() == 1); tikhomirov@224: // tikhomirov@224: // wasn't there in rev 2, shall not be reported at all tikhomirov@224: cmd.base(2).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file4).isEmpty()); tikhomirov@224: } tikhomirov@224: tikhomirov@224: /* tikhomirov@224: * status-1/dir/file3 tracked, listed in .hgignore since rev 4, removed (hg remove file3) from repo and WC tikhomirov@224: * (but entry in .hgignore left) in revision 5, and new file3 got created in WC. tikhomirov@224: * Shall be reported as ignored when comparing against WC's parent, tikhomirov@224: * and both ignored and removed when comparing against revision 3 tikhomirov@224: */ tikhomirov@224: @Test tikhomirov@224: public void testRemovedIgnoredInWC() throws Exception { tikhomirov@224: // check branch !known, ignored tikhomirov@224: repo = Configuration.get().find("status-1"); tikhomirov@224: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@224: StatusCollector sc = new StatusCollector(); tikhomirov@224: cmd.all().execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: final Path file3 = Path.create("dir/file3"); tikhomirov@224: assertTrue(sc.get(file3).contains(Ignored)); tikhomirov@224: assertTrue(sc.get(file3).size() == 1); tikhomirov@224: // tikhomirov@224: cmd.base(3).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file3).contains(Ignored)); tikhomirov@224: assertTrue(sc.get(file3).contains(Removed)); tikhomirov@224: assertTrue(sc.get(file3).size() == 2); tikhomirov@224: // tikhomirov@224: cmd.base(5).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file3).contains(Ignored)); tikhomirov@224: assertTrue(sc.get(file3).size() == 1); tikhomirov@224: // tikhomirov@224: cmd.base(0).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file3).contains(Ignored)); tikhomirov@224: assertTrue(sc.get(file3).size() == 1); tikhomirov@224: tikhomirov@224: } tikhomirov@224: tikhomirov@224: /* tikhomirov@224: * status/file1 was removed in cset 2. New file with the same name in the WC. tikhomirov@224: * Shall report 2 statuses (as cmdline hg does): unknown and removed when comparing against that revision. tikhomirov@224: */ tikhomirov@224: @Test tikhomirov@224: public void testNewFileWithSameNameAsDeletedOld() throws Exception { tikhomirov@224: // check branch !known, !ignored (=> unknown) tikhomirov@224: repo = Configuration.get().find("status-1"); tikhomirov@224: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@224: StatusCollector sc = new StatusCollector(); tikhomirov@224: cmd.base(1); tikhomirov@224: cmd.all().execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: final Path file1 = Path.create("file1"); tikhomirov@224: assertTrue(sc.get(file1).contains(Unknown)); tikhomirov@224: assertTrue(sc.get(file1).contains(Removed)); tikhomirov@224: assertTrue(sc.get(file1).size() == 2); tikhomirov@224: // tikhomirov@224: // no file1 in rev 2, shall be reported as unknown only tikhomirov@224: cmd.base(2).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@224: assertTrue(sc.get(file1).contains(Unknown)); tikhomirov@224: assertTrue(sc.get(file1).size() == 1); tikhomirov@76: } tikhomirov@76: tikhomirov@229: @Test tikhomirov@229: public void testSubTreeStatus() throws Exception { tikhomirov@229: repo = Configuration.get().find("status-1"); tikhomirov@229: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@229: StatusCollector sc = new StatusCollector(); tikhomirov@229: cmd.match(new PathGlobMatcher("*")); tikhomirov@229: cmd.all().execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@229: /* tikhomirov@229: * C .hgignore tikhomirov@229: * ? file1 tikhomirov@229: * M file2 tikhomirov@229: * C readme tikhomirov@229: */ tikhomirov@229: final Path file1 = Path.create("file1"); tikhomirov@229: assertTrue(sc.get(file1).contains(Unknown)); tikhomirov@229: assertTrue(sc.get(file1).size() == 1); tikhomirov@229: assertTrue(sc.get(Removed).isEmpty()); tikhomirov@229: assertTrue(sc.get(Clean).size() == 2); tikhomirov@229: assertTrue(sc.get(Modified).size() == 1); tikhomirov@229: // tikhomirov@229: cmd.match(new PathGlobMatcher("dir/*")).execute(sc = new StatusCollector()); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@229: /* tikhomirov@229: * I dir/file3 tikhomirov@229: * R dir/file4 tikhomirov@229: * R dir/file5 tikhomirov@229: */ tikhomirov@229: assertTrue(sc.get(Modified).isEmpty()); tikhomirov@229: assertTrue(sc.get(Added).isEmpty()); tikhomirov@229: assertTrue(sc.get(Ignored).size() == 1); tikhomirov@229: assertTrue(sc.get(Removed).size() == 2); tikhomirov@229: } tikhomirov@229: tikhomirov@229: tikhomirov@229: @Test tikhomirov@229: public void testSpecificFileStatus() throws Exception { tikhomirov@229: repo = Configuration.get().find("status-1"); tikhomirov@229: // files only tikhomirov@229: final Path file2 = Path.create("file2"); tikhomirov@229: final Path file3 = Path.create("dir/file3"); tikhomirov@229: HgWorkingCopyStatusCollector sc = HgWorkingCopyStatusCollector.create(repo, file2, file3); tikhomirov@229: HgStatusCollector.Record r = new HgStatusCollector.Record(); tikhomirov@229: sc.walk(TIP, r); tikhomirov@229: assertTrue(r.getAdded().isEmpty()); tikhomirov@229: assertTrue(r.getRemoved().isEmpty()); tikhomirov@229: assertTrue(r.getUnknown().isEmpty()); tikhomirov@229: assertTrue(r.getClean().isEmpty()); tikhomirov@229: assertTrue(r.getMissing().isEmpty()); tikhomirov@229: assertTrue(r.getCopied().isEmpty()); tikhomirov@229: assertTrue(r.getIgnored().contains(file3)); tikhomirov@229: assertTrue(r.getIgnored().size() == 1); tikhomirov@229: assertTrue(r.getModified().contains(file2)); tikhomirov@229: assertTrue(r.getModified().size() == 1); tikhomirov@229: // mix files and directories tikhomirov@229: final Path readme = Path.create("readme"); tikhomirov@229: final Path dir = Path.create("dir/"); tikhomirov@229: sc = HgWorkingCopyStatusCollector.create(repo, readme, dir); tikhomirov@229: sc.walk(TIP, r = new HgStatusCollector.Record()); tikhomirov@229: assertTrue(r.getAdded().isEmpty()); tikhomirov@229: assertTrue(r.getRemoved().size() == 2); tikhomirov@229: for (Path p : r.getRemoved()) { tikhomirov@229: assertEquals(p.compareWith(dir), Path.CompareResult.Nested); tikhomirov@229: } tikhomirov@229: assertTrue(r.getUnknown().isEmpty()); tikhomirov@229: assertTrue(r.getClean().size() == 1); tikhomirov@229: assertTrue(r.getClean().contains(readme)); tikhomirov@229: assertTrue(r.getMissing().isEmpty()); tikhomirov@229: assertTrue(r.getCopied().isEmpty()); tikhomirov@229: assertTrue(r.getIgnored().contains(file3)); tikhomirov@229: assertTrue(r.getIgnored().size() == 1); tikhomirov@229: assertTrue(r.getModified().isEmpty()); tikhomirov@229: } tikhomirov@229: tikhomirov@229: @Test tikhomirov@229: public void testSameResultDirectPathVsMatcher() throws Exception { tikhomirov@229: repo = Configuration.get().find("status-1"); tikhomirov@229: final Path file3 = Path.create("dir/file3"); tikhomirov@229: final Path file5 = Path.create("dir/file5"); tikhomirov@229: tikhomirov@229: HgWorkingCopyStatusCollector sc = HgWorkingCopyStatusCollector.create(repo, file3, file5); tikhomirov@229: HgStatusCollector.Record r; tikhomirov@229: sc.walk(TIP, r = new HgStatusCollector.Record()); tikhomirov@229: assertTrue(r.getRemoved().contains(file5)); tikhomirov@229: assertTrue(r.getIgnored().contains(file3)); tikhomirov@229: // tikhomirov@229: // query for the same file, but with tikhomirov@229: sc = HgWorkingCopyStatusCollector.create(repo, new PathGlobMatcher(file3.toString(), file5.toString())); tikhomirov@229: sc.walk(TIP, r = new HgStatusCollector.Record()); tikhomirov@229: assertTrue(r.getRemoved().contains(file5)); tikhomirov@229: assertTrue(r.getIgnored().contains(file3)); tikhomirov@229: } tikhomirov@229: tikhomirov@230: @Test tikhomirov@230: public void testScopeInHistoricalStatus() throws Exception { tikhomirov@230: repo = Configuration.get().find("status-1"); tikhomirov@230: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@230: cmd.base(3).revision(8).all(); tikhomirov@230: cmd.match(new PathGlobMatcher("dir/*")); tikhomirov@230: StatusCollector sc = new StatusCollector(); tikhomirov@230: cmd.execute(sc); tikhomirov@360: assertTrue(sc.getErrors().isEmpty()); tikhomirov@230: final Path file3 = Path.create("dir/file3"); tikhomirov@230: final Path file4 = Path.create("dir/file4"); tikhomirov@230: final Path file5 = Path.create("dir/file5"); tikhomirov@230: // tikhomirov@230: assertTrue(sc.get(file3).contains(Removed)); tikhomirov@230: assertTrue(sc.get(file3).size() == 1); tikhomirov@230: assertTrue(sc.get(Removed).size() == 1); tikhomirov@230: // tikhomirov@230: assertTrue(sc.get(file4).contains(Clean)); tikhomirov@230: assertTrue(sc.get(file4).size() == 1); tikhomirov@230: assertTrue(sc.get(Clean).size() == 1); tikhomirov@230: // tikhomirov@230: assertTrue(sc.get(file5).contains(Added)); tikhomirov@230: assertTrue(sc.get(file5).size() == 1); tikhomirov@230: assertTrue(sc.get(Added).size() == 1); tikhomirov@230: tikhomirov@230: } tikhomirov@230: tikhomirov@372: /** tikhomirov@372: * Issue 22 tikhomirov@372: */ tikhomirov@372: @Test tikhomirov@372: public void testOnEmptyRepositoryWithAllFilesDeleted() throws Exception { tikhomirov@372: repo = Configuration.get().find("status-2"); tikhomirov@372: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@372: cmd.all(); tikhomirov@372: StatusCollector sc = new StatusCollector(); tikhomirov@372: cmd.execute(sc); tikhomirov@372: // shall pass without exception tikhomirov@372: assertTrue(sc.getErrors().isEmpty()); tikhomirov@372: for (HgStatus.Kind k : HgStatus.Kind.values()) { tikhomirov@372: assertTrue("Kind " + k.name() + " shall be empty",sc.get(k).isEmpty()); tikhomirov@372: } tikhomirov@372: } tikhomirov@372: tikhomirov@377: /** tikhomirov@377: * Issue 22, two subsequent commits that remove all repository files, each in a different branch. tikhomirov@377: * Here's excerpt from my RevlogWriter utility: tikhomirov@377: *
tikhomirov@377: 	 * 		final List filesList = Collections.singletonList("file1");
tikhomirov@377: 	 *	//
tikhomirov@377: 	 *	file1.writeUncompressed(-1, -1, 0, 0, "garbage".getBytes());
tikhomirov@377: 	 *	//
tikhomirov@377: 	 *	ManifestBuilder mb = new ManifestBuilder();
tikhomirov@377: 	 *	mb.reset().add("file1", file1.getRevision(0));
tikhomirov@377: 	 *	manifest.writeUncompressed(-1, -1, 0, 0, mb.build()); // manifest revision 0
tikhomirov@377: 	 *	final byte[] cset1 = buildChangelogEntry(manifest.getRevision(0), Collections.emptyMap(), filesList, "Add a file");
tikhomirov@377: 	 *	changelog.writeUncompressed(-1, -1, 0, 0, cset1);
tikhomirov@377: 	 *	//
tikhomirov@377: 	 *	// pretend we delete all files in a branch 1
tikhomirov@377: 	 *	manifest.writeUncompressed(0, -1, 1, 1, new byte[0]); // manifest revision 1
tikhomirov@377: 	 *	final byte[] cset2 = buildChangelogEntry(manifest.getRevision(1), Collections.singletonMap("branch", "delete-all-1"), filesList, "Delete all files in a first branch");
tikhomirov@377: 	 *	 changelog.writeUncompressed(0, -1, 1, 1, cset2);
tikhomirov@377: 	 *	//
tikhomirov@377: 	 *	// pretend we delete all files in a branch 2 (which is based on revision 0, same as branch 1)
tikhomirov@377: 	 *	manifest.writeUncompressed(1, -1, 1 /*!!! here comes baseRevision != index * /, 2, new byte[0]); // manifest revision 2
tikhomirov@377: 	 *	final byte[] cset3 = buildChangelogEntry(manifest.getRevision(2), Collections.singletonMap("branch", "delete-all-2"), filesList, "Again delete all files but in another branch");
tikhomirov@377: 	 *	changelog.writeUncompressed(0, -1, 2, 2, cset3);
tikhomirov@377: 	 * 
tikhomirov@377: */ tikhomirov@377: @Test tikhomirov@377: public void testOnEmptyRepositoryWithAllFilesDeletedInBranch() throws Exception { tikhomirov@377: repo = Configuration.get().find("status-3"); tikhomirov@377: HgStatusCommand cmd = new HgStatusCommand(repo); tikhomirov@377: cmd.all(); tikhomirov@377: StatusCollector sc = new StatusCollector(); tikhomirov@377: cmd.execute(sc); tikhomirov@377: // shall pass without exception tikhomirov@377: assertTrue(sc.getErrors().isEmpty()); tikhomirov@377: for (HgStatus.Kind k : HgStatus.Kind.values()) { tikhomirov@377: assertTrue("Kind " + k.name() + " shall be empty",sc.get(k).isEmpty()); tikhomirov@377: } tikhomirov@377: } tikhomirov@377: tikhomirov@377: tikhomirov@100: /* tikhomirov@100: * With warm-up of previous tests, 10 runs, time in milliseconds tikhomirov@100: * 'hg status -A': Native client total 953 (95 per run), Java client 94 (9) tikhomirov@100: * 'hg status -A --rev 3:80': Native client total 1828 (182 per run), Java client 235 (23) tikhomirov@100: * 'hg log --debug', 10 runs: Native client total 1766 (176 per run), Java client 78 (7) tikhomirov@147: * tikhomirov@147: * 18.02.2011 tikhomirov@147: * 'hg status -A --rev 3:80', 10 runs: Native client total 2000 (200 per run), Java client 250 (25) tikhomirov@147: * 'hg log --debug', 10 runs: Native client total 2297 (229 per run), Java client 125 (12) tikhomirov@162: * tikhomirov@162: * 9.3.2011 (DataAccess instead of byte[] in ReflogStream.Inspector tikhomirov@162: * 'hg status -A', 10 runs: Native client total 1516 (151 per run), Java client 219 (21) tikhomirov@162: * 'hg status -A --rev 3:80', 10 runs: Native client total 1875 (187 per run), Java client 3187 (318) (!!! ???) tikhomirov@162: * 'hg log --debug', 10 runs: Native client total 2484 (248 per run), Java client 344 (34) tikhomirov@100: */ tikhomirov@100: public void testPerformance() throws Exception { tikhomirov@100: final int runs = 10; tikhomirov@100: final long start1 = System.currentTimeMillis(); tikhomirov@100: for (int i = 0; i < runs; i++) { tikhomirov@100: statusParser.reset(); tikhomirov@100: eh.run("hg", "status", "-A", "--rev", "3:80"); tikhomirov@100: } tikhomirov@100: final long start2 = System.currentTimeMillis(); tikhomirov@100: for (int i = 0; i < runs; i++) { tikhomirov@109: StatusCollector r = new StatusCollector(); tikhomirov@131: new HgStatusCommand(repo).all().base(3).revision(80).execute(r); tikhomirov@100: } tikhomirov@100: final long end = System.currentTimeMillis(); tikhomirov@100: 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); tikhomirov@100: } tikhomirov@100: tikhomirov@109: private void report(String what, StatusCollector r) { tikhomirov@360: assertTrue(r.getErrors().isEmpty()); tikhomirov@109: reportNotEqual(what + "#MODIFIED", r.get(Modified), statusParser.getModified()); tikhomirov@109: reportNotEqual(what + "#ADDED", r.get(Added), statusParser.getAdded()); tikhomirov@109: reportNotEqual(what + "#REMOVED", r.get(Removed), statusParser.getRemoved()); tikhomirov@109: reportNotEqual(what + "#CLEAN", r.get(Clean), statusParser.getClean()); tikhomirov@109: reportNotEqual(what + "#IGNORED", r.get(Ignored), statusParser.getIgnored()); tikhomirov@109: reportNotEqual(what + "#MISSING", r.get(Missing), statusParser.getMissing()); tikhomirov@109: reportNotEqual(what + "#UNKNOWN", r.get(Unknown), statusParser.getUnknown()); tikhomirov@109: // FIXME test copies tikhomirov@109: } tikhomirov@109: tikhomirov@103: private void report(String what, HgStatusCollector.Record r, StatusOutputParser statusParser) { tikhomirov@103: reportNotEqual(what + "#MODIFIED", r.getModified(), statusParser.getModified()); tikhomirov@103: reportNotEqual(what + "#ADDED", r.getAdded(), statusParser.getAdded()); tikhomirov@103: reportNotEqual(what + "#REMOVED", r.getRemoved(), statusParser.getRemoved()); tikhomirov@103: reportNotEqual(what + "#CLEAN", r.getClean(), statusParser.getClean()); tikhomirov@103: reportNotEqual(what + "#IGNORED", r.getIgnored(), statusParser.getIgnored()); tikhomirov@103: reportNotEqual(what + "#MISSING", r.getMissing(), statusParser.getMissing()); tikhomirov@103: reportNotEqual(what + "#UNKNOWN", r.getUnknown(), statusParser.getUnknown()); tikhomirov@93: List copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); tikhomirov@93: HashMap copyDiff = new HashMap(); tikhomirov@75: if (copiedKeyDiff.isEmpty()) { tikhomirov@93: for (Path jk : r.getCopied().keySet()) { tikhomirov@93: Path jv = r.getCopied().get(jk); tikhomirov@75: if (statusParser.getCopied().containsKey(jk)) { tikhomirov@93: Path cmdv = statusParser.getCopied().get(jk); tikhomirov@75: if (!jv.equals(cmdv)) { tikhomirov@75: copyDiff.put(jk, jv + " instead of " + cmdv); tikhomirov@75: } tikhomirov@75: } else { tikhomirov@75: copyDiff.put(jk, "ERRONEOUSLY REPORTED IN JAVA"); tikhomirov@75: } tikhomirov@75: } tikhomirov@75: } tikhomirov@103: errorCollector.checkThat(what + "#Non-matching 'copied' keys: ", copiedKeyDiff, equalTo(Collections.emptyList())); tikhomirov@103: errorCollector.checkThat(what + "#COPIED", copyDiff, equalTo(Collections.emptyMap())); tikhomirov@61: } tikhomirov@61: tikhomirov@282: private > void reportNotEqual(String what, Collection l1, Collection l2) { tikhomirov@282: // List diff = difference(l1, l2); tikhomirov@282: // errorCollector.checkThat(what, diff, equalTo(Collections.emptyList())); tikhomirov@282: ArrayList sl1 = new ArrayList(l1); tikhomirov@282: Collections.sort(sl1); tikhomirov@282: ArrayList sl2 = new ArrayList(l2); tikhomirov@282: Collections.sort(sl2); tikhomirov@282: errorCollector.checkThat(what, sl1, equalTo(sl2)); tikhomirov@61: } tikhomirov@61: tikhomirov@61: private static List difference(Collection l1, Collection l2) { tikhomirov@61: LinkedList result = new LinkedList(l2); tikhomirov@61: for (T t : l1) { tikhomirov@61: if (l2.contains(t)) { tikhomirov@61: result.remove(t); tikhomirov@61: } else { tikhomirov@61: result.add(t); tikhomirov@61: } tikhomirov@61: } tikhomirov@61: return result; tikhomirov@61: } tikhomirov@61: }