package org.tmatesoft.svn.test; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; import org.tmatesoft.sqljet.core.SqlJetException; import org.tmatesoft.sqljet.core.SqlJetTransactionMode; import org.tmatesoft.sqljet.core.table.ISqlJetCursor; import org.tmatesoft.sqljet.core.table.ISqlJetTable; import org.tmatesoft.sqljet.core.table.SqlJetDb; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.db.SVNSqlJetStatement; import org.tmatesoft.svn.core.internal.io.dav.DAVUtil; import org.tmatesoft.svn.core.internal.wc.SVNExternal; import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.*; import java.io.File; import java.util.ArrayList; import java.util.List; public class UpdateTest { @Test public void testUpdateWithoutChangesReportsUnlock() throws Exception { final TestOptions options = TestOptions.getInstance(); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateWithoutChangesReportsUnlock", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder = new CommitBuilder(url); commitBuilder.addFile("file"); commitBuilder.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory(); final File file = new File(workingCopyDirectory, "file"); final SVNURL fileUrl = url.appendPath("file", false); final SvnSetLock setLock = svnOperationFactory.createSetLock(); setLock.setSingleTarget(SvnTarget.fromFile(file)); setLock.run(); final SvnUnlock unlock = svnOperationFactory.createUnlock(); unlock.setSingleTarget(SvnTarget.fromURL(fileUrl)); unlock.run(); final EventsHandler eventsHandler = new EventsHandler(); svnOperationFactory.setEventHandler(eventsHandler); final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory)); update.run(); final List<SVNEvent> events = eventsHandler.getEvents(); Assert.assertEquals(3, events.size()); final SVNEvent updateStartedEvent = events.get(0); final SVNEvent unlockedEvent = events.get(1); final SVNEvent updateCompletedEvent = events.get(2); Assert.assertEquals(SVNEventAction.UPDATE_STARTED, updateStartedEvent.getAction()); Assert.assertEquals(SVNEventAction.UPDATE_UPDATE, unlockedEvent.getAction()); Assert.assertEquals(SVNEventAction.UPDATE_COMPLETED, updateCompletedEvent.getAction()); Assert.assertEquals(workingCopyDirectory, updateStartedEvent.getFile()); Assert.assertEquals(file, unlockedEvent.getFile()); Assert.assertEquals(workingCopyDirectory, updateCompletedEvent.getFile()); Assert.assertEquals(SVNStatusType.LOCK_UNLOCKED, unlockedEvent.getLockStatus()); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testUpdateOverUnversionedFileProducesConflict() throws Exception { final TestOptions options = TestOptions.getInstance(); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOverUnversionedFileProducesConflict", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder = new CommitBuilder(url); commitBuilder.addFile("file"); commitBuilder.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory(); final SvnCheckout checkout = svnOperationFactory.createCheckout(); checkout.setSource(SvnTarget.fromURL(url)); checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory)); checkout.setDepth(SVNDepth.EMPTY); checkout.run(); final File file = new File(workingCopyDirectory, "file"); TestUtil.writeFileContentsString(file, "contents"); if (TestUtil.isNewWorkingCopyTest()) { final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(file)); update.run(); //in new working copy a conflict should be produced final SvnGetStatus getStatus = svnOperationFactory.createGetStatus(); getStatus.setSingleTarget(SvnTarget.fromFile(file)); final SvnStatus status = getStatus.run(); Assert.assertTrue(status.isConflicted()); } else { final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(file)); try { update.run(); //in old working copy obstructed update should cause an exception Assert.fail("An exception should be thrown"); } catch (SVNException e) { //expected Assert.assertEquals(SVNErrorCode.WC_OBSTRUCTED_UPDATE, e.getErrorMessage().getErrorCode()); } } } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testCorrectDepthIsReportedForDepthImmediates() throws Exception { final TestOptions options = TestOptions.getInstance(); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testCorrectDepthIsReportedForDepthImmediates", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder = new CommitBuilder(url); commitBuilder.addFile("directory/file"); commitBuilder.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory(); final File file = workingCopy.getFile("directory/file"); TestUtil.writeFileContentsString(file, "changed contents"); final SvnCommit commit = svnOperationFactory.createCommit(); commit.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory)); commit.run(); final SvnUpdate update = svnOperationFactory.createUpdate(); update.setDepth(SVNDepth.IMMEDIATES); update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory)); final long[] revision = update.run(); Assert.assertEquals(2, revision[0]); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testDavCacheIsCleaned() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options)); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testDavCacheIsCleaned", options); try { final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(); final SVNExternal external = new SVNExternal("external", url.appendPath("file", false).toString(), SVNRevision.HEAD, SVNRevision.HEAD, false, false, true); final CommitBuilder commitBuilder1 = new CommitBuilder(url); commitBuilder1.addFile("file"); commitBuilder1.setDirectoryProperty("", SVNProperty.EXTERNALS, SVNPropertyValue.create(external.toString())); commitBuilder1.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory(); final CommitBuilder commitBuilder2 = new CommitBuilder(url); commitBuilder2.changeFile("file", "contents".getBytes()); commitBuilder2.commit(); DAVUtil.setUseDAVWCURL(false); final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory)); update.run(); assertDavPropertiesAreCleaned(workingCopy); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testUpdateAlwaysUpdatesFileTimestamp() throws Exception { //SVNKIT-322: file timestamp is updated to some time in past // if update rolls back a file to some contents that is already present in .svn/pristine final TestOptions options = TestOptions.getInstance(); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateAlwaysUpdatesFileTimestamp", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder1 = new CommitBuilder(url); commitBuilder1.addFile("file", "original contents".getBytes()); commitBuilder1.commit(); final CommitBuilder commitBuilder2 = new CommitBuilder(url); commitBuilder2.changeFile("file", "changes contents".getBytes()); commitBuilder2.commit(); final CommitBuilder commitBuilder3 = new CommitBuilder(url); commitBuilder3.changeFile("file", "original contents".getBytes()); commitBuilder3.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File file = workingCopy.getFile("file"); final SvnGetInfo getInfoBeforeUpdates = svnOperationFactory.createGetInfo(); getInfoBeforeUpdates.setSingleTarget(SvnTarget.fromFile(file)); final SvnInfo infoBeforeUpdates = getInfoBeforeUpdates.run(); Thread.sleep(1000); // make expected file timestamp different from the actual final SvnUpdate update1 = svnOperationFactory.createUpdate(); update1.setSingleTarget(SvnTarget.fromFile(file)); update1.setRevision(SVNRevision.create(2)); update1.run(); final SvnGetInfo getInfoBetweenUpdates = svnOperationFactory.createGetInfo(); getInfoBetweenUpdates.setSingleTarget(SvnTarget.fromFile(file)); final SvnInfo infoBetweenUpdates = getInfoBetweenUpdates.run(); Thread.sleep(1000); // make expected file timestamp different from the actual final SvnUpdate update2 = svnOperationFactory.createUpdate(); update2.setSingleTarget(SvnTarget.fromFile(file)); update2.setRevision(SVNRevision.create(1)); update2.run(); final SvnGetInfo getInfoAfterUpdates = svnOperationFactory.createGetInfo(); getInfoAfterUpdates.setSingleTarget(SvnTarget.fromFile(file)); final SvnInfo infoAfterUpdates = getInfoAfterUpdates.run(); final long timestampBeforeUpdates = infoBeforeUpdates.getWcInfo().getRecordedTime(); final long timestampBetweenUpdates = infoBetweenUpdates.getWcInfo().getRecordedTime(); final long timestampAfterUpdates = infoAfterUpdates.getWcInfo().getRecordedTime(); // timestamps should always increase, otherwise this confuses build tools that rely on timestamps Assert.assertTrue(timestampBeforeUpdates < timestampBetweenUpdates); Assert.assertTrue(timestampBetweenUpdates < timestampAfterUpdates); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } private void assertDavPropertiesAreCleaned(WorkingCopy workingCopy) throws SqlJetException, SVNException { final SqlJetDb db = SqlJetDb.open(workingCopy.getWCDbFile(), false); try { final ISqlJetTable table = db.getTable(SVNWCDbSchema.NODES.name()); db.beginTransaction(SqlJetTransactionMode.READ_ONLY); final ISqlJetCursor cursor = table.open(); for (; !cursor.eof(); cursor.next()) { final SVNProperties properties = SVNSqlJetStatement.parseProperties(cursor.getBlobAsArray(SVNWCDbSchema.NODES__Fields.dav_cache.name())); final SVNPropertyValue wcUrlPropertyValue = properties == null ? null : properties.getSVNPropertyValue(SVNProperty.WC_URL); Assert.assertNull(wcUrlPropertyValue); } cursor.close(); db.commit(); } finally { db.close(); } } private String getTestName() { return "UpdateTest"; } private static class EventsHandler implements ISVNEventHandler { private final List<SVNEvent> events; private EventsHandler() { this.events = new ArrayList<SVNEvent>(); } public List<SVNEvent> getEvents() { return events; } public void handleEvent(SVNEvent event, double progress) throws SVNException { events.add(event); } public void checkCancelled() throws SVNCancelException { } } }