tikhomirov@70: /* tikhomirov@628: * Copyright (c) 2011-2013 TMate Software Ltd tikhomirov@70: * tikhomirov@70: * This program is free software; you can redistribute it and/or modify tikhomirov@70: * it under the terms of the GNU General Public License as published by tikhomirov@70: * the Free Software Foundation; version 2 of the License. tikhomirov@70: * tikhomirov@70: * This program is distributed in the hope that it will be useful, tikhomirov@70: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@70: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@70: * GNU General Public License for more details. tikhomirov@70: * tikhomirov@70: * For information on how to redistribute this software under tikhomirov@70: * the terms of a license other than GNU General Public License tikhomirov@102: * contact TMate Software at support@hg4j.com tikhomirov@70: */ tikhomirov@70: package org.tmatesoft.hg.test; tikhomirov@70: tikhomirov@103: import static org.hamcrest.CoreMatchers.equalTo; tikhomirov@103: import static org.hamcrest.CoreMatchers.is; tikhomirov@514: import static org.junit.Assert.assertEquals; tikhomirov@147: import static org.junit.Assert.assertTrue; tikhomirov@522: import static org.tmatesoft.hg.core.HgIterateDirection.NewToOld; tikhomirov@103: tikhomirov@202: import java.util.ArrayList; tikhomirov@70: import java.util.Collections; tikhomirov@103: import java.util.Comparator; tikhomirov@70: import java.util.Iterator; tikhomirov@103: import java.util.LinkedList; tikhomirov@70: import java.util.List; tikhomirov@70: tikhomirov@518: import org.junit.Assert; tikhomirov@103: import org.junit.Rule; tikhomirov@101: import org.junit.Test; tikhomirov@507: import org.tmatesoft.hg.core.HgCallbackTargetException; tikhomirov@129: import org.tmatesoft.hg.core.HgChangeset; tikhomirov@427: import org.tmatesoft.hg.core.HgChangesetHandler; tikhomirov@507: import org.tmatesoft.hg.core.HgChangesetTreeHandler; tikhomirov@514: import org.tmatesoft.hg.core.HgFileRenameHandlerMixin; tikhomirov@249: import org.tmatesoft.hg.core.HgFileRevision; tikhomirov@528: import org.tmatesoft.hg.core.HgIterateDirection; tikhomirov@131: import org.tmatesoft.hg.core.HgLogCommand; tikhomirov@131: import org.tmatesoft.hg.core.HgLogCommand.CollectHandler; tikhomirov@507: import org.tmatesoft.hg.core.Nodeid; tikhomirov@520: import org.tmatesoft.hg.internal.AdapterPlug; tikhomirov@101: import org.tmatesoft.hg.repo.HgLookup; tikhomirov@74: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@628: import org.tmatesoft.hg.repo.HgRuntimeException; tikhomirov@70: import org.tmatesoft.hg.test.LogOutputParser.Record; tikhomirov@514: import org.tmatesoft.hg.util.Adaptable; tikhomirov@520: import org.tmatesoft.hg.util.CancelSupport; tikhomirov@520: import org.tmatesoft.hg.util.CancelledException; tikhomirov@507: import org.tmatesoft.hg.util.Pair; tikhomirov@133: import org.tmatesoft.hg.util.Path; tikhomirov@70: tikhomirov@70: tikhomirov@70: /** tikhomirov@70: * tikhomirov@70: * @author Artem Tikhomirov tikhomirov@70: * @author TMate Software Ltd. tikhomirov@70: */ tikhomirov@70: public class TestHistory { tikhomirov@70: tikhomirov@103: @Rule tikhomirov@103: public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); tikhomirov@103: tikhomirov@147: private HgRepository repo; tikhomirov@147: private final ExecHelper eh; tikhomirov@70: private LogOutputParser changelogParser; tikhomirov@70: tikhomirov@103: public static void main(String[] args) throws Throwable { tikhomirov@101: TestHistory th = new TestHistory(); tikhomirov@70: th.testCompleteLog(); tikhomirov@82: th.testFollowHistory(); tikhomirov@103: th.errorCollector.verify(); tikhomirov@147: // th.testPerformance(); tikhomirov@147: th.testOriginalTestLogRepo(); tikhomirov@147: th.testUsernames(); tikhomirov@147: th.testBranches(); tikhomirov@147: // tikhomirov@147: th.errorCollector.verify(); tikhomirov@70: } tikhomirov@101: tikhomirov@101: public TestHistory() throws Exception { tikhomirov@101: this(new HgLookup().detectFromWorkingDir()); tikhomirov@214: // this(new HgLookup().detect("\\temp\\hg\\hello")); tikhomirov@101: } tikhomirov@70: tikhomirov@101: private TestHistory(HgRepository hgRepo) { tikhomirov@70: repo = hgRepo; tikhomirov@282: eh = new ExecHelper(changelogParser = new LogOutputParser(true), repo.getWorkingDir()); tikhomirov@147: tikhomirov@70: } tikhomirov@70: tikhomirov@101: @Test tikhomirov@70: public void testCompleteLog() throws Exception { tikhomirov@70: changelogParser.reset(); tikhomirov@70: eh.run("hg", "log", "--debug"); tikhomirov@131: List r = new HgLogCommand(repo).execute(); tikhomirov@520: report("hg log - COMPLETE REPO HISTORY", r, true); tikhomirov@520: tikhomirov@522: r = new HgLogCommand(repo).order(NewToOld).execute(); tikhomirov@520: report("hg log - COMPLETE REPO HISTORY, FROM NEW TO OLD", r, false); tikhomirov@82: } tikhomirov@82: tikhomirov@101: @Test tikhomirov@82: public void testFollowHistory() throws Exception { tikhomirov@82: final Path f = Path.create("cmdline/org/tmatesoft/hg/console/Remote.java"); tikhomirov@214: assertTrue(repo.getFileNode(f).exists()); tikhomirov@214: changelogParser.reset(); tikhomirov@214: eh.run("hg", "log", "--debug", "--follow", f.toString()); tikhomirov@214: tikhomirov@518: CollectWithRenameHandler h = new CollectWithRenameHandler(); tikhomirov@214: new HgLogCommand(repo).file(f, true).execute(h); tikhomirov@521: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: HgFileRevision from = h.rh.renames.get(0).first(); tikhomirov@518: boolean fromMatched = "src/com/tmate/hgkit/console/Remote.java".equals(from.getPath().toString()); tikhomirov@214: String what = "hg log - FOLLOW FILE HISTORY"; tikhomirov@521: errorCollector.checkThat(what + "#copyReported ", h.rh.copyReported, is(true)); tikhomirov@518: errorCollector.checkThat(what + "#copyFromMatched", fromMatched, is(true)); tikhomirov@214: // tikhomirov@214: // cmdline always gives in changesets in order from newest (bigger rev number) to oldest. tikhomirov@214: // LogCommand does other way round, from oldest to newest, follewed by revisions of copy source, if any tikhomirov@214: // (apparently older than oldest of the copy target). Hence need to sort Java results according to rev numbers tikhomirov@214: final LinkedList sorted = new LinkedList(h.getChanges()); tikhomirov@214: Collections.sort(sorted, new Comparator() { tikhomirov@214: public int compare(HgChangeset cs1, HgChangeset cs2) { tikhomirov@423: return cs1.getRevisionIndex() < cs2.getRevisionIndex() ? 1 : -1; tikhomirov@214: } tikhomirov@214: }); tikhomirov@214: report(what, sorted, false); tikhomirov@70: } tikhomirov@507: tikhomirov@507: @Test tikhomirov@507: public void testChangesetTree() throws Exception { tikhomirov@507: repo = Configuration.get().find("branches-1"); tikhomirov@507: final String fname = "file1"; tikhomirov@507: assertTrue("[sanity]", repo.getFileNode(fname).exists()); tikhomirov@507: eh.run("hg", "log", "--debug", fname, "--cwd", repo.getLocation()); tikhomirov@507: tikhomirov@508: TreeCollectHandler h = new TreeCollectHandler(false); tikhomirov@508: new HgLogCommand(repo).file(fname, false).execute(h); tikhomirov@508: // since we use TreeCollectHandler with natural order (older to newer), shall reverse console result in report() tikhomirov@508: report("execute with HgChangesetTreeHandler(follow == false)", h.getResult(), true); tikhomirov@508: } tikhomirov@508: tikhomirov@518: /** tikhomirov@518: * Few tests to check newly introduced followAncestry parameter to HgLogCommand: tikhomirov@518: * followRename: true, followAncestry: false tikhomirov@518: * followRename: false, followAncestry: true tikhomirov@518: * followRename: true, followAncestry: true tikhomirov@518: * Perhaps, shall be merged with {@link #testFollowHistory()} tikhomirov@518: */ tikhomirov@518: @Test tikhomirov@518: public void testFollowRenamesNotAncestry() throws Exception { tikhomirov@518: repo = Configuration.get().find("log-follow"); tikhomirov@518: final String fname1 = "file1_a"; tikhomirov@518: final String fname2 = "file1_b"; tikhomirov@518: assertTrue("[sanity]", repo.getFileNode(fname2).exists()); tikhomirov@518: // no --follow, but two names we know have been the same file (fname1 renamed to fname2) tikhomirov@518: // sequentially gives follow rename semantics without ancestry tikhomirov@518: eh.run("hg", "log", "--debug", fname2, fname1, "--cwd", repo.getLocation()); tikhomirov@518: tikhomirov@518: CollectWithRenameHandler h = new CollectWithRenameHandler(); tikhomirov@518: new HgLogCommand(repo).file(fname2, true, false).execute(h); tikhomirov@521: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: Pair rename = h.rh.renames.get(0); tikhomirov@518: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@518: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@518: // Ensure rename info came in the right moment tikhomirov@518: errorCollector.assertEquals(1, h.lastChangesetReportedAtRename.size()); tikhomirov@518: // Command iterates old to new, rename comes after last fname1 revision. Since we don't follow tikhomirov@518: // ancestry, it's the very last revision in fname1 history tikhomirov@518: String lastRevOfFname1 = "369c0882d477c11424a62eb4b791e86d1d4b6769"; tikhomirov@518: errorCollector.assertEquals(lastRevOfFname1, h.lastChangesetReportedAtRename.get(0).getNodeid().toString()); tikhomirov@518: report("HgChangesetHandler(renames: true, ancestry:false)", h.getChanges(), true); tikhomirov@520: // tikhomirov@520: // Direction tikhomirov@520: h = new CollectWithRenameHandler(); tikhomirov@522: new HgLogCommand(repo).file(fname2, true, false).order(NewToOld).execute(h); tikhomirov@520: // Identical rename shall be reported, at the same moment tikhomirov@521: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: rename = h.rh.renames.get(0); tikhomirov@520: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@520: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@520: errorCollector.assertEquals(1, h.lastChangesetReportedAtRename.size()); tikhomirov@520: // new to old, recently reported would be the very first revision fname2 pops up tikhomirov@520: String firstRevOfFname2 = "27e7a69373b74d42e75f3211e56510ff17d01370"; tikhomirov@520: errorCollector.assertEquals(firstRevOfFname2, h.lastChangesetReportedAtRename.get(0).getNodeid().toString()); tikhomirov@520: report("HgChangesetHandler(renames: true, ancestry:false)", h.getChanges(), false); tikhomirov@520: // tikhomirov@521: // TreeChangeHandler - in #testChangesetTreeFollowRenamesNotAncestry tikhomirov@521: } tikhomirov@521: tikhomirov@521: @Test tikhomirov@521: public void testChangesetTreeFollowRenamesNotAncestry() throws Exception { tikhomirov@521: repo = Configuration.get().find("log-follow"); tikhomirov@521: final String fname1 = "file1_a"; tikhomirov@521: final String fname2 = "file1_b"; tikhomirov@521: assertTrue("[sanity]", repo.getFileNode(fname2).exists()); tikhomirov@521: // no --follow, but two names we know have been the same file (fname1 renamed to fname2) tikhomirov@521: // sequentially gives follow rename semantics without ancestry tikhomirov@521: eh.run("hg", "log", "--debug", fname2, fname1, "--cwd", repo.getLocation()); tikhomirov@521: tikhomirov@521: TreeCollectHandler h = new TreeCollectHandler(true); tikhomirov@521: RenameCollector rh = new RenameCollector(h); tikhomirov@521: // can't check that prev revision is in parent because there are forks in tikhomirov@521: // file history (e.g. rev2 and rev3 (that comes next) both have rev0 as their parent tikhomirov@521: // and followAncestry is false tikhomirov@521: // h.checkPrevInParents = true; tikhomirov@521: new HgLogCommand(repo).file(fname2, true, false).execute(h); tikhomirov@521: errorCollector.assertEquals(1, rh.renames.size()); tikhomirov@521: Pair rename = rh.renames.get(0); tikhomirov@521: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@521: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@521: report("HgChangesetTreeHandler(renames: true, ancestry:false)", h.getResult(), false); tikhomirov@521: tikhomirov@521: // Direction tikhomirov@521: h = new TreeCollectHandler(false); tikhomirov@521: rh = new RenameCollector(h); tikhomirov@521: // h.checkPrevInChildren = true; see above tikhomirov@522: new HgLogCommand(repo).file(fname2, true, false).order(NewToOld).execute(h); tikhomirov@521: errorCollector.assertEquals(1, rh.renames.size()); tikhomirov@521: rename = rh.renames.get(0); tikhomirov@521: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@521: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@521: report("HgChangesetTreeHandler(renames: true, ancestry:false)", h.getResult(), false); tikhomirov@518: } tikhomirov@518: tikhomirov@518: @Test tikhomirov@518: public void testFollowAncestryNotRenames() throws Exception { tikhomirov@518: repo = Configuration.get().find("log-follow"); tikhomirov@518: final String fname2 = "file1_b"; tikhomirov@518: assertTrue("[sanity]", repo.getFileNode(fname2).exists()); tikhomirov@521: final List fname2Follow = getAncestryWithoutRenamesFromCmdline(fname2); tikhomirov@521: tikhomirov@521: CollectWithRenameHandler h = new CollectWithRenameHandler(); tikhomirov@521: new HgLogCommand(repo).file(fname2, false, true).execute(h); tikhomirov@528: // renames are reported regardless of followRenames parameter, but tikhomirov@528: // solely based on HgFileRenameHandlerMixin tikhomirov@528: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: report("HgChangesetHandler(renames: false, ancestry:true)", h.getChanges(), fname2Follow, true, errorCollector); tikhomirov@521: // tikhomirov@521: // Direction tikhomirov@521: h = new CollectWithRenameHandler(); tikhomirov@522: new HgLogCommand(repo).file(fname2, false, true).order(NewToOld).execute(h); tikhomirov@521: report("HgChangesetHandler(renames: false, ancestry:true)", h.getChanges(), fname2Follow, false/*!!!*/, errorCollector); tikhomirov@521: // tikhomirov@521: // TreeChangeHandler - in #testChangesetTreeFollowAncestryNotRenames tikhomirov@521: } tikhomirov@521: tikhomirov@521: @Test tikhomirov@521: public void testChangesetTreeFollowAncestryNotRenames() throws Exception { tikhomirov@521: repo = Configuration.get().find("log-follow"); tikhomirov@521: final String fname2 = "file1_b"; tikhomirov@521: final List fname2Follow = getAncestryWithoutRenamesFromCmdline(fname2); tikhomirov@521: tikhomirov@521: TreeCollectHandler h = new TreeCollectHandler(false); tikhomirov@521: h.checkPrevInParents = true; tikhomirov@521: new HgLogCommand(repo).file(fname2, false, true).execute(h); tikhomirov@521: report("HgChangesetTreeHandler(renames: false, ancestry:true)", h.getResult(), fname2Follow, true, errorCollector); tikhomirov@521: tikhomirov@521: // Direction tikhomirov@521: h = new TreeCollectHandler(false); tikhomirov@521: h.checkPrevInChildren = true; tikhomirov@522: new HgLogCommand(repo).file(fname2, false, true).order(NewToOld).execute(h); tikhomirov@521: report("HgChangesetTreeHandler(renames: false, ancestry:true)", h.getResult(), fname2Follow, false, errorCollector); tikhomirov@521: } tikhomirov@521: tikhomirov@521: tikhomirov@521: private List getAncestryWithoutRenamesFromCmdline(String fname2) throws Exception { tikhomirov@518: // to get "followed" history of fname2 only (without fname1 origin), tikhomirov@518: // get the complete history and keep there only elements that match fname2 own history tikhomirov@518: eh.run("hg", "log", "--debug", "--follow", fname2, "--cwd", repo.getLocation()); tikhomirov@518: final List fname2Follow = new LinkedList(changelogParser.getResult()); tikhomirov@518: changelogParser.reset(); tikhomirov@518: eh.run("hg", "log", "--debug", fname2, "--cwd", repo.getLocation()); tikhomirov@518: // fname2Follow.retainAll(changelogParser.getResult()); tikhomirov@518: for (Iterator it = fname2Follow.iterator(); it.hasNext();) { tikhomirov@518: Record r = it.next(); tikhomirov@518: boolean belongsToSoleFname2History = false; tikhomirov@518: for (Record d : changelogParser.getResult()) { tikhomirov@518: if (d.changesetIndex == r.changesetIndex) { tikhomirov@518: assert d.changesetNodeid.equals(r.changesetNodeid) : "[sanity]"; tikhomirov@518: belongsToSoleFname2History = true; tikhomirov@518: break; tikhomirov@518: } tikhomirov@518: } tikhomirov@518: if (!belongsToSoleFname2History) { tikhomirov@518: it.remove(); tikhomirov@518: } tikhomirov@518: } tikhomirov@521: return fname2Follow; tikhomirov@518: } tikhomirov@518: tikhomirov@518: /** tikhomirov@518: * output identical to that of "hg log --follow" tikhomirov@518: */ tikhomirov@518: @Test tikhomirov@518: public void testFollowBothRenameAndAncestry() throws Exception { tikhomirov@518: repo = Configuration.get().find("log-follow"); tikhomirov@518: final String fname1 = "file1_a"; tikhomirov@518: final String fname2 = "file1_b"; tikhomirov@518: assertTrue("[sanity]", repo.getFileNode(fname2).exists()); tikhomirov@518: eh.run("hg", "log", "--debug", "--follow", fname2, "--cwd", repo.getLocation()); tikhomirov@518: tikhomirov@518: CollectWithRenameHandler h = new CollectWithRenameHandler(); tikhomirov@518: new HgLogCommand(repo).file(fname2, true, true).execute(h); tikhomirov@521: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: Pair rename = h.rh.renames.get(0); tikhomirov@518: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@518: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@518: // Ensure rename info came in the right moment tikhomirov@518: errorCollector.assertEquals(1, h.lastChangesetReportedAtRename.size()); tikhomirov@518: String fname1BranchRevision = "6e668ff2940acb250c8627843f8116166fe5d5cd"; tikhomirov@518: errorCollector.assertEquals(fname1BranchRevision, h.lastChangesetReportedAtRename.get(0).getNodeid().toString()); tikhomirov@518: // finally, match output tikhomirov@518: report("HgChangesetHandler(renames: true, ancestry:true)", h.getChanges(), true); tikhomirov@520: // tikhomirov@520: // Switch direction and compare, order shall match that from console tikhomirov@520: h = new CollectWithRenameHandler(); tikhomirov@522: new HgLogCommand(repo).file(fname2, true, true).order(NewToOld).execute(h); tikhomirov@520: // Identical rename event shall be reported tikhomirov@521: errorCollector.assertEquals(1, h.rh.renames.size()); tikhomirov@521: rename = h.rh.renames.get(0); tikhomirov@520: errorCollector.assertEquals(fname1, rename.first().getPath().toString()); tikhomirov@520: errorCollector.assertEquals(fname2, rename.second().getPath().toString()); tikhomirov@520: // new to old, recently reported would be the very first revision fname2 pops up tikhomirov@520: String firstRevOfFname2 = "27e7a69373b74d42e75f3211e56510ff17d01370"; tikhomirov@520: errorCollector.assertEquals(firstRevOfFname2, h.lastChangesetReportedAtRename.get(0).getNodeid().toString()); tikhomirov@520: report("HgChangesetHandler(renames: true, ancestry:true)", h.getChanges(), false /*do not reorder console results !!!*/); tikhomirov@520: // tikhomirov@518: // TreeChangeHandler in #testChangesetTreeFollowRenameAndAncestry tikhomirov@518: } tikhomirov@521: tikhomirov@521: @Test tikhomirov@521: public void testChangesetTreeFollowRenameAndAncestry() throws Exception { tikhomirov@521: repo = Configuration.get().find("log-follow"); tikhomirov@521: final String fname = "file1_b"; tikhomirov@521: assertTrue("[sanity]", repo.getFileNode(fname).exists()); tikhomirov@521: eh.run("hg", "log", "--debug", "--follow", fname, "--cwd", repo.getLocation()); tikhomirov@521: tikhomirov@521: TreeCollectHandler h = new TreeCollectHandler(true); tikhomirov@521: RenameCollector rh = new RenameCollector(h); tikhomirov@521: h.checkPrevInParents = true; tikhomirov@521: new HgLogCommand(repo).file(fname, true, true).execute(h); tikhomirov@521: tikhomirov@521: assertEquals(1, h.getAdapterUse(HgFileRenameHandlerMixin.class)); tikhomirov@521: tikhomirov@521: report("execute with HgChangesetTreeHandler(follow == true)", h.getResult(), false); tikhomirov@521: tikhomirov@521: assertEquals(1, rh.renames.size()); tikhomirov@521: assertEquals(Path.create(fname), rh.renames.get(0).second().getPath()); tikhomirov@521: } tikhomirov@528: tikhomirov@528: /** tikhomirov@528: * Ensure {@link HgFileRenameHandlerMixin} is always notified, even tikhomirov@528: * if followRename is false. tikhomirov@528: * Shall check: tikhomirov@528: * both {@link HgLogCommand#execute(HgChangesetHandler)} and {@link HgLogCommand#execute(HgChangesetTreeHandler)} tikhomirov@528: * and for both iteration directions in each case tikhomirov@528: */ tikhomirov@528: @Test tikhomirov@528: public void testRenameHandlerNotifiedEvenIfNotFollowRename() throws Exception { tikhomirov@528: repo = Configuration.get().find("log-follow"); tikhomirov@528: final String fname1 = "file1_a"; tikhomirov@528: final String fname2 = "file1_b"; tikhomirov@528: final String fnameNoRename = "file2"; tikhomirov@528: assertTrue("[sanity]", repo.getFileNode(fnameNoRename).exists()); tikhomirov@528: tikhomirov@528: // first, check that file without renames doesn't report any accidentally tikhomirov@528: CollectWithRenameHandler h1 = new CollectWithRenameHandler(); tikhomirov@528: HgLogCommand cmd = new HgLogCommand(repo).file(fnameNoRename, false, false); tikhomirov@528: cmd.execute(h1); tikhomirov@528: errorCollector.assertEquals(0, h1.rh.renames.size()); tikhomirov@528: TreeCollectHandler h2 = new TreeCollectHandler(false); tikhomirov@528: RenameCollector rh = new RenameCollector(h2); tikhomirov@528: cmd.execute(h2); tikhomirov@528: errorCollector.assertEquals(0, rh.renames.size()); tikhomirov@528: tikhomirov@528: // check default iterate direction tikhomirov@528: cmd = new HgLogCommand(repo).file(fname2, false, false); tikhomirov@528: cmd.execute(h1 = new CollectWithRenameHandler()); tikhomirov@528: errorCollector.assertEquals(1, h1.rh.renames.size()); tikhomirov@528: assertRename(fname1, fname2, h1.rh.renames.get(0)); tikhomirov@528: tikhomirov@528: h2 = new TreeCollectHandler(false); tikhomirov@528: rh = new RenameCollector(h2); tikhomirov@528: cmd.execute(h2); tikhomirov@528: errorCollector.assertEquals(1, rh.renames.size()); tikhomirov@528: assertRename(fname1, fname2, rh.renames.get(0)); tikhomirov@528: tikhomirov@528: eh.run("hg", "log", "--debug", fname2, "--cwd", repo.getLocation()); tikhomirov@528: report("HgChangesetHandler+RenameHandler with followRenames = false, default iteration order", h1.getChanges(), true); tikhomirov@528: report("HgChangesetTreeHandler+RenameHandler with followRenames = false, default iteration order", h2.getResult(), true); tikhomirov@528: tikhomirov@528: // tikhomirov@528: // Now, check that iteration in opposite direction (new to old) tikhomirov@528: // still reports renames (and correct revisions, too) tikhomirov@528: cmd.order(HgIterateDirection.NewToOld); tikhomirov@528: cmd.execute(h1 = new CollectWithRenameHandler()); tikhomirov@528: errorCollector.assertEquals(1, h1.rh.renames.size()); tikhomirov@528: assertRename(fname1, fname2, h1.rh.renames.get(0)); tikhomirov@528: h2 = new TreeCollectHandler(false); tikhomirov@528: rh = new RenameCollector(h2); tikhomirov@528: cmd.execute(h2); tikhomirov@528: errorCollector.assertEquals(1, rh.renames.size()); tikhomirov@528: assertRename(fname1, fname2, rh.renames.get(0)); tikhomirov@528: report("HgChangesetHandler+RenameHandler with followRenames = false, new2old iteration order", h1.getChanges(), false); tikhomirov@528: report("HgChangesetTreeHandler+RenameHandler with followRenames = false, new2old iteration order", h2.getResult(), false); tikhomirov@528: } tikhomirov@528: tikhomirov@528: private void assertRename(String fnameFrom, String fnameTo, Pair rename) { tikhomirov@528: errorCollector.assertEquals(fnameFrom, rename.first().getPath().toString()); tikhomirov@528: errorCollector.assertEquals(fnameTo, rename.second().getPath().toString()); tikhomirov@528: } tikhomirov@70: tikhomirov@520: /** tikhomirov@520: * @see TestAuxUtilities#testChangelogCancelSupport() tikhomirov@520: */ tikhomirov@520: @Test tikhomirov@520: public void testLogCommandCancelSupport() throws Exception { tikhomirov@520: repo = Configuration.get().find("branches-1"); // any repo with more revisions tikhomirov@520: class BaseCancel extends TestAuxUtilities.CancelAtValue implements HgChangesetHandler { tikhomirov@520: BaseCancel(int limit) { tikhomirov@520: super(limit); tikhomirov@520: } tikhomirov@520: public void cset(HgChangeset changeset) throws HgCallbackTargetException { tikhomirov@520: nextValue(changeset.getRevisionIndex()); tikhomirov@520: } tikhomirov@520: }; tikhomirov@520: class ImplementsCancel extends BaseCancel implements CancelSupport { tikhomirov@520: ImplementsCancel(int limit) { tikhomirov@520: super(limit); tikhomirov@520: } tikhomirov@520: public void checkCancelled() throws CancelledException { tikhomirov@520: cancelImpl.checkCancelled(); tikhomirov@520: } tikhomirov@520: }; tikhomirov@520: class AdaptsToCancel extends BaseCancel implements Adaptable { tikhomirov@520: AdaptsToCancel(int limit) { tikhomirov@520: super(limit); tikhomirov@520: } tikhomirov@520: public T getAdapter(Class adapterClass) { tikhomirov@520: if (adapterClass == CancelSupport.class) { tikhomirov@520: return adapterClass.cast(cancelImpl); tikhomirov@520: } tikhomirov@520: return null; tikhomirov@520: } tikhomirov@520: } tikhomirov@520: tikhomirov@520: BaseCancel insp = new ImplementsCancel(3); tikhomirov@520: try { tikhomirov@520: new HgLogCommand(repo).execute(insp); tikhomirov@520: errorCollector.fail("CancelSupport as implemented iface"); tikhomirov@520: } catch (CancelledException ex) { tikhomirov@520: errorCollector.assertEquals("CancelSupport as implemented iface", insp.stopValue, insp.lastSeen); tikhomirov@520: } tikhomirov@520: insp = new AdaptsToCancel(5); tikhomirov@520: try { tikhomirov@520: new HgLogCommand(repo).execute(insp); tikhomirov@520: errorCollector.fail("Adaptable to CancelSupport"); tikhomirov@520: } catch (CancelledException ex) { tikhomirov@520: errorCollector.assertEquals("Adaptable to CancelSupport", insp.stopValue, insp.lastSeen); tikhomirov@520: } tikhomirov@520: insp = new BaseCancel(9); tikhomirov@520: try { tikhomirov@520: new HgLogCommand(repo).set(insp.cancelImpl).execute(insp); tikhomirov@520: errorCollector.fail("cmd#set(CancelSupport)"); tikhomirov@520: } catch (CancelledException e) { tikhomirov@520: errorCollector.assertEquals("cmd#set(CancelSupport)", insp.stopValue, insp.lastSeen); tikhomirov@520: } tikhomirov@520: } tikhomirov@520: tikhomirov@202: private void report(String what, List r, boolean reverseConsoleResult) { tikhomirov@70: final List consoleResult = changelogParser.getResult(); tikhomirov@202: report(what, r, consoleResult, reverseConsoleResult, errorCollector); tikhomirov@202: } tikhomirov@202: tikhomirov@202: static void report(String what, List hg4jResult, List consoleResult, boolean reverseConsoleResult, ErrorCollectorExt errorCollector) { tikhomirov@202: consoleResult = new ArrayList(consoleResult); // need a copy in case callee would use result again tikhomirov@202: if (reverseConsoleResult) { tikhomirov@82: Collections.reverse(consoleResult); tikhomirov@82: } tikhomirov@518: errorCollector.checkThat(what + ". Number of changeset reported didn't match", hg4jResult.size(), equalTo(consoleResult.size())); tikhomirov@147: Iterator consoleResultItr = consoleResult.iterator(); tikhomirov@202: for (HgChangeset cs : hg4jResult) { tikhomirov@202: if (!consoleResultItr.hasNext()) { tikhomirov@202: errorCollector.addError(new AssertionError("Ran out of console results while there are still hg4j results")); tikhomirov@202: break; tikhomirov@202: } tikhomirov@147: Record cr = consoleResultItr.next(); tikhomirov@520: // flags, not separate checkThat() because when lists are large, and do not match, tikhomirov@520: // number of failures may slow down test process significantly tikhomirov@423: int x = cs.getRevisionIndex() == cr.changesetIndex ? 0x1 : 0; tikhomirov@214: x |= cs.getDate().toString().equals(cr.date) ? 0x2 : 0; tikhomirov@70: x |= cs.getNodeid().toString().equals(cr.changesetNodeid) ? 0x4 : 0; tikhomirov@70: x |= cs.getUser().equals(cr.user) ? 0x8 : 0; tikhomirov@202: // need to do trim() on comment because command-line template does, and there are tikhomirov@202: // repositories that have couple of newlines in the end of the comment (e.g. hello sample repo from the book) tikhomirov@202: x |= cs.getComment().trim().equals(cr.description) ? 0x10 : 0; tikhomirov@423: errorCollector.checkThat(String.format(what + ". Mismatch (0x%x) in %d hg4j rev comparing to %d cmdline's.", x, cs.getRevisionIndex(), cr.changesetIndex), x, equalTo(0x1f)); tikhomirov@70: consoleResultItr.remove(); tikhomirov@70: } tikhomirov@202: errorCollector.checkThat(what + ". Unprocessed results in console left (insufficient from hg4j)", consoleResultItr.hasNext(), equalTo(false)); tikhomirov@70: } 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: changelogParser.reset(); tikhomirov@100: eh.run("hg", "log", "--debug"); tikhomirov@100: } tikhomirov@100: final long start2 = System.currentTimeMillis(); tikhomirov@100: for (int i = 0; i < runs; i++) { tikhomirov@131: new HgLogCommand(repo).execute(); tikhomirov@100: } tikhomirov@100: final long end = System.currentTimeMillis(); tikhomirov@100: System.out.printf("'hg log --debug', %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@147: tikhomirov@147: @Test tikhomirov@147: public void testOriginalTestLogRepo() throws Exception { tikhomirov@462: // tests fro mercurial distribution, test-log.t tikhomirov@147: repo = Configuration.get().find("log-1"); tikhomirov@147: HgLogCommand cmd = new HgLogCommand(repo); tikhomirov@147: // funny enough, but hg log -vf a -R c:\temp\hg\test-log\a doesn't work, while --cwd works fine tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "a", "--cwd", repo.getLocation()); tikhomirov@147: report("log a", cmd.file("a", false).execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@462: // fails with Mercurial 2.2.1, @see http://selenic.com/pipermail/mercurial-devel/2012-February/038249.html tikhomirov@462: // and http://www.selenic.com/hg/rev/60101427d618?rev= tikhomirov@468: // fix for the test (replacement) is available below tikhomirov@468: // eh.run("hg", "log", "--debug", "-f", "a", "--cwd", repo.getLocation()); tikhomirov@468: // List r = cmd.file("a", true).execute(); tikhomirov@468: // report("log -f a", r, true); tikhomirov@468: tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-f", "e", "--cwd", repo.getLocation()); tikhomirov@518: report("log -f e", cmd.file("e", true).execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "dir/b", "--cwd", repo.getLocation()); tikhomirov@147: report("log dir/b", cmd.file("dir/b", false).execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@462: // tikhomirov@462: // Commented out for the same reason as above hg log -f a - newly introduced error message in Mercurial 2.2 tikhomirov@462: // when files are not part of the parent revision tikhomirov@462: // eh.run("hg", "log", "--debug", "-f", "dir/b", "--cwd", repo.getLocation()); tikhomirov@462: // report("log -f dir/b", cmd.file("dir/b", true).execute(), false /*#1, below*/); tikhomirov@147: /* tikhomirov@147: * #1: false works because presently commands dispatches history of the queried file, and then history tikhomirov@147: * of it's origin. With history comprising of renames only, this effectively gives reversed (newest to oldest) tikhomirov@147: * order of revisions. tikhomirov@147: */ tikhomirov@462: tikhomirov@462: // commented tests from above updated to work in 2.2 - update repo to revision where files are present tikhomirov@462: eh.run("hg", "update", "-q", "-r", "2", "--cwd", repo.getLocation()); tikhomirov@462: changelogParser.reset(); tikhomirov@462: eh.run("hg", "log", "--debug", "-f", "a", "--cwd", repo.getLocation()); tikhomirov@462: List r = cmd.file("a", true).execute(); tikhomirov@462: report("log -f a", r, true); tikhomirov@462: changelogParser.reset(); tikhomirov@462: eh.run("hg", "log", "--debug", "-f", "dir/b", "--cwd", repo.getLocation()); tikhomirov@518: report("log -f dir/b", cmd.file("dir/b", true).execute(), true); tikhomirov@462: // tikhomirov@462: // get repo back into clear state, up to the tip tikhomirov@462: eh.run("hg", "update", "-q", "--cwd", repo.getLocation()); tikhomirov@147: } tikhomirov@147: tikhomirov@147: @Test tikhomirov@147: public void testUsernames() throws Exception { tikhomirov@147: repo = Configuration.get().find("log-users"); tikhomirov@147: final String user1 = "User One "; tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-u", user1, "--cwd", repo.getLocation()); tikhomirov@147: report("log -u " + user1, new HgLogCommand(repo).user(user1).execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-u", "user1", "-u", "user2", "--cwd", repo.getLocation()); tikhomirov@147: report("log -u user1 -u user2", new HgLogCommand(repo).user("user1").user("user2").execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-u", "user3", "--cwd", repo.getLocation()); tikhomirov@147: report("log -u user3", new HgLogCommand(repo).user("user3").execute(), true); tikhomirov@147: } tikhomirov@147: tikhomirov@147: @Test tikhomirov@147: public void testBranches() throws Exception { tikhomirov@147: repo = Configuration.get().find("log-branches"); tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-b", "default", "--cwd", repo.getLocation()); tikhomirov@147: report("log -b default" , new HgLogCommand(repo).branch("default").execute(), true); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-b", "test", "--cwd", repo.getLocation()); tikhomirov@147: report("log -b test" , new HgLogCommand(repo).branch("test").execute(), true); tikhomirov@147: // tikhomirov@147: assertTrue("log -b dummy shall yeild empty result", new HgLogCommand(repo).branch("dummy").execute().isEmpty()); tikhomirov@147: // tikhomirov@147: changelogParser.reset(); tikhomirov@147: eh.run("hg", "log", "--debug", "-b", "default", "-b", "test", "--cwd", repo.getLocation()); tikhomirov@147: report("log -b default -b test" , new HgLogCommand(repo).branch("default").branch("test").execute(), true); tikhomirov@147: } tikhomirov@508: tikhomirov@508: //// tikhomirov@508: tikhomirov@514: private final class TreeCollectHandler extends AdapterPlug implements HgChangesetTreeHandler { tikhomirov@508: private final LinkedList cmdResult = new LinkedList(); tikhomirov@508: private final boolean reverseResult; tikhomirov@521: boolean checkPrevInChildren = false; // true when iterating new to old tikhomirov@521: boolean checkPrevInParents = false; // true when iterating old to new tikhomirov@508: tikhomirov@508: public TreeCollectHandler(boolean _reverseResult) { tikhomirov@508: this.reverseResult = _reverseResult; tikhomirov@508: } tikhomirov@508: tikhomirov@508: public List getResult() { tikhomirov@508: return cmdResult; tikhomirov@508: } tikhomirov@509: tikhomirov@508: tikhomirov@628: public void treeElement(TreeElement entry) throws HgCallbackTargetException, HgRuntimeException { tikhomirov@508: // check consistency tikhomirov@508: Nodeid cset = entry.changeset().getNodeid(); tikhomirov@508: errorCollector.assertEquals(entry.changesetRevision(), cset); tikhomirov@509: Pair p = entry.parents(); tikhomirov@509: Pair parents_a = p; tikhomirov@508: Pair parents_b = entry.parentRevisions(); tikhomirov@508: if (parents_b.first().isNull()) { tikhomirov@508: errorCollector.assertTrue(parents_a.first() == null); tikhomirov@508: } else { tikhomirov@508: errorCollector.assertEquals(parents_b.first(), parents_a.first().getNodeid()); tikhomirov@508: } tikhomirov@508: if (parents_b.second().isNull()) { tikhomirov@508: errorCollector.assertTrue(parents_a.second() == null); tikhomirov@508: } else { tikhomirov@508: errorCollector.assertEquals(parents_b.second(), parents_a.second().getNodeid()); tikhomirov@508: } tikhomirov@508: // tikhomirov@509: if (checkPrevInChildren && !cmdResult.isEmpty()) { tikhomirov@509: HgChangeset prevChangeset = reverseResult ? cmdResult.getFirst() : cmdResult.getLast(); tikhomirov@517: String msg = String.format("No parent-child bind between revisions %d and %d", prevChangeset.getRevisionIndex(), entry.changeset().getRevisionIndex()); tikhomirov@509: errorCollector.assertTrue(msg, entry.children().contains(prevChangeset)); tikhomirov@509: } tikhomirov@509: if (checkPrevInParents && !cmdResult.isEmpty()) { tikhomirov@509: HgChangeset prevChangeset = reverseResult ? cmdResult.getFirst() : cmdResult.getLast(); tikhomirov@517: String msg = String.format("No parent-child bind between revisions %d and %d", prevChangeset.getRevisionIndex(), entry.changeset().getRevisionIndex()); tikhomirov@509: errorCollector.assertTrue(msg, p.first() == prevChangeset || p.second() == prevChangeset); tikhomirov@509: } tikhomirov@509: // tikhomirov@508: if (reverseResult) { tikhomirov@508: cmdResult.addFirst(entry.changeset()); tikhomirov@508: } else { tikhomirov@508: cmdResult.addLast(entry.changeset()); tikhomirov@508: } tikhomirov@508: } tikhomirov@508: } tikhomirov@518: tikhomirov@518: private static class CollectWithRenameHandler extends CollectHandler implements HgChangesetHandler.WithCopyHistory { tikhomirov@521: public final RenameCollector rh = new RenameCollector(); tikhomirov@521: public List lastChangesetReportedAtRename = new LinkedList(); tikhomirov@521: tikhomirov@521: public void copy(HgFileRevision from, HgFileRevision to) throws HgCallbackTargetException { tikhomirov@521: Assert.assertTrue("Renames couldn't be reported prior to any change", getChanges().size() > 0); tikhomirov@521: HgChangeset lastKnown = getChanges().get(getChanges().size() - 1); tikhomirov@521: lastChangesetReportedAtRename.add(lastKnown); tikhomirov@521: rh.copy(from, to); tikhomirov@521: } tikhomirov@521: }; tikhomirov@521: tikhomirov@521: private static class RenameCollector implements HgFileRenameHandlerMixin { tikhomirov@518: public boolean copyReported = false; tikhomirov@518: public List> renames = new LinkedList>(); tikhomirov@521: tikhomirov@521: public RenameCollector() { tikhomirov@521: } tikhomirov@521: tikhomirov@521: public RenameCollector(AdapterPlug ap) { tikhomirov@521: ap.attachAdapter(HgFileRenameHandlerMixin.class, this); tikhomirov@521: } tikhomirov@528: tikhomirov@518: public void copy(HgFileRevision from, HgFileRevision to) { tikhomirov@518: copyReported = true; tikhomirov@518: renames.add(new Pair(from, to)); tikhomirov@518: } tikhomirov@521: } tikhomirov@70: }