tikhomirov@534: /* tikhomirov@534: * Copyright (c) 2013 TMate Software Ltd tikhomirov@534: * tikhomirov@534: * This program is free software; you can redistribute it and/or modify tikhomirov@534: * it under the terms of the GNU General Public License as published by tikhomirov@534: * the Free Software Foundation; version 2 of the License. tikhomirov@534: * tikhomirov@534: * This program is distributed in the hope that it will be useful, tikhomirov@534: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@534: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@534: * GNU General Public License for more details. tikhomirov@534: * tikhomirov@534: * For information on how to redistribute this software under tikhomirov@534: * the terms of a license other than GNU General Public License tikhomirov@534: * contact TMate Software at support@hg4j.com tikhomirov@534: */ tikhomirov@534: package org.tmatesoft.hg.test; tikhomirov@534: tikhomirov@559: import static org.junit.Assert.*; tikhomirov@559: import static org.tmatesoft.hg.repo.HgRepository.*; tikhomirov@539: tikhomirov@538: import java.io.File; tikhomirov@559: import java.io.IOException; tikhomirov@538: import java.nio.ByteBuffer; tikhomirov@559: import java.util.List; tikhomirov@538: tikhomirov@588: import org.junit.Rule; tikhomirov@534: import org.junit.Test; tikhomirov@559: import org.tmatesoft.hg.core.HgAddRemoveCommand; tikhomirov@559: import org.tmatesoft.hg.core.HgCatCommand; tikhomirov@559: import org.tmatesoft.hg.core.HgChangeset; tikhomirov@586: import org.tmatesoft.hg.core.HgCommitCommand; tikhomirov@559: import org.tmatesoft.hg.core.HgLogCommand; tikhomirov@586: import org.tmatesoft.hg.core.HgStatus.Kind; tikhomirov@586: import org.tmatesoft.hg.core.HgStatusCommand; tikhomirov@559: import org.tmatesoft.hg.core.Nodeid; tikhomirov@559: import org.tmatesoft.hg.internal.ByteArrayChannel; tikhomirov@591: import org.tmatesoft.hg.internal.CommitFacility; tikhomirov@586: import org.tmatesoft.hg.internal.FileContentSupplier; tikhomirov@591: import org.tmatesoft.hg.internal.Internals; tikhomirov@559: import org.tmatesoft.hg.repo.HgDataFile; tikhomirov@538: import org.tmatesoft.hg.repo.HgLookup; tikhomirov@538: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@586: import org.tmatesoft.hg.util.Outcome; tikhomirov@559: import org.tmatesoft.hg.util.Path; tikhomirov@534: tikhomirov@534: /** tikhomirov@559: * Handy for debug to see patch content: tikhomirov@559: * ...RevlogDump /tmp/test-commit2non-empty/.hg/ store/data/file1.i dumpData tikhomirov@534: * tikhomirov@534: * @author Artem Tikhomirov tikhomirov@534: * @author TMate Software Ltd. tikhomirov@534: */ tikhomirov@534: public class TestCommit { tikhomirov@534: tikhomirov@588: @Rule tikhomirov@588: public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); tikhomirov@588: tikhomirov@534: @Test tikhomirov@538: public void testCommitToNonEmpty() throws Exception { tikhomirov@538: File repoLoc = RepoUtils.initEmptyTempRepo("test-commit2non-empty"); tikhomirov@559: RepoUtils.createFile(new File(repoLoc, "file1"), "hello\n"); tikhomirov@559: new ExecHelper(new OutputParser.Stub(), repoLoc).run("hg", "commit", "--addremove", "-m", "FIRST"); tikhomirov@538: // tikhomirov@538: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@591: CommitFacility cf = new CommitFacility(Internals.getInstance(hgRepo), 0); tikhomirov@559: // FIXME test diff for processing changed newlines (ie \r\n -> \n or vice verse) - if a whole line or tikhomirov@559: // just changed endings are in the patch! tikhomirov@559: HgDataFile df = hgRepo.getFileNode("file1"); tikhomirov@559: cf.add(df, new ByteArraySupplier("hello\nworld".getBytes())); tikhomirov@559: Nodeid secondRev = cf.commit("SECOND"); tikhomirov@559: // tikhomirov@559: List commits = new HgLogCommand(hgRepo).execute(); tikhomirov@588: errorCollector.assertEquals(2, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@559: HgChangeset c2 = commits.get(1); tikhomirov@588: errorCollector.assertEquals("FIRST", c1.getComment()); tikhomirov@588: errorCollector.assertEquals("SECOND", c2.getComment()); tikhomirov@588: errorCollector.assertEquals(df.getPath(), c2.getAffectedFiles().get(0)); tikhomirov@588: errorCollector.assertEquals(c1.getNodeid(), c2.getFirstParentRevision()); tikhomirov@588: errorCollector.assertEquals(Nodeid.NULL, c2.getSecondParentRevision()); tikhomirov@588: errorCollector.assertEquals(secondRev, c2.getNodeid()); tikhomirov@539: } tikhomirov@539: tikhomirov@539: @Test tikhomirov@539: public void testCommitToEmpty() throws Exception { tikhomirov@539: File repoLoc = RepoUtils.initEmptyTempRepo("test-commit2empty"); tikhomirov@559: String fname = "file1"; tikhomirov@559: RepoUtils.createFile(new File(repoLoc, fname), null); tikhomirov@559: new ExecHelper(new OutputParser.Stub(), repoLoc).run("hg", "add", fname); tikhomirov@539: // tikhomirov@539: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@559: assertEquals("[sanity]", 0, new HgLogCommand(hgRepo).execute().size()); tikhomirov@591: CommitFacility cf = new CommitFacility(Internals.getInstance(hgRepo), NO_REVISION); tikhomirov@559: HgDataFile df = hgRepo.getFileNode(fname); tikhomirov@559: final byte[] initialContent = "hello\nworld".getBytes(); tikhomirov@559: cf.add(df, new ByteArraySupplier(initialContent)); tikhomirov@559: String comment = "commit 1"; tikhomirov@559: Nodeid c1Rev = cf.commit(comment); tikhomirov@559: List commits = new HgLogCommand(hgRepo).execute(); tikhomirov@588: errorCollector.assertEquals(1, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@588: errorCollector.assertEquals(1, c1.getAffectedFiles().size()); tikhomirov@588: errorCollector.assertEquals(df.getPath(), c1.getAffectedFiles().get(0)); tikhomirov@588: errorCollector.assertEquals(0, c1.getRevisionIndex()); tikhomirov@588: errorCollector.assertEquals(Nodeid.NULL, c1.getFirstParentRevision()); tikhomirov@588: errorCollector.assertEquals(Nodeid.NULL, c1.getSecondParentRevision()); tikhomirov@588: errorCollector.assertEquals(HgRepository.DEFAULT_BRANCH_NAME, c1.getBranch()); tikhomirov@588: errorCollector.assertEquals(comment, c1.getComment()); tikhomirov@588: errorCollector.assertEquals(c1Rev, c1.getNodeid()); tikhomirov@559: ByteArrayChannel bac = new ByteArrayChannel(); tikhomirov@559: new HgCatCommand(hgRepo).file(df.getPath()).execute(bac); tikhomirov@559: assertArrayEquals(initialContent, bac.toArray()); tikhomirov@559: } tikhomirov@559: tikhomirov@559: @Test tikhomirov@559: public void testCommitIntoBranch() throws Exception { tikhomirov@559: File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-add-remove-commit", false); tikhomirov@559: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@559: HgDataFile dfD = hgRepo.getFileNode("d"); tikhomirov@559: assertTrue("[sanity]", dfD.exists()); tikhomirov@559: File fileD = new File(repoLoc, "d"); tikhomirov@559: assertTrue("[sanity]", fileD.canRead()); tikhomirov@559: final int parentCsetRevIndex = hgRepo.getChangelog().getLastRevision(); tikhomirov@559: HgChangeset parentCset = new HgLogCommand(hgRepo).range(parentCsetRevIndex, parentCsetRevIndex).execute().get(0); tikhomirov@559: assertEquals("[sanity]", DEFAULT_BRANCH_NAME, parentCset.getBranch()); tikhomirov@559: // tikhomirov@559: RepoUtils.modifyFileAppend(fileD, "A CHANGE\n"); tikhomirov@591: CommitFacility cf = new CommitFacility(Internals.getInstance(hgRepo), parentCsetRevIndex); tikhomirov@559: FileContentSupplier contentProvider = new FileContentSupplier(fileD); tikhomirov@559: cf.add(dfD, contentProvider); tikhomirov@559: cf.branch("branch1"); tikhomirov@559: Nodeid commitRev1 = cf.commit("FIRST"); tikhomirov@559: contentProvider.done(); tikhomirov@559: // tikhomirov@559: List commits = new HgLogCommand(hgRepo).range(parentCsetRevIndex+1, TIP).execute(); tikhomirov@559: assertEquals(1, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@588: errorCollector.assertEquals(c1.getNodeid(), commitRev1); tikhomirov@588: errorCollector.assertEquals("branch1", c1.getBranch()); tikhomirov@588: errorCollector.assertEquals("FIRST", c1.getComment()); tikhomirov@559: // tikhomirov@559: assertHgVerifyOk(repoLoc); tikhomirov@559: } tikhomirov@559: tikhomirov@559: /** tikhomirov@559: * use own add and remove commands and then commit tikhomirov@559: */ tikhomirov@559: @Test tikhomirov@559: public void testCommitWithAddRemove() throws Exception { tikhomirov@559: File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-add-remove-commit", false); tikhomirov@559: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@559: assertTrue("[sanity]", hgRepo.getFileNode("d").exists()); tikhomirov@559: assertTrue("[sanity]", new File(repoLoc, "d").canRead()); tikhomirov@559: RepoUtils.createFile(new File(repoLoc, "xx"), "xyz"); tikhomirov@559: new HgAddRemoveCommand(hgRepo).add(Path.create("xx")).remove(Path.create("d")).execute(); tikhomirov@591: CommitFacility cf = new CommitFacility(Internals.getInstance(hgRepo), hgRepo.getChangelog().getLastRevision()); tikhomirov@559: FileContentSupplier contentProvider = new FileContentSupplier(new File(repoLoc, "xx")); tikhomirov@559: cf.add(hgRepo.getFileNode("xx"), contentProvider); tikhomirov@559: cf.forget(hgRepo.getFileNode("d")); tikhomirov@559: Nodeid commitRev = cf.commit("Commit with add/remove cmd"); tikhomirov@559: contentProvider.done(); tikhomirov@559: // Note, working directory still points to original revision, CommitFacility doesn't update dirstate tikhomirov@559: // tikhomirov@559: List commits = new HgLogCommand(hgRepo).changeset(commitRev).execute(); tikhomirov@559: HgChangeset cmt = commits.get(0); tikhomirov@588: errorCollector.assertEquals(1, cmt.getAddedFiles().size()); tikhomirov@588: errorCollector.assertEquals("xx", cmt.getAddedFiles().get(0).getPath().toString()); tikhomirov@588: errorCollector.assertEquals(1, cmt.getRemovedFiles().size()); tikhomirov@588: errorCollector.assertEquals("d", cmt.getRemovedFiles().get(0).toString()); tikhomirov@559: ByteArrayChannel sink = new ByteArrayChannel(); tikhomirov@559: new HgCatCommand(hgRepo).file(Path.create("xx")).changeset(commitRev).execute(sink); tikhomirov@559: assertArrayEquals("xyz".getBytes(), sink.toArray()); tikhomirov@559: // tikhomirov@559: assertHgVerifyOk(repoLoc); tikhomirov@559: } tikhomirov@559: /** tikhomirov@559: * perform few commits one by one, into different branches tikhomirov@559: */ tikhomirov@559: @Test tikhomirov@559: public void testSequentialCommits() throws Exception { tikhomirov@559: File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-add-remove-commit", false); tikhomirov@559: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@559: HgDataFile dfD = hgRepo.getFileNode("d"); tikhomirov@559: assertTrue("[sanity]", dfD.exists()); tikhomirov@559: File fileD = new File(repoLoc, "d"); tikhomirov@559: assertTrue("[sanity]", fileD.canRead()); tikhomirov@559: // tikhomirov@559: RepoUtils.modifyFileAppend(fileD, " 1 \n"); tikhomirov@559: final int parentCsetRevIndex = hgRepo.getChangelog().getLastRevision(); tikhomirov@591: CommitFacility cf = new CommitFacility(Internals.getInstance(hgRepo), parentCsetRevIndex); tikhomirov@559: FileContentSupplier contentProvider = new FileContentSupplier(fileD); tikhomirov@559: cf.add(dfD, contentProvider); tikhomirov@559: cf.branch("branch1"); tikhomirov@559: Nodeid commitRev1 = cf.commit("FIRST"); tikhomirov@559: contentProvider.done(); tikhomirov@559: // tikhomirov@559: RepoUtils.modifyFileAppend(fileD, " 2 \n"); tikhomirov@559: cf.add(dfD, contentProvider = new FileContentSupplier(fileD)); tikhomirov@559: cf.branch("branch2"); tikhomirov@559: Nodeid commitRev2 = cf.commit("SECOND"); tikhomirov@559: contentProvider.done(); tikhomirov@559: // tikhomirov@559: RepoUtils.modifyFileAppend(fileD, " 2 \n"); tikhomirov@559: cf.add(dfD, contentProvider = new FileContentSupplier(fileD)); tikhomirov@559: cf.branch(DEFAULT_BRANCH_NAME); tikhomirov@559: Nodeid commitRev3 = cf.commit("THIRD"); tikhomirov@559: contentProvider.done(); tikhomirov@559: // tikhomirov@559: List commits = new HgLogCommand(hgRepo).range(parentCsetRevIndex+1, TIP).execute(); tikhomirov@559: assertEquals(3, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@559: HgChangeset c2 = commits.get(1); tikhomirov@559: HgChangeset c3 = commits.get(2); tikhomirov@588: errorCollector.assertEquals(c1.getNodeid(), commitRev1); tikhomirov@588: errorCollector.assertEquals(c2.getNodeid(), commitRev2); tikhomirov@588: errorCollector.assertEquals(c3.getNodeid(), commitRev3); tikhomirov@588: errorCollector.assertEquals("branch1", c1.getBranch()); tikhomirov@588: errorCollector.assertEquals("branch2", c2.getBranch()); tikhomirov@588: errorCollector.assertEquals(DEFAULT_BRANCH_NAME, c3.getBranch()); tikhomirov@588: errorCollector.assertEquals("FIRST", c1.getComment()); tikhomirov@588: errorCollector.assertEquals("SECOND", c2.getComment()); tikhomirov@588: errorCollector.assertEquals("THIRD", c3.getComment()); tikhomirov@559: assertHgVerifyOk(repoLoc); tikhomirov@559: } tikhomirov@559: tikhomirov@586: @Test tikhomirov@586: public void testCommandBasics() throws Exception { tikhomirov@586: File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-commit-cmd", false); tikhomirov@586: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@586: HgDataFile dfB = hgRepo.getFileNode("b"); tikhomirov@586: assertTrue("[sanity]", dfB.exists()); tikhomirov@586: File fileB = new File(repoLoc, "b"); tikhomirov@586: assertTrue("[sanity]", fileB.canRead()); tikhomirov@586: RepoUtils.modifyFileAppend(fileB, " 1 \n"); tikhomirov@586: tikhomirov@586: HgCommitCommand cmd = new HgCommitCommand(hgRepo); tikhomirov@586: assertFalse(cmd.isMergeCommit()); tikhomirov@586: Outcome r = cmd.message("FIRST").execute(); tikhomirov@588: errorCollector.assertTrue(r.isOk()); tikhomirov@586: Nodeid c1 = cmd.getCommittedRevision(); tikhomirov@586: tikhomirov@588: // check that modified files are no longer reported as such tikhomirov@586: TestStatus.StatusCollector status = new TestStatus.StatusCollector(); tikhomirov@588: new HgStatusCommand(hgRepo).all().execute(status); tikhomirov@588: errorCollector.assertTrue(status.getErrors().isEmpty()); tikhomirov@588: errorCollector.assertTrue(status.get(Kind.Modified).isEmpty()); tikhomirov@588: errorCollector.assertEquals(1, status.get(dfB.getPath()).size()); tikhomirov@588: errorCollector.assertTrue(status.get(dfB.getPath()).contains(Kind.Clean)); tikhomirov@586: tikhomirov@586: HgDataFile dfD = hgRepo.getFileNode("d"); tikhomirov@586: assertTrue("[sanity]", dfD.exists()); tikhomirov@586: File fileD = new File(repoLoc, "d"); tikhomirov@586: assertTrue("[sanity]", fileD.canRead()); tikhomirov@586: // tikhomirov@586: RepoUtils.modifyFileAppend(fileD, " 1 \n"); tikhomirov@586: cmd = new HgCommitCommand(hgRepo); tikhomirov@586: assertFalse(cmd.isMergeCommit()); tikhomirov@586: r = cmd.message("SECOND").execute(); tikhomirov@588: errorCollector.assertTrue(r.isOk()); tikhomirov@586: Nodeid c2 = cmd.getCommittedRevision(); tikhomirov@586: // tikhomirov@586: int lastRev = hgRepo.getChangelog().getLastRevision(); tikhomirov@586: List csets = new HgLogCommand(hgRepo).range(lastRev-1, lastRev).execute(); tikhomirov@588: errorCollector.assertEquals(csets.get(0).getNodeid(), c1); tikhomirov@588: errorCollector.assertEquals(csets.get(1).getNodeid(), c2); tikhomirov@588: errorCollector.assertEquals(csets.get(0).getComment(), "FIRST"); tikhomirov@588: errorCollector.assertEquals(csets.get(1).getComment(), "SECOND"); tikhomirov@586: assertHgVerifyOk(repoLoc); tikhomirov@586: } tikhomirov@586: tikhomirov@605: @Test tikhomirov@605: public void testUpdateActiveBookmark() throws Exception { tikhomirov@605: File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-commit-cmd", false); tikhomirov@605: ExecHelper eh = new ExecHelper(new OutputParser.Stub(), repoLoc); tikhomirov@605: String activeBookmark = "bm1"; tikhomirov@605: eh.run("hg", "bookmarks", activeBookmark); tikhomirov@605: tikhomirov@605: HgRepository hgRepo = new HgLookup().detect(repoLoc); tikhomirov@605: assertEquals("[sanity]", activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); tikhomirov@605: Nodeid activeBookmarkRevision = hgRepo.getBookmarks().getRevision(activeBookmark); tikhomirov@605: assertEquals("[sanity]", activeBookmarkRevision, hgRepo.getWorkingCopyParents().first()); tikhomirov@605: tikhomirov@605: HgDataFile dfD = hgRepo.getFileNode("d"); tikhomirov@605: File fileD = new File(repoLoc, "d"); tikhomirov@605: assertTrue("[sanity]", dfD.exists()); tikhomirov@605: assertTrue("[sanity]", fileD.canRead()); tikhomirov@605: tikhomirov@605: RepoUtils.modifyFileAppend(fileD, " 1 \n"); tikhomirov@605: HgCommitCommand cmd = new HgCommitCommand(hgRepo).message("FIRST"); tikhomirov@605: Outcome r = cmd.execute(); tikhomirov@605: errorCollector.assertTrue(r.isOk()); tikhomirov@605: Nodeid c = cmd.getCommittedRevision(); tikhomirov@605: tikhomirov@605: errorCollector.assertEquals(activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); tikhomirov@605: errorCollector.assertEquals(c, hgRepo.getBookmarks().getRevision(activeBookmark)); tikhomirov@605: // reload repo, and repeat the check tikhomirov@605: hgRepo = new HgLookup().detect(repoLoc); tikhomirov@605: errorCollector.assertEquals(activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); tikhomirov@605: errorCollector.assertEquals(c, hgRepo.getBookmarks().getRevision(activeBookmark)); tikhomirov@605: } tikhomirov@605: tikhomirov@559: private void assertHgVerifyOk(File repoLoc) throws InterruptedException, IOException { tikhomirov@559: ExecHelper verifyRun = new ExecHelper(new OutputParser.Stub(), repoLoc); tikhomirov@559: verifyRun.run("hg", "verify"); tikhomirov@588: errorCollector.assertEquals("hg verify", 0, verifyRun.getExitValue()); tikhomirov@538: } tikhomirov@539: tikhomirov@538: public static void main(String[] args) throws Exception { tikhomirov@539: new TestCommit().testCommitToEmpty(); tikhomirov@539: if (Boolean.TRUE.booleanValue()) { tikhomirov@539: return; tikhomirov@539: } tikhomirov@538: String input = "abcdefghijklmnopqrstuvwxyz"; tikhomirov@538: ByteArraySupplier bas = new ByteArraySupplier(input.getBytes()); tikhomirov@538: ByteBuffer bb = ByteBuffer.allocate(7); tikhomirov@538: byte[] result = new byte[26]; tikhomirov@538: int rpos = 0; tikhomirov@538: while (bas.read(bb) != -1) { tikhomirov@538: bb.flip(); tikhomirov@538: bb.get(result, rpos, bb.limit()); tikhomirov@538: rpos += bb.limit(); tikhomirov@538: bb.clear(); tikhomirov@538: } tikhomirov@538: if (input.length() != rpos) { tikhomirov@538: throw new AssertionError(); tikhomirov@538: } tikhomirov@538: String output = new String(result); tikhomirov@538: if (!input.equals(output)) { tikhomirov@538: throw new AssertionError(); tikhomirov@538: } tikhomirov@538: System.out.println(output); tikhomirov@538: } tikhomirov@538: tikhomirov@538: static class ByteArraySupplier implements CommitFacility.ByteDataSupplier { tikhomirov@538: tikhomirov@538: private final byte[] data; tikhomirov@538: private int pos = 0; tikhomirov@538: tikhomirov@538: public ByteArraySupplier(byte[] source) { tikhomirov@538: data = source; tikhomirov@538: } tikhomirov@538: tikhomirov@538: public int read(ByteBuffer buf) { tikhomirov@538: if (pos >= data.length) { tikhomirov@538: return -1; tikhomirov@538: } tikhomirov@538: int count = Math.min(buf.remaining(), data.length - pos); tikhomirov@538: buf.put(data, pos, count); tikhomirov@538: pos += count; tikhomirov@538: return count; tikhomirov@538: } tikhomirov@534: } tikhomirov@534: }