/** * This file is part of git-as-svn. It is subject to the license terms * in the LICENSE file found in the top-level directory of this distribution * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn, * including this file, may be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ package svnserver.server; import com.google.common.base.Strings; import org.jetbrains.annotations.NotNull; import org.testng.Assert; import org.testng.annotations.Test; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.SVNRepository; import svnserver.SvnTestServer; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.zip.GZIPOutputStream; import static svnserver.SvnTestHelper.*; /** * Check file content filter. * * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com> */ public class SvnFilterTest { @NotNull private final static byte[] CONTENT_FOO = (Strings.repeat("Some data\n", 100) + "Foo file\n").getBytes(StandardCharsets.UTF_8); @NotNull private final static byte[] CONTENT_BAR = (Strings.repeat("Some data\n", 100) + "Bar file\n").getBytes(StandardCharsets.UTF_8); @NotNull private final static Map<String, String> propsBinary = new HashMap<String, String>() {{ put(SVNProperty.MIME_TYPE, SVNFileUtil.BINARY_MIME_TYPE); }}; @NotNull private final static Map<String, String> propsNative = new HashMap<String, String>() {{ put(SVNProperty.EOL_STYLE, SVNProperty.EOL_STYLE_NATIVE); }}; /** * Check file read content on filter change. * * @throws Exception */ @Test public void binaryRead() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); final byte[] uncompressed = "Test file\0".getBytes(StandardCharsets.UTF_8); final byte[] compressed = gzip(uncompressed); // Add compressed file to repository. createFile(repo, "/data.z", compressed, propsBinary); createFile(repo, "/data.x", compressed, propsBinary); checkFileProp(repo, "/data.z", propsBinary); checkFileProp(repo, "/data.x", propsBinary); checkFileContent(repo, "/data.z", compressed); checkFileContent(repo, "/data.x", compressed); // Add filter to file. createFile(repo, "/.gitattributes", "*.z\t\t\tfilter=gzip\n", propsNative); // On file read now we must have uncompressed content. checkFileProp(repo, "/data.z", propsBinary); checkFileProp(repo, "/data.x", propsBinary); checkFileContent(repo, "/data.z", uncompressed); checkFileContent(repo, "/data.x", compressed); // Modify filter. modifyFile(repo, "/.gitattributes", "*.x\t\t\tfilter=gzip\n", repo.getLatestRevision()); // Check result. checkFileProp(repo, "/data.z", propsBinary); checkFileProp(repo, "/data.x", propsBinary); checkFileContent(repo, "/data.z", compressed); checkFileContent(repo, "/data.x", uncompressed); } } /** * Check file read content on filter change. * * @throws Exception */ @Test public void textRead() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); final byte[] uncompressed = "Test file".getBytes(StandardCharsets.UTF_8); final byte[] compressed = gzip(uncompressed); // Add compressed file to repository. createFile(repo, "/data.z", compressed, propsBinary); createFile(repo, "/data.x", compressed, propsBinary); checkFileProp(repo, "/data.z", propsBinary); checkFileProp(repo, "/data.x", propsBinary); checkFileContent(repo, "/data.z", compressed); checkFileContent(repo, "/data.x", compressed); // Add filter to file. createFile(repo, "/.gitattributes", "*.z\t\t\tfilter=gzip\n", propsNative); // After commit .gitattributes file data.z must change property svn:mime-type and content automagically. { final Set<String> changed = new HashSet<>(); repo.log(new String[]{""}, repo.getLatestRevision(), repo.getLatestRevision(), true, false, logEntry -> changed.addAll(logEntry.getChangedPaths().keySet())); Assert.assertTrue(changed.contains("/.gitattributes")); Assert.assertTrue(changed.contains("/data.z")); Assert.assertEquals(changed.size(), 2); } // On file read now we must have uncompressed content. checkFileProp(repo, "/data.z", propsNative); checkFileProp(repo, "/data.x", propsBinary); checkFileContent(repo, "/data.z", uncompressed); checkFileContent(repo, "/data.x", compressed); // Modify filter. modifyFile(repo, "/.gitattributes", "*.x\t\t\tfilter=gzip\n", repo.getLatestRevision()); // After commit .gitattributes file data.z must change property svn:mime-type and content automagically. { final Set<String> changed = new HashSet<>(); repo.log(new String[]{""}, repo.getLatestRevision(), repo.getLatestRevision(), true, false, logEntry -> changed.addAll(logEntry.getChangedPaths().keySet())); Assert.assertTrue(changed.contains("/.gitattributes")); Assert.assertTrue(changed.contains("/data.z")); Assert.assertTrue(changed.contains("/data.x")); Assert.assertEquals(changed.size(), 3); } // Check result. checkFileProp(repo, "/data.z", propsBinary); checkFileProp(repo, "/data.x", propsNative); checkFileContent(repo, "/data.z", compressed); checkFileContent(repo, "/data.x", uncompressed); } } /** * Write filtered file. * * @throws Exception */ @Test() public void write() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); // Add filter to file. createFile(repo, "/.gitattributes", "/*.z\t\t\tfilter=gzip\n", propsNative); // On file read now we must have uncompressed content. createFile(repo, "/data.z", CONTENT_FOO, propsNative); checkFileContent(repo, "/data.z", CONTENT_FOO); // Modify file. modifyFile(repo, "/data.z", CONTENT_BAR, repo.getLatestRevision()); checkFileContent(repo, "/data.z", CONTENT_BAR); } } /** * Write file before .gitattributes in single commit. * * @throws Exception */ @Test() public void writeBeforeAttributes() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); // Create file. { final ISVNEditor editor = repo.getCommitEditor("Complex commit", null, false, null); editor.openRoot(-1); editor.addFile("data.z", null, -1); editor.changeFileProperty("data.z", SVNProperty.EOL_STYLE, SVNPropertyValue.create(SVNProperty.EOL_STYLE_NATIVE)); sendDeltaAndClose(editor, "data.z", null, CONTENT_FOO); editor.addFile(".gitattributes", null, -1); editor.changeFileProperty(".gitattributes", SVNProperty.EOL_STYLE, SVNPropertyValue.create(SVNProperty.EOL_STYLE_NATIVE)); sendDeltaAndClose(editor, ".gitattributes", null, "*.z\t\t\tfilter=gzip\n"); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_FOO); // Modify file. { final long rev = repo.getLatestRevision(); final ISVNEditor editor = repo.getCommitEditor("Complex commit", null, false, null); editor.openRoot(-1); editor.openFile("data.z", rev); sendDeltaAndClose(editor, "data.z", CONTENT_FOO, CONTENT_BAR); editor.openFile(".gitattributes", rev); sendDeltaAndClose(editor, ".gitattributes", "*.z\t\t\tfilter=gzip\n", ""); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_BAR); } } /** * Write file after .gitattributes in single commit. * * @throws Exception */ @Test() public void writeAfterAttributes() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); // Create file. { final ISVNEditor editor = repo.getCommitEditor("Complex commit", null, false, null); editor.openRoot(-1); editor.addFile(".gitattributes", null, -1); editor.changeFileProperty(".gitattributes", SVNProperty.EOL_STYLE, SVNPropertyValue.create(SVNProperty.EOL_STYLE_NATIVE)); sendDeltaAndClose(editor, ".gitattributes", null, "*.z\t\t\tfilter=gzip\n"); editor.addFile("data.z", null, -1); editor.changeFileProperty("data.z", SVNProperty.EOL_STYLE, SVNPropertyValue.create(SVNProperty.EOL_STYLE_NATIVE)); sendDeltaAndClose(editor, "data.z", null, CONTENT_FOO); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_FOO); // Modify file. { final long rev = repo.getLatestRevision(); final ISVNEditor editor = repo.getCommitEditor("Complex commit", null, false, null); editor.openRoot(-1); editor.openFile(".gitattributes", rev); sendDeltaAndClose(editor, ".gitattributes", "*.z\t\t\tfilter=gzip\n", ""); editor.openFile("data.z", rev); sendDeltaAndClose(editor, "data.z", CONTENT_FOO, CONTENT_BAR); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_BAR); } } /** * Copy file with filter change. * * @throws Exception */ @Test() public void copy() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); // Add filter to file. createFile(repo, "/.gitattributes", "/*.z\t\t\tfilter=gzip\n", propsNative); // Create source file. createFile(repo, "/data.txt", CONTENT_FOO, propsNative); // Copy source file with "raw" filter to destination with "gzip" filter. { final long rev = repo.getLatestRevision(); final ISVNEditor editor = repo.getCommitEditor("Copy file commit", null, false, null); editor.openRoot(-1); editor.addFile("data.z", "data.txt", rev); editor.closeFile("data.z", null); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_FOO); } } /** * Copy file with filter change. * * @throws Exception */ @Test() public void copyAndModify() throws Exception { try (SvnTestServer server = SvnTestServer.createEmpty()) { final SVNRepository repo = server.openSvnRepository(); // Add filter to file. createFile(repo, "/.gitattributes", "/*.z\t\t\tfilter=gzip\n", propsNative); // Create source file. createFile(repo, "/data.txt", CONTENT_FOO, propsNative); // Copy source file with "raw" filter to destination with "gzip" filter. { final long rev = repo.getLatestRevision(); final ISVNEditor editor = repo.getCommitEditor("Copy file commit", null, false, null); editor.openRoot(-1); editor.addFile("data.z", "data.txt", rev); sendDeltaAndClose(editor, "data.z", CONTENT_FOO, CONTENT_BAR); editor.closeDir(); editor.closeEdit(); } // On file read now we must have uncompressed content. checkFileContent(repo, "/data.z", CONTENT_BAR); } } private static byte[] gzip(@NotNull byte[] data) throws IOException { final ByteArrayOutputStream result = new ByteArrayOutputStream(); try (OutputStream stream = new GZIPOutputStream(result)) { stream.write(data); } return result.toByteArray(); } }