kitaev@213: /* kitaev@213: * Copyright (c) 2011 TMate Software Ltd kitaev@213: * kitaev@213: * This program is free software; you can redistribute it and/or modify kitaev@213: * it under the terms of the GNU General Public License as published by kitaev@213: * the Free Software Foundation; version 2 of the License. kitaev@213: * kitaev@213: * This program is distributed in the hope that it will be useful, kitaev@213: * but WITHOUT ANY WARRANTY; without even the implied warranty of kitaev@213: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the kitaev@213: * GNU General Public License for more details. kitaev@213: * kitaev@213: * For information on how to redistribute this software under kitaev@213: * the terms of a license other than GNU General Public License kitaev@213: * contact TMate Software at support@hg4j.com kitaev@213: */ kitaev@213: package org.tmatesoft.hg.test; kitaev@213: kitaev@213: import static org.hamcrest.CoreMatchers.equalTo; kitaev@213: import static org.tmatesoft.hg.core.HgStatus.*; kitaev@213: import static org.tmatesoft.hg.core.HgStatus.Kind.*; kitaev@213: import static org.tmatesoft.hg.repo.HgRepository.TIP; kitaev@213: kitaev@213: import java.util.Collection; kitaev@213: import java.util.Collections; kitaev@213: import java.util.HashMap; kitaev@213: import java.util.LinkedList; kitaev@213: import java.util.List; kitaev@213: import java.util.Map; kitaev@213: import java.util.TreeMap; kitaev@213: kitaev@213: import org.junit.Assume; kitaev@213: import org.junit.Rule; kitaev@213: import org.junit.Test; kitaev@213: import org.tmatesoft.hg.core.HgStatus; kitaev@213: import org.tmatesoft.hg.core.HgStatusCommand; kitaev@213: import org.tmatesoft.hg.repo.HgLookup; kitaev@213: import org.tmatesoft.hg.repo.HgRepository; kitaev@213: import org.tmatesoft.hg.repo.HgStatusCollector; kitaev@213: import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; kitaev@213: import org.tmatesoft.hg.util.Path; kitaev@213: kitaev@213: kitaev@213: /** kitaev@213: * kitaev@213: * @author Artem Tikhomirov kitaev@213: * @author TMate Software Ltd. kitaev@213: */ kitaev@213: public class TestStatus { kitaev@213: kitaev@213: @Rule kitaev@213: public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); kitaev@213: kitaev@213: private final HgRepository repo; kitaev@213: private StatusOutputParser statusParser; kitaev@213: private ExecHelper eh; kitaev@213: kitaev@213: public static void main(String[] args) throws Throwable { kitaev@213: TestStatus test = new TestStatus(); kitaev@213: test.testLowLevel(); kitaev@213: test.testStatusCommand(); kitaev@213: test.testPerformance(); kitaev@213: test.errorCollector.verify(); kitaev@213: } kitaev@213: kitaev@213: public TestStatus() throws Exception { kitaev@213: this(new HgLookup().detectFromWorkingDir()); kitaev@213: } kitaev@213: kitaev@213: private TestStatus(HgRepository hgRepo) { kitaev@213: repo = hgRepo; kitaev@213: Assume.assumeTrue(!repo.isInvalid()); kitaev@213: statusParser = new StatusOutputParser(); kitaev@213: eh = new ExecHelper(statusParser, null); kitaev@213: } kitaev@213: kitaev@213: @Test kitaev@213: public void testLowLevel() throws Exception { kitaev@213: final HgWorkingCopyStatusCollector wcc = new HgWorkingCopyStatusCollector(repo); kitaev@213: statusParser.reset(); kitaev@213: eh.run("hg", "status", "-A"); kitaev@213: HgStatusCollector.Record r = wcc.status(HgRepository.TIP); kitaev@213: report("hg status -A", r, statusParser); kitaev@213: // kitaev@213: statusParser.reset(); kitaev@213: int revision = 3; kitaev@213: eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); kitaev@213: r = wcc.status(revision); kitaev@213: report("status -A --rev " + revision, r, statusParser); kitaev@213: // kitaev@213: statusParser.reset(); kitaev@213: eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); kitaev@213: r = new HgStatusCollector.Record(); kitaev@213: new HgStatusCollector(repo).change(revision, r); kitaev@213: report("status -A --change " + revision, r, statusParser); kitaev@213: // kitaev@213: statusParser.reset(); kitaev@213: int rev2 = 80; kitaev@213: final String range = String.valueOf(revision) + ":" + String.valueOf(rev2); kitaev@213: eh.run("hg", "status", "-A", "--rev", range); kitaev@213: r = new HgStatusCollector(repo).status(revision, rev2); kitaev@213: report("Status -A -rev " + range, r, statusParser); kitaev@213: } kitaev@213: kitaev@213: @Test kitaev@213: public void testStatusCommand() throws Exception { kitaev@213: final HgStatusCommand sc = new HgStatusCommand(repo).all(); kitaev@213: StatusCollector r; kitaev@213: statusParser.reset(); kitaev@213: eh.run("hg", "status", "-A"); kitaev@213: sc.execute(r = new StatusCollector()); kitaev@213: report("hg status -A", r); kitaev@213: // kitaev@213: statusParser.reset(); kitaev@213: int revision = 3; kitaev@213: eh.run("hg", "status", "-A", "--rev", String.valueOf(revision)); kitaev@213: sc.base(revision).execute(r = new StatusCollector()); kitaev@213: report("status -A --rev " + revision, r); kitaev@213: // kitaev@213: statusParser.reset(); kitaev@213: eh.run("hg", "status", "-A", "--change", String.valueOf(revision)); kitaev@213: sc.base(TIP).revision(revision).execute(r = new StatusCollector()); kitaev@213: report("status -A --change " + revision, r); kitaev@213: kitaev@213: // TODO check not -A, but defaults()/custom set of modifications kitaev@213: } kitaev@213: kitaev@213: private static class StatusCollector implements HgStatusCommand.Handler { kitaev@213: private final Map> map = new TreeMap>(); kitaev@213: kitaev@213: public void handleStatus(HgStatus s) { kitaev@213: List l = map.get(s.getKind()); kitaev@213: if (l == null) { kitaev@213: l = new LinkedList(); kitaev@213: map.put(s.getKind(), l); kitaev@213: } kitaev@213: l.add(s.getPath()); kitaev@213: } kitaev@213: kitaev@213: public List get(Kind k) { kitaev@213: List rv = map.get(k); kitaev@213: if (rv == null) { kitaev@213: return Collections.emptyList(); kitaev@213: } kitaev@213: return rv; kitaev@213: } kitaev@213: } kitaev@213: kitaev@213: public void testRemovedAgainstNonTip() { kitaev@213: /* kitaev@213: status --rev N when a file added past revision N was removed ((both physically and in dirstate), but not yet committed kitaev@213: kitaev@213: Reports extra REMOVED file (the one added and removed in between). Shall not kitaev@213: */ kitaev@213: } kitaev@213: kitaev@213: /* kitaev@213: * With warm-up of previous tests, 10 runs, time in milliseconds kitaev@213: * 'hg status -A': Native client total 953 (95 per run), Java client 94 (9) kitaev@213: * 'hg status -A --rev 3:80': Native client total 1828 (182 per run), Java client 235 (23) kitaev@213: * 'hg log --debug', 10 runs: Native client total 1766 (176 per run), Java client 78 (7) kitaev@213: * kitaev@213: * 18.02.2011 kitaev@213: * 'hg status -A --rev 3:80', 10 runs: Native client total 2000 (200 per run), Java client 250 (25) kitaev@213: * 'hg log --debug', 10 runs: Native client total 2297 (229 per run), Java client 125 (12) kitaev@213: * kitaev@213: * 9.3.2011 (DataAccess instead of byte[] in ReflogStream.Inspector kitaev@213: * 'hg status -A', 10 runs: Native client total 1516 (151 per run), Java client 219 (21) kitaev@213: * 'hg status -A --rev 3:80', 10 runs: Native client total 1875 (187 per run), Java client 3187 (318) (!!! ???) kitaev@213: * 'hg log --debug', 10 runs: Native client total 2484 (248 per run), Java client 344 (34) kitaev@213: */ kitaev@213: public void testPerformance() throws Exception { kitaev@213: final int runs = 10; kitaev@213: final long start1 = System.currentTimeMillis(); kitaev@213: for (int i = 0; i < runs; i++) { kitaev@213: statusParser.reset(); kitaev@213: eh.run("hg", "status", "-A", "--rev", "3:80"); kitaev@213: } kitaev@213: final long start2 = System.currentTimeMillis(); kitaev@213: for (int i = 0; i < runs; i++) { kitaev@213: StatusCollector r = new StatusCollector(); kitaev@213: new HgStatusCommand(repo).all().base(3).revision(80).execute(r); kitaev@213: } kitaev@213: final long end = System.currentTimeMillis(); kitaev@213: 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); kitaev@213: } kitaev@213: kitaev@213: private void report(String what, StatusCollector r) { kitaev@213: reportNotEqual(what + "#MODIFIED", r.get(Modified), statusParser.getModified()); kitaev@213: reportNotEqual(what + "#ADDED", r.get(Added), statusParser.getAdded()); kitaev@213: reportNotEqual(what + "#REMOVED", r.get(Removed), statusParser.getRemoved()); kitaev@213: reportNotEqual(what + "#CLEAN", r.get(Clean), statusParser.getClean()); kitaev@213: reportNotEqual(what + "#IGNORED", r.get(Ignored), statusParser.getIgnored()); kitaev@213: reportNotEqual(what + "#MISSING", r.get(Missing), statusParser.getMissing()); kitaev@213: reportNotEqual(what + "#UNKNOWN", r.get(Unknown), statusParser.getUnknown()); kitaev@213: // FIXME test copies kitaev@213: } kitaev@213: kitaev@213: private void report(String what, HgStatusCollector.Record r, StatusOutputParser statusParser) { kitaev@213: reportNotEqual(what + "#MODIFIED", r.getModified(), statusParser.getModified()); kitaev@213: reportNotEqual(what + "#ADDED", r.getAdded(), statusParser.getAdded()); kitaev@213: reportNotEqual(what + "#REMOVED", r.getRemoved(), statusParser.getRemoved()); kitaev@213: reportNotEqual(what + "#CLEAN", r.getClean(), statusParser.getClean()); kitaev@213: reportNotEqual(what + "#IGNORED", r.getIgnored(), statusParser.getIgnored()); kitaev@213: reportNotEqual(what + "#MISSING", r.getMissing(), statusParser.getMissing()); kitaev@213: reportNotEqual(what + "#UNKNOWN", r.getUnknown(), statusParser.getUnknown()); kitaev@213: List copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); kitaev@213: HashMap copyDiff = new HashMap(); kitaev@213: if (copiedKeyDiff.isEmpty()) { kitaev@213: for (Path jk : r.getCopied().keySet()) { kitaev@213: Path jv = r.getCopied().get(jk); kitaev@213: if (statusParser.getCopied().containsKey(jk)) { kitaev@213: Path cmdv = statusParser.getCopied().get(jk); kitaev@213: if (!jv.equals(cmdv)) { kitaev@213: copyDiff.put(jk, jv + " instead of " + cmdv); kitaev@213: } kitaev@213: } else { kitaev@213: copyDiff.put(jk, "ERRONEOUSLY REPORTED IN JAVA"); kitaev@213: } kitaev@213: } kitaev@213: } kitaev@213: errorCollector.checkThat(what + "#Non-matching 'copied' keys: ", copiedKeyDiff, equalTo(Collections.emptyList())); kitaev@213: errorCollector.checkThat(what + "#COPIED", copyDiff, equalTo(Collections.emptyMap())); kitaev@213: } kitaev@213: kitaev@213: private void reportNotEqual(String what, Collection l1, Collection l2) { kitaev@213: List diff = difference(l1, l2); kitaev@213: errorCollector.checkThat(what, diff, equalTo(Collections.emptyList())); kitaev@213: } kitaev@213: kitaev@213: private static List difference(Collection l1, Collection l2) { kitaev@213: LinkedList result = new LinkedList(l2); kitaev@213: for (T t : l1) { kitaev@213: if (l2.contains(t)) { kitaev@213: result.remove(t); kitaev@213: } else { kitaev@213: result.add(t); kitaev@213: } kitaev@213: } kitaev@213: return result; kitaev@213: } kitaev@213: }