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.wc.SVNFileUtil; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbStatementUtil; import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.*; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class CorruptionTest { @Test public void testDavUpdateFileWithCorruptedPristine() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options)); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testDavUpdateFileWithCorruptedPristine", options); try { final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(); final String originalContentsString = "original contents"; final String newContentsString = "new contents"; final SvnChecksum originalContentsSha1 = TestUtil.calculateSha1(originalContentsString.getBytes()); final CommitBuilder commitBuilder1 = new CommitBuilder(url); commitBuilder1.addFile("file", originalContentsString.getBytes()); commitBuilder1.commit(); final CommitBuilder commitBuilder2 = new CommitBuilder(url); commitBuilder2.changeFile("file", newContentsString.getBytes()); commitBuilder2.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, 1); final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory(); final File file = new File(workingCopyDirectory, "file"); final File pristinePath = getPristinePath(svnOperationFactory, originalContentsSha1, file); corruptContents(pristinePath, "corrupted contents"); final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(file)); try { update.run(); Assert.fail("An exception should be thrown"); } catch (SVNException e) { //expected e.printStackTrace(); Assert.assertEquals(SVNErrorCode.WC_CORRUPT_TEXT_BASE, e.getErrorMessage().getErrorCode()); Assert.assertTrue(e.getErrorMessage().getMessage().contains("Checksum mismatch")); } } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testReposPathDoesntStartWithSlashAfterUpdateOnFile() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testReposPathDoesntStartWithSlashAfterUpdateOnFile", 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 SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(workingCopy.getFile("file"))); update.setRevision(SVNRevision.create(0)); update.run(); assertNoReposPathStartsWithSlash(workingCopy); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testPropdelOfSvnEolStyleResetsTranslatedSizeCache() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testPropdelOfSvnEolStyleResetsTranslatedSizeCache", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder = new CommitBuilder(url); commitBuilder.addFile("file"); commitBuilder.setFileProperty("file", SVNProperty.EOL_STYLE, SVNPropertyValue.create(SVNProperty.EOL_STYLE_NATIVE)); commitBuilder.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File file = workingCopy.getFile("file"); final SvnSetProperty setProperty = svnOperationFactory.createSetProperty(); setProperty.setSingleTarget(SvnTarget.fromFile(file)); setProperty.setPropertyName(SVNProperty.EOL_STYLE); setProperty.setPropertyValue(null); setProperty.run(); assertTranslatedSizeCacheIsReset(workingCopy); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testSymlinkHasCorrectTranslatedSize() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testSymlinkHasCorrectTranslatedSize", options); try { final SVNURL url = sandbox.createSvnRepository(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url); final File link = workingCopy.getFile("directory/link"); SVNFileUtil.ensureDirectoryExists(link.getParentFile()); SVNFileUtil.createSymlink(link, "target"); workingCopy.add(link); workingCopy.commit("Added a link"); assertTranslatedSizeMaybeEquals(workingCopy, "directory/link", "target".getBytes().length); workingCopy.copy("directory", "copiedDirectory"); assertTranslatedSizeMaybeEquals(workingCopy, "copiedDirectory/link", "target".getBytes().length); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } @Test public void testActualNodeConflictWorkingHasNullValueForBinaryConflict() throws Exception { final TestOptions options = TestOptions.getInstance(); Assume.assumeTrue(TestUtil.isNewWorkingCopyTest()); final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testActualNodeConflictWorkingHasNullValueForBinaryConflict", options); try { final SVNURL url = sandbox.createSvnRepository(); final CommitBuilder commitBuilder1 = new CommitBuilder(url); commitBuilder1.addFile("file"); commitBuilder1.commit(); final CommitBuilder commitBuilder2 = new CommitBuilder(url); commitBuilder2.changeFile("file", new byte[]{0, 1, 2}); commitBuilder2.setFileProperty("file", SVNProperty.MIME_TYPE, SVNPropertyValue.create("application/octet-stream")); commitBuilder2.commit(); final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, 1); final File file = workingCopy.getFile("file"); writeBinaryContents(file, new byte[]{0, 1}); final SvnUpdate update = svnOperationFactory.createUpdate(); update.setSingleTarget(SvnTarget.fromFile(workingCopy.getWorkingCopyDirectory())); update.run(); assertActualNodeHasNullConflictWorking(workingCopy, "file"); } finally { svnOperationFactory.dispose(); sandbox.dispose(); } } private void assertActualNodeHasNullConflictWorking(WorkingCopy workingCopy, String path) throws SqlJetException { final SqlJetDb db = SqlJetDb.open(workingCopy.getWCDbFile(), false); try { final ISqlJetTable table = db.getTable(SVNWCDbSchema.ACTUAL_NODE.name()); db.beginTransaction(SqlJetTransactionMode.READ_ONLY); final ISqlJetCursor cursor = table.open(); for (; !cursor.eof(); cursor.next()) { String cursorPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.name()); if (!path.equals(cursorPath)) { continue; } final String conflictWorking = cursor.getString(SVNWCDbSchema.ACTUAL_NODE__Fields.conflict_working.name()); Assert.assertNull(conflictWorking); } cursor.close(); db.commit(); } finally { db.close(); } } private void assertTranslatedSizeMaybeEquals(WorkingCopy workingCopy, String path, int expectedTranslatedSize) throws SqlJetException { 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()) { String cursorPath = cursor.getString(SVNWCDbSchema.NODES__Fields.local_relpath.name()); if (!path.equals(cursorPath)) { continue; } final String translatedSizeString = cursor.getString(SVNWCDbSchema.NODES__Fields.translated_size.name()); if (translatedSizeString == null) { //valid value, skip it continue; } int translatedSize = Integer.parseInt(translatedSizeString); Assert.assertEquals(expectedTranslatedSize, translatedSize); } cursor.close(); db.commit(); } finally { db.close(); } } private void assertTranslatedSizeCacheIsReset(WorkingCopy workingCopy) throws SqlJetException { 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 ISVNWCDb.SVNWCDbKind kind = SvnWcDbStatementUtil.parseKind(cursor.getString(SVNWCDbSchema.NODES__Fields.kind.name())); if (kind != ISVNWCDb.SVNWCDbKind.File) { continue; } final long translatedSize = cursor.getInteger(SVNWCDbSchema.NODES__Fields.translated_size.name()); Assert.assertEquals(-1, translatedSize); } cursor.close(); db.commit(); } finally { db.close(); } } private void writeBinaryContents(File file, byte[] binaryContents) throws IOException { BufferedOutputStream bufferedOutputStream = null; try { bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file)); bufferedOutputStream.write(binaryContents); } finally { SVNFileUtil.closeFile(bufferedOutputStream); } } private void assertNoReposPathStartsWithSlash(WorkingCopy workingCopy) throws SqlJetException { 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 String reposPath = cursor.getString(SVNWCDbSchema.NODES__Fields.repos_path.name()); Assert.assertFalse("repos_path '" + reposPath + "' starts with '/'", reposPath.startsWith("/")); } cursor.close(); db.commit(); } finally { db.close(); } } private File getPristinePath(SvnOperationFactory svnOperationFactory, SvnChecksum originalContentsSha1, File file) throws SVNException { final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler()); try { final ISVNWCDb db = context.getDb(); return db.getPristinePath(file, originalContentsSha1); } finally { context.close(); } } private void corruptContents(File pristinePath, String corruptedContents) throws SVNException { TestUtil.writeFileContentsString(pristinePath, corruptedContents); } private String getTestName() { return "CorruptionTest"; } }