# HG changeset patch # User Artem Tikhomirov # Date 1373480197 -7200 # Node ID dde18bc7053b922a104ae5431b7907f663f3d69d # Parent ae2d439fbed3bd76515b145f5518ee35a3749a59 Test Copy-on-Write transactions diff -r ae2d439fbed3 -r dde18bc7053b build.xml --- a/build.xml Wed Jul 10 19:33:51 2013 +0200 +++ b/build.xml Wed Jul 10 20:16:37 2013 +0200 @@ -84,6 +84,7 @@ + diff -r ae2d439fbed3 -r dde18bc7053b src/org/tmatesoft/hg/internal/BasicSessionContext.java --- a/src/org/tmatesoft/hg/internal/BasicSessionContext.java Wed Jul 10 19:33:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/BasicSessionContext.java Wed Jul 10 20:16:37 2013 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012 TMate Software Ltd + * Copyright (c) 2011-2013 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ -public class BasicSessionContext extends SessionContext { +public class BasicSessionContext extends SessionContext implements SessionContext.Source { private LogFacility logFacility; private final Map properties; @@ -68,4 +68,8 @@ value = System.getProperty(name); return value == null ? defaultValue : value; } + + public SessionContext getSessionContext() { + return this; + } } diff -r ae2d439fbed3 -r dde18bc7053b src/org/tmatesoft/hg/internal/COWTransaction.java --- a/src/org/tmatesoft/hg/internal/COWTransaction.java Wed Jul 10 19:33:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/COWTransaction.java Wed Jul 10 20:16:37 2013 +0200 @@ -24,6 +24,7 @@ import org.tmatesoft.hg.core.HgIOException; import org.tmatesoft.hg.core.SessionContext; +import org.tmatesoft.hg.repo.HgInvalidStateException; /** * This transaction strategy makes a copy of original file and breaks origin hard links, if any. @@ -103,7 +104,9 @@ public void commit() throws HgIOException { for (Iterator it = entries.iterator(); it.hasNext();) { RollbackEntry e = it.next(); - assert e.success; + if (!e.success) { + throw new HgInvalidStateException(String.format("Attempt to commit transaction without successful clearance of file %s", e.origin)); + } if (e.failure != null) { throw new HgIOException("Can't close transaction with a failure.", e.failure, e.origin); } diff -r ae2d439fbed3 -r dde18bc7053b test/org/tmatesoft/hg/test/TestPull.java --- a/test/org/tmatesoft/hg/test/TestPull.java Wed Jul 10 19:33:51 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestPull.java Wed Jul 10 20:16:37 2013 +0200 @@ -45,7 +45,6 @@ import org.tmatesoft.hg.util.Path; /** - * FIXME need TestTransaction to check transaction rolback/commit as it's tricky to test transactions as part of pull/push commands * @author Artem Tikhomirov * @author TMate Software Ltd. */ diff -r ae2d439fbed3 -r dde18bc7053b test/org/tmatesoft/hg/test/TestTransaction.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/org/tmatesoft/hg/test/TestTransaction.java Wed Jul 10 20:16:37 2013 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.test; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; +import org.tmatesoft.hg.internal.BasicSessionContext; +import org.tmatesoft.hg.internal.COWTransaction; +import org.tmatesoft.hg.internal.Transaction; + +/** + * Check transaction rollback/commit as it's tricky to test transactions as part of pull/push commands + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class TestTransaction { + + @Rule + public ErrorCollectorExt errorCollector = new ErrorCollectorExt(); + + @Test + public void testCopyOnWriteTransaction() throws Exception { + final BasicSessionContext ctx = new BasicSessionContext(null); + Transaction.Factory f = new COWTransaction.Factory(); + File dir = RepoUtils.createEmptyDir("test-transaction-cow"); + File f1 = new File(dir, "f1"); + File f2 = new File(dir, "f2"); + File f3 = new File(dir, "f3"); + RepoUtils.createFile(f1, "1"); + assertTrue(f1.exists()); + assertFalse(f2.exists()); + assertFalse(f3.exists()); + // + // transaction commit + Transaction tr1 = f.create(ctx); + File tf1 = tr1.prepare(f1); + RepoUtils.modifyFileAppend(tf1, "2"); + tr1.done(tf1); + File tf2 = tr1.prepare(f2); + errorCollector.assertTrue(tf2.exists()); + RepoUtils.modifyFileAppend(tf2, "A"); + tr1.done(tf2); + tr1.commit(); + errorCollector.assertTrue(f1.isFile()); + errorCollector.assertTrue(f2.isFile()); + errorCollector.assertEquals("12", read(f1)); + errorCollector.assertEquals("A", read(f2)); + // + // transaction rollback + assertFalse(f3.exists()); + Transaction tr2 = f.create(ctx); + tf1 = tr2.prepare(f1); + RepoUtils.modifyFileAppend(tf1, "3"); + tr2.done(tf1); + errorCollector.assertEquals("123", read(tf1)); + tf2 = tr2.prepare(f2); + RepoUtils.modifyFileAppend(tf2, "B"); + tr2.done(tf2); + errorCollector.assertEquals("AB", read(tf2)); + File tf3 = tr2.prepare(f3); + errorCollector.assertTrue(tf3.exists()); + RepoUtils.modifyFileAppend(tf3, "!"); + tr2.done(tf3); + errorCollector.assertEquals("!", read(tf3)); + tr2.rollback(); + errorCollector.assertTrue(f1.isFile()); + errorCollector.assertTrue(f2.isFile()); + errorCollector.assertFalse(f3.isFile()); + errorCollector.assertEquals("12", read(f1)); + errorCollector.assertEquals("A", read(f2)); + } + + String read(File f) throws IOException { + StringBuilder sb = new StringBuilder(); + FileReader fr = new FileReader(f); + int ch; + while ((ch = fr.read()) != -1) { + sb.append((char) ch); + } + fr.close(); + return sb.toString(); + } +}