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@559: import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME; tikhomirov@539: import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; tikhomirov@539: tikhomirov@538: import java.io.File; tikhomirov@559: import java.io.FileInputStream; tikhomirov@538: import java.io.FileWriter; tikhomirov@559: import java.io.IOException; tikhomirov@538: import java.nio.ByteBuffer; tikhomirov@559: import java.nio.channels.FileChannel; tikhomirov@559: import java.util.List; tikhomirov@538: tikhomirov@559: import org.hamcrest.CoreMatchers; 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@559: import org.tmatesoft.hg.core.HgLogCommand; tikhomirov@559: import org.tmatesoft.hg.core.Nodeid; tikhomirov@559: import org.tmatesoft.hg.internal.ByteArrayChannel; tikhomirov@538: import org.tmatesoft.hg.repo.CommitFacility; 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@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@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@539: CommitFacility cf = new CommitFacility(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@559: assertEquals(2, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@559: HgChangeset c2 = commits.get(1); tikhomirov@559: assertEquals("FIRST", c1.getComment()); tikhomirov@559: assertEquals("SECOND", c2.getComment()); tikhomirov@559: assertEquals(df.getPath(), c2.getAffectedFiles().get(0)); tikhomirov@559: assertEquals(c1.getNodeid(), c2.getFirstParentRevision()); tikhomirov@559: assertEquals(Nodeid.NULL, c2.getSecondParentRevision()); tikhomirov@559: 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@539: CommitFacility cf = new CommitFacility(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@559: assertEquals(1, commits.size()); tikhomirov@559: HgChangeset c1 = commits.get(0); tikhomirov@559: assertEquals(1, c1.getAffectedFiles().size()); tikhomirov@559: assertEquals(df.getPath(), c1.getAffectedFiles().get(0)); tikhomirov@559: assertEquals(0, c1.getRevisionIndex()); tikhomirov@559: assertEquals(Nodeid.NULL, c1.getFirstParentRevision()); tikhomirov@559: assertEquals(Nodeid.NULL, c1.getSecondParentRevision()); tikhomirov@559: assertEquals(HgRepository.DEFAULT_BRANCH_NAME, c1.getBranch()); tikhomirov@559: assertEquals(comment, c1.getComment()); tikhomirov@559: 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@559: CommitFacility cf = new CommitFacility(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: // FIXME requirement to reload repository is disgusting tikhomirov@559: hgRepo = new HgLookup().detect(repoLoc); 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@559: assertEquals(c1.getNodeid(), commitRev1); tikhomirov@559: assertEquals("branch1", c1.getBranch()); tikhomirov@559: 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@559: CommitFacility cf = new CommitFacility(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: // FIXME requirement to reload repository is disgusting tikhomirov@559: hgRepo = new HgLookup().detect(repoLoc); tikhomirov@559: List commits = new HgLogCommand(hgRepo).changeset(commitRev).execute(); tikhomirov@559: HgChangeset cmt = commits.get(0); tikhomirov@559: assertEquals(1, cmt.getAddedFiles().size()); tikhomirov@559: assertEquals("xx", cmt.getAddedFiles().get(0).getPath().toString()); tikhomirov@559: assertEquals(1, cmt.getRemovedFiles().size()); tikhomirov@559: 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@559: CommitFacility cf = new CommitFacility(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: // FIXME requirement to reload repository is disgusting tikhomirov@559: hgRepo = new HgLookup().detect(repoLoc); 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@559: assertEquals(c1.getNodeid(), commitRev1); tikhomirov@559: assertEquals(c2.getNodeid(), commitRev2); tikhomirov@559: assertEquals(c3.getNodeid(), commitRev3); tikhomirov@559: assertEquals("branch1", c1.getBranch()); tikhomirov@559: assertEquals("branch2", c2.getBranch()); tikhomirov@559: assertEquals(DEFAULT_BRANCH_NAME, c3.getBranch()); tikhomirov@559: assertEquals("FIRST", c1.getComment()); tikhomirov@559: assertEquals("SECOND", c2.getComment()); tikhomirov@559: assertEquals("THIRD", c3.getComment()); tikhomirov@559: assertHgVerifyOk(repoLoc); tikhomirov@559: } tikhomirov@559: 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@559: 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@559: tikhomirov@559: static class FileContentSupplier implements CommitFacility.ByteDataSupplier { tikhomirov@559: private final FileChannel channel; tikhomirov@559: private IOException error; tikhomirov@559: tikhomirov@559: public FileContentSupplier(File f) throws IOException { tikhomirov@559: if (!f.canRead()) { tikhomirov@559: throw new IOException(String.format("Can't read file %s", f)); tikhomirov@559: } tikhomirov@559: channel = new FileInputStream(f).getChannel(); tikhomirov@559: } tikhomirov@559: tikhomirov@559: public int read(ByteBuffer buf) { tikhomirov@559: if (error != null) { tikhomirov@559: return -1; tikhomirov@559: } tikhomirov@559: try { tikhomirov@559: return channel.read(buf); tikhomirov@559: } catch (IOException ex) { tikhomirov@559: error = ex; tikhomirov@559: } tikhomirov@559: return -1; tikhomirov@559: } tikhomirov@559: tikhomirov@559: public void done() throws IOException { tikhomirov@559: channel.close(); tikhomirov@559: if (error != null) { tikhomirov@559: throw error; tikhomirov@559: } tikhomirov@559: } tikhomirov@559: } tikhomirov@534: }