summaryrefslogtreecommitdiff
path: root/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java')
-rw-r--r--subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java1444
1 files changed, 1444 insertions, 0 deletions
diff --git a/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java b/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
new file mode 100644
index 0000000..b557416
--- /dev/null
+++ b/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
@@ -0,0 +1,1444 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+package org.apache.subversion.javahl;
+
+import org.apache.subversion.javahl.*;
+import org.apache.subversion.javahl.remote.*;
+import org.apache.subversion.javahl.callback.*;
+import org.apache.subversion.javahl.types.*;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * This class is used for testing the SVNReposAccess class
+ *
+ * More methodes for testing are still needed
+ */
+public class SVNRemoteTests extends SVNTests
+{
+ protected OneTest thisTest;
+
+ public SVNRemoteTests()
+ {
+ }
+
+ public SVNRemoteTests(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ thisTest = new OneTest();
+ }
+
+ public static ISVNRemote getSession(String url, String configDirectory)
+ {
+ try
+ {
+ RemoteFactory factory = new RemoteFactory();
+ factory.setConfigDirectory(configDirectory);
+ factory.setUsername(USERNAME);
+ // Do not set default password, exercise prompter instead.
+ if (DefaultAuthn.useDeprecated())
+ factory.setPrompt(DefaultAuthn.getDeprecated());
+ else
+ factory.setPrompt(DefaultAuthn.getDefault());
+
+ ISVNRemote raSession = factory.openRemoteSession(url);
+ assertNotNull("Null session was returned by factory", raSession);
+ return raSession;
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private ISVNRemote getSession()
+ {
+ return getSession(getTestRepoUrl(), super.conf.getAbsolutePath());
+ }
+
+ /**
+ * Test the basic SVNAdmin.create functionality
+ * @throws SubversionException
+ */
+ public void testCreate()
+ throws SubversionException, IOException
+ {
+ assertTrue("repository exists", thisTest.getRepository().exists());
+ }
+
+ public void testGetSession_ConfigConstructor() throws Exception
+ {
+ ISVNRemote session;
+ try
+ {
+ if (DefaultAuthn.useDeprecated())
+ session = new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDeprecated(),
+ null, null, null)
+ .openRemoteSession(getTestRepoUrl());
+ else
+ session = new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDefault(),
+ null, null, null)
+ .openRemoteSession(getTestRepoUrl());
+ }
+ catch (ClientException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ assertNotNull("Null session was returned by factory", session);
+ assertEquals(getTestRepoUrl(), session.getSessionUrl());
+ }
+
+ public void testDispose() throws Exception
+ {
+ ISVNRemote session = getSession();
+ session.dispose();
+ }
+
+ public void testSessionGC() throws Exception
+ {
+ int svnErrorCode = 0;
+ try {
+ try {
+ String prefix = getTestRepoUrl().substring(
+ 0, 1 + getTestRepoUrl().lastIndexOf("/"));
+
+ if (DefaultAuthn.useDeprecated())
+ new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDeprecated(),
+ null, null, null)
+ .openRemoteSession(prefix + "repositorydoesnotexisthere");
+ else
+ new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDefault(),
+ null, null, null)
+ .openRemoteSession(prefix + "repositorydoesnotexisthere");
+ }
+ finally
+ {
+ for(int i = 0; i < 100; i++)
+ {
+ Runtime.getRuntime().gc(); // GC should run finalize
+
+ // Do something
+ byte[] memEater = new byte[1024 * 1024];
+ Arrays.fill(memEater, (byte) i);
+
+ // Do some more javahl activity (this url is OK)
+ final ISVNRemote session = getSession();
+ session.getLatestRevision();
+ session.dispose();
+ }
+ }
+ }
+ catch (ClientException ex)
+ {
+ List<ClientException.ErrorMessage> msgs = ex.getAllMessages();
+ svnErrorCode = msgs.get(msgs.size() - 1).getCode();
+ }
+
+ assertTrue(svnErrorCode == 180001 // file:
+ || svnErrorCode == 210005 // svn:
+ || svnErrorCode == 2); // http:
+ }
+
+ public void testDatedRev() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ long revision = session.getRevisionByDate(new Date());
+ assertEquals(revision, 1);
+ }
+
+ public void testGetLocks() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ Set<String> iotaPathSet = new HashSet<String>(1);
+ String iotaPath = thisTest.getWCPath() + "/iota";
+ iotaPathSet.add(iotaPath);
+
+ client.lock(iotaPathSet, "foo", false);
+
+ Map<String, Lock> locks = session.getLocks("iota", Depth.infinity);
+
+ assertEquals(locks.size(), 1);
+ Lock lock = locks.get("/iota");
+ assertNotNull(lock);
+ assertEquals(lock.getOwner(), "jrandom");
+ }
+
+ public void testCheckPath() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ NodeKind kind = session.checkPath("iota", 1);
+ assertEquals(NodeKind.file, kind);
+
+ kind = session.checkPath("iota", 0);
+ assertEquals(NodeKind.none, kind);
+
+ kind = session.checkPath("A", 1);
+ assertEquals(NodeKind.dir, kind);
+ }
+
+ public void testStat() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ DirEntry dirent = session.stat("iota", 1);
+ assertEquals(NodeKind.file, dirent.getNodeKind());
+
+ dirent = session.stat("iota", 0);
+ assertNull(dirent);
+
+ dirent = session.stat("A", 1);
+ assertEquals(NodeKind.dir, dirent.getNodeKind());
+ }
+
+ private String getTestRepoUrl()
+ {
+ return thisTest.getUrl().toASCIIString();
+ }
+
+ public void testGetLatestRevision() throws Exception
+ {
+ ISVNRemote session = getSession();
+ long revision = session.getLatestRevision();
+ assertEquals(revision, 1);
+ }
+
+ public void testGetUUID() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ /*
+ * Test UUID
+ * TODO: Test for actual UUID once test dump file has
+ * fixed UUID
+ */
+ assertNotNull(session.getReposUUID());
+ }
+
+ public void testGetUrl() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ assertEquals(getTestRepoUrl(), session.getSessionUrl());
+ }
+
+ public void testGetRootUrl() throws Exception
+ {
+ ISVNRemote session = getSession();
+ session.reparent(session.getSessionUrl() + "/A/B/E");
+ assertEquals(getTestRepoUrl(), session.getReposRootUrl());
+ }
+
+ public void testGetUrl_viaSVNClient() throws Exception
+ {
+ ISVNRemote session = client.openRemoteSession(getTestRepoUrl());
+
+ assertEquals(getTestRepoUrl(), session.getSessionUrl());
+ }
+
+ public void testGetUrl_viaSVNClientWorkingCopy() throws Exception
+ {
+ ISVNRemote session = client.openRemoteSession(thisTest.getWCPath());
+
+ assertEquals(getTestRepoUrl(), session.getSessionUrl());
+ }
+
+ public void testReparent() throws Exception
+ {
+ ISVNRemote session = getSession();
+ String newUrl = session.getSessionUrl() + "/A/B/E";
+ session.reparent(newUrl);
+ assertEquals(newUrl, session.getSessionUrl());
+ }
+
+ public void testGetRelativePath() throws Exception
+ {
+ ISVNRemote session = getSession();
+ String baseUrl = session.getSessionUrl() + "/A/B/E";
+ session.reparent(baseUrl);
+
+ String relPath = session.getSessionRelativePath(baseUrl + "/alpha");
+ assertEquals("alpha", relPath);
+
+ relPath = session.getReposRelativePath(baseUrl + "/beta");
+ assertEquals("A/B/E/beta", relPath);
+ }
+
+ public void testGetCommitEditor() throws Exception
+ {
+ ISVNRemote session = getSession();
+ session.getCommitEditor(null, null, null, false);
+ }
+
+ public void testDisposeCommitEditor() throws Exception
+ {
+ ISVNRemote session = getSession();
+ session.getCommitEditor(null, null, null, false);
+ session.dispose();
+ }
+
+ public void testHasCapability() throws Exception
+ {
+ ISVNRemote session = getSession();
+ assert(session.hasCapability(ISVNRemote.Capability.depth));
+ }
+
+ public void testChangeRevpropNoAtomic() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ boolean atomic =
+ session.hasCapability(ISVNRemote.Capability.atomic_revprops);
+
+ if (atomic)
+ return;
+
+ boolean exceptioned = false;
+ try
+ {
+ byte[] oldValue = "bumble".getBytes(UTF8);
+ byte[] newValue = "bee".getBytes(UTF8);
+ session.changeRevisionProperty(1, "svn:author",
+ oldValue, newValue);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ exceptioned = true;
+ }
+ assert(exceptioned);
+ }
+
+ public void testChangeRevpropAtomic() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ boolean atomic =
+ session.hasCapability(ISVNRemote.Capability.atomic_revprops);
+
+ if (!atomic)
+ return;
+
+ byte[] oldValue = client.revProperty(getTestRepoUrl(), "svn:author",
+ Revision.getInstance(1));
+ byte[] newValue = "rayjandom".getBytes(UTF8);
+ try
+ {
+ session.changeRevisionProperty(1, "svn:author",
+ oldValue, newValue);
+ }
+ catch (ClientException ex)
+ {
+ ClientException.ErrorMessage error = null;
+ for (ClientException.ErrorMessage m : ex.getAllMessages())
+ if (!m.isGeneric()) {
+ error = m;
+ break;
+ }
+
+ if (error == null)
+ fail("Failed with no error message");
+
+ if (error.getCode() != 175002 && // SVN_ERR_RA_DAV_REQUEST_FAILED
+ error.getCode() != 165006) // SVN_ERR_REPOS_DISABLED_FEATURE
+ fail(error.getMessage());
+
+ return;
+ }
+
+ byte[] check = client.revProperty(getTestRepoUrl(), "svn:author",
+ Revision.getInstance(1));
+ assertTrue(Arrays.equals(check, newValue));
+ }
+
+ public void testGetRevpropList() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ Map<String, byte[]> proplist = session.getRevisionProperties(1);
+ assertTrue(Arrays.equals(proplist.get("svn:author"),
+ USERNAME.getBytes(UTF8)));
+ }
+
+ public void testGetRevprop() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] propval = session.getRevisionProperty(1, "svn:author");
+ assertTrue(Arrays.equals(propval, USERNAME.getBytes(UTF8)));
+ }
+
+ public void testGetFile() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ ByteArrayOutputStream contents = new ByteArrayOutputStream();
+ HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+ properties.put("fakename", "fakecontents".getBytes(UTF8));
+ long fetched_rev =
+ session.getFile(Revision.SVN_INVALID_REVNUM, "A/B/lambda",
+ contents, properties);
+ assertEquals(fetched_rev, 1);
+ assertEquals(contents.toString("UTF-8"),
+ "This is the file 'lambda'.");
+ for (Map.Entry<String, byte[]> e : properties.entrySet()) {
+ final String key = e.getKey();
+ assertTrue(key.startsWith("svn:entry:")
+ || key.startsWith("svn:wc:"));
+ }
+ }
+
+ public void testGetDirectory() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ HashMap<String, DirEntry> dirents = new HashMap<String, DirEntry>();
+ dirents.put("E", null);
+ dirents.put("F", null);
+ dirents.put("lambda", null);
+ HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+ properties.put("fakename", "fakecontents".getBytes(UTF8));
+ long fetched_rev =
+ session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/B",
+ DirEntry.Fields.all, dirents, properties);
+ assertEquals(fetched_rev, 1);
+ assertEquals(dirents.get("E").getPath(), "E");
+ assertEquals(dirents.get("F").getPath(), "F");
+ assertEquals(dirents.get("lambda").getPath(), "lambda");
+ for (Map.Entry<String, byte[]> e : properties.entrySet()) {
+ final String key = e.getKey();
+ assertTrue(key.startsWith("svn:entry:")
+ || key.startsWith("svn:wc:"));
+ }
+ }
+
+ private static final class CommitContext implements CommitCallback
+ {
+ public final ISVNEditor editor;
+ public CommitContext(ISVNRemote session, String logstr,
+ ISVNEditor.ProvideBaseCallback getBase,
+ ISVNEditor.ProvidePropsCallback getProps,
+ ISVNEditor.GetNodeKindCallback getKind)
+ throws ClientException
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ byte[] log = (logstr == null
+ ? new byte[0]
+ : logstr.getBytes(UTF8));
+ HashMap<String, byte[]> revprops = new HashMap<String, byte[]>();
+ revprops.put("svn:log", log);
+
+ // Run the getCommitEditor overloads through their paces, too.
+ if (getBase == null && getProps == null && getKind == null)
+ editor = session.getCommitEditor(revprops, this, null, false);
+ else
+ editor = session.getCommitEditor(revprops, this, null, false,
+ getBase, getProps, getKind);
+ }
+
+ public CommitContext(ISVNRemote session, String logstr)
+ throws ClientException
+ {
+ this(session, logstr, null, null, null);
+ }
+
+ public void commitInfo(CommitInfo info) { this.info = info; }
+ public long getRevision() { return info.getRevision(); }
+
+ private CommitInfo info;
+ }
+
+ private static final class EditorCallbacks
+ {
+ private final String wcpath;
+ private final long revision;
+ private final Map<String, byte[]> props;
+ private final NodeKind kind;
+
+ public EditorCallbacks(String wcpath, long revision,
+ Map<String, byte[]> props,
+ NodeKind kind)
+ {
+ this.wcpath = wcpath;
+ this.revision = revision;
+ this.props = props;
+ this.kind = kind;
+ }
+
+ public final ISVNEditor.ProvideBaseCallback getBase =
+ new ISVNEditor.ProvideBaseCallback()
+ {
+ public ISVNEditor.ProvideBaseCallback.ReturnValue
+ getContents(String relpath)
+ {
+ try {
+ return new ISVNEditor.ProvideBaseCallback.ReturnValue(
+ new FileInputStream(wcpath + relpath), revision);
+ } catch (java.io.FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ public final ISVNEditor.ProvidePropsCallback getProps =
+ new ISVNEditor.ProvidePropsCallback()
+ {
+ public ISVNEditor.ProvidePropsCallback.ReturnValue
+ getProperties(String relpath)
+ {
+ return new ISVNEditor.ProvidePropsCallback.ReturnValue(
+ props, revision);
+ }
+ };
+
+ public final ISVNEditor.GetNodeKindCallback getKind =
+ new ISVNEditor.GetNodeKindCallback()
+ {
+ public NodeKind getKind(String relpath, long revision)
+ {
+ return kind;
+ }
+ };
+ };
+
+ private void testEditorCopy(EditorCallbacks cb) throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ (cb != null
+ ? new CommitContext(session, "Copy A/B/lambda -> A/B/omega",
+ cb.getBase, cb.getProps, cb.getKind)
+ : new CommitContext(session, "Copy A/B/lambda -> A/B/omega"));
+
+ try {
+ // FIXME: alter dir A/B first
+ cc.editor.copy("A/B/lambda", 1, "A/B/omega",
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/lambda",
+ Revision.SVN_INVALID_REVNUM));
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/omega",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorCopy() throws Exception
+ {
+ testEditorCopy(null);
+ }
+
+ public void testEditorCopy_WithCallbacks() throws Exception
+ {
+ testEditorCopy(new EditorCallbacks(thisTest.getWCPath(), 1L,
+ new HashMap<String, byte[]>(),
+ NodeKind.file));
+ }
+
+ public void testEditorMove() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ new CommitContext(session, "Move A/B/lambda -> A/B/omega");
+
+ try {
+ // FIXME: alter dir A/B first
+ cc.editor.move("A/B/lambda", 1, "A/B/omega",
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.none,
+ session.checkPath("A/B/lambda",
+ Revision.SVN_INVALID_REVNUM));
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/omega",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorDelete() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ new CommitContext(session, "Delete all greek files");
+
+ String[] filePaths = { "iota",
+ "A/mu",
+ "A/B/lambda",
+ "A/B/E/alpha",
+ "A/B/E/beta",
+ "A/D/gamma",
+ "A/D/G/pi",
+ "A/D/G/rho",
+ "A/D/G/tau",
+ "A/D/H/chi",
+ "A/D/H/omega",
+ "A/D/H/psi" };
+
+ try {
+ // FIXME: alter a bunch of dirs first
+ for (String path : filePaths)
+ cc.editor.delete(path, 1);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ for (String path : filePaths)
+ assertEquals(NodeKind.none,
+ session.checkPath(path, Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorMkdir() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc = new CommitContext(session, "Make hebrew dir");
+
+ try {
+ // FIXME: alter dir . first
+ cc.editor.addDirectory("ALEPH",
+ new ArrayList<String>(),
+ new HashMap<String, byte[]>(),
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.dir,
+ session.checkPath("ALEPH",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ private void testEditorSetDirProps(EditorCallbacks cb) throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] ignoreval = "*.pyc\n.gitignore\n".getBytes(UTF8);
+ byte[] binaryval = new byte[]{(byte)0, (byte)13, (byte)255, (byte)8,
+ (byte)127, (byte)128, (byte)129};
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ props.put("svn:ignore", ignoreval);
+ props.put("binaryprop", binaryval);
+
+ CommitContext cc =
+ (cb != null
+ ? new CommitContext(session, "Add svn:ignore and binaryprop",
+ cb.getBase, cb.getProps, cb.getKind)
+ : new CommitContext(session, "Add svn:ignore and binaryprop"));
+ try {
+ cc.editor.alterDirectory("", 1, null, props);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertTrue(Arrays.equals(ignoreval,
+ client.propertyGet(session.getSessionUrl(),
+ "svn:ignore",
+ Revision.HEAD,
+ Revision.HEAD)));
+ assertTrue(Arrays.equals(binaryval,
+ client.propertyGet(session.getSessionUrl(),
+ "binaryprop",
+ Revision.HEAD,
+ Revision.HEAD)));
+ }
+
+ public void testEditorSetDirProps() throws Exception
+ {
+ testEditorSetDirProps(null);
+ }
+
+ public void testEditorSetDirProps_WithCallbacks() throws Exception
+ {
+ testEditorSetDirProps(new EditorCallbacks(thisTest.getWCPath(), 1L,
+ new HashMap<String, byte[]>(),
+ NodeKind.dir));
+ }
+
+ private static byte[] SHA1(byte[] text) throws NoSuchAlgorithmException
+ {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ return md.digest(text);
+ }
+
+ public void testEditorAddFile() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] eolstyle = "native".getBytes(UTF8);
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ props.put("svn:eol-style", eolstyle);
+
+ byte[] contents = "This is file 'xi'.".getBytes(UTF8);
+ Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
+ ByteArrayInputStream stream = new ByteArrayInputStream(contents);
+
+ CommitContext cc = new CommitContext(session, "Add A/xi");
+ try {
+ // FIXME: alter dir A first
+ cc.editor.addFile("A/xi", hash, stream, props,
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.file,
+ session.checkPath("A/xi",
+ Revision.SVN_INVALID_REVNUM));
+
+ byte[] propval = client.propertyGet(session.getSessionUrl() + "/A/xi",
+ "svn:eol-style",
+ Revision.HEAD,
+ Revision.HEAD);
+ assertTrue(Arrays.equals(eolstyle, propval));
+ }
+
+ public void testEditorDeleteFileProps() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ client.propertySetRemote(
+ thisTest.getUrl() + "/iota", 1L,
+ "name", "value".getBytes(UTF8),
+ new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> elements) {
+ return "Set property 'name' to 'value'";
+ }
+ }, false, null, null);
+
+ ISVNRemote session = getSession();
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ assertEquals(2L, session.getFile(Revision.SVN_INVALID_REVNUM, "iota",
+ null, props));
+
+ int propcount = 0;
+ for (Map.Entry<String, byte[]> e : props.entrySet()) {
+ final String key = e.getKey();
+ if (key.startsWith("svn:entry:") || key.startsWith("svn:wc:"))
+ continue;
+ ++propcount;
+ }
+ assertEquals(1, propcount);
+
+ CommitContext cc = new CommitContext(session, "Remove all props");
+ try {
+ props.clear();
+ cc.editor.alterFile("iota", 2L, null, null, props);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(3L, session.getFile(Revision.SVN_INVALID_REVNUM, "iota",
+ null, props));
+ propcount = 0;
+ for (Map.Entry<String, byte[]> e : props.entrySet()) {
+ final String key = e.getKey();
+ if (key.startsWith("svn:entry:") || key.startsWith("svn:wc:"))
+ continue;
+ ++propcount;
+ }
+ assertEquals(0, propcount);
+ }
+
+ private void testEditorSetFileContents(EditorCallbacks cb) throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] contents = "This is modified file 'alpha'.".getBytes(UTF8);
+ Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
+ ByteArrayInputStream stream = new ByteArrayInputStream(contents);
+
+ CommitContext cc =
+ (cb != null
+ ? new CommitContext(session, "Change contents of A/B/E/alpha",
+ cb.getBase, cb.getProps, cb.getKind)
+ : new CommitContext(session, "Change contents of A/B/E/alpha"));
+ try {
+ cc.editor.alterFile("A/B/E/alpha", 1, hash, stream, null);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ ByteArrayOutputStream checkcontents = new ByteArrayOutputStream();
+ client.streamFileContent(session.getSessionUrl() + "/A/B/E/alpha",
+ Revision.HEAD, Revision.HEAD, checkcontents);
+ assertTrue(Arrays.equals(contents, checkcontents.toByteArray()));
+ }
+
+ public void testEditorSetFileContents() throws Exception
+ {
+ testEditorSetFileContents(null);
+ }
+
+ public void testEditorSetFileContents_WithCallbacks() throws Exception
+ {
+ testEditorSetFileContents(new EditorCallbacks(thisTest.getWCPath(), 1L,
+ new HashMap<String, byte[]>(),
+ NodeKind.file));
+ }
+
+ // Sanity check so that we don't forget about unimplemented methods.
+ public void testEditorNotImplemented() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ // ArrayList<ISVNEditor.RotatePair> rotation =
+ // new ArrayList<ISVNEditor.RotatePair>();
+
+ CommitContext cc = new CommitContext(session, "not implemented");
+ try {
+ String exmsg;
+
+ try {
+ exmsg = "";
+ cc.editor.addSymlink("", "", props, 1);
+ } catch (RuntimeException ex) {
+ exmsg = ex.getMessage();
+ }
+ assertEquals("Not implemented: CommitEditor.addSymlink", exmsg);
+
+ try {
+ exmsg = "";
+ cc.editor.alterSymlink("", 1, "", null);
+ } catch (RuntimeException ex) {
+ exmsg = ex.getMessage();
+ }
+ assertEquals("Not implemented: CommitEditor.alterSymlink", exmsg);
+
+ } finally {
+ cc.editor.dispose();
+ }
+ }
+
+ private static final class LogMsg
+ {
+ public Set<ChangePath> changedPaths;
+ public long revision;
+ public Map<String, byte[]> revprops;
+ public boolean hasChildren;
+ }
+
+ private static final class LogReceiver implements LogMessageCallback
+ {
+ public final ArrayList<LogMsg> logs = new ArrayList<LogMsg>();
+
+ public void singleMessage(Set<ChangePath> changedPaths,
+ long revision,
+ Map<String, byte[]> revprops,
+ boolean hasChildren)
+ {
+ LogMsg msg = new LogMsg();
+ msg.changedPaths = changedPaths;
+ msg.revision = revision;
+ msg.revprops = revprops;
+ msg.hasChildren = hasChildren;
+ logs.add(msg);
+ }
+ }
+
+ public void testGetLog() throws Exception
+ {
+ ISVNRemote session = getSession();
+ LogReceiver receiver = new LogReceiver();
+
+ session.getLog(null,
+ Revision.SVN_INVALID_REVNUM,
+ Revision.SVN_INVALID_REVNUM,
+ 0, false, false, false, null,
+ receiver);
+ assertEquals(1, receiver.logs.size());
+ assertTrue(receiver.logs.get(0).revprops.size() > 0);
+
+ receiver.logs.clear();
+ session.reparent(getTestRepoUrl() + "/A");
+ session.getLog(null,
+ Revision.SVN_INVALID_REVNUM,
+ 0, 0, false, false, false, null,
+ receiver);
+ assertEquals(2, receiver.logs.size());
+ assertTrue(receiver.logs.get(0).revprops.size() > 0);
+ }
+
+ public void testGetLogMissing() throws Exception
+ {
+ ISVNRemote session = getSession();
+ LogReceiver receiver = new LogReceiver();
+
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("X");
+
+ boolean exception = false;
+ try {
+ session.getLog(paths,
+ Revision.SVN_INVALID_REVNUM,
+ Revision.SVN_INVALID_REVNUM,
+ 0, false, false, false, null,
+ receiver);
+ } catch (ClientException ex) {
+ assertEquals("Filesystem has no item",
+ ex.getAllMessages().get(0).getMessage());
+ exception = true;
+ }
+
+ assertEquals(0, receiver.logs.size());
+ assertTrue(exception);
+ }
+
+ public void testConfigHandler() throws Exception
+ {
+ ConfigEvent handler = new ConfigEvent()
+ {
+ public void onLoad(ISVNConfig cfg)
+ {
+ //System.out.println("config:");
+ onecat(cfg.config());
+ //System.out.println("servers:");
+ onecat(cfg.servers());
+ }
+
+ private void onecat(ISVNConfig.Category cat)
+ {
+ for (String sec : cat.sections()) {
+ //System.out.println(" [" + sec + "]");
+ ISVNConfig.Enumerator en = new ISVNConfig.Enumerator()
+ {
+ public void option(String name, String value)
+ {
+ //System.out.println(" " + name
+ // + " = " + value);
+ }
+ };
+ cat.enumerate(sec, en);
+ }
+ }
+
+ };
+
+ ISVNRemote session;
+ try
+ {
+ if (DefaultAuthn.useDeprecated())
+ session = new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDeprecated(),
+ null, handler, null)
+ .openRemoteSession(getTestRepoUrl());
+ else
+ session = new RemoteFactory(
+ super.conf.getAbsolutePath(),
+ USERNAME, null, // Do not set default password.
+ DefaultAuthn.getDefault(),
+ null, handler, null)
+ .openRemoteSession(getTestRepoUrl());
+ }
+ catch (ClientException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ session.getLatestRevision();
+ }
+
+ private static class RemoteStatusReceiver implements RemoteStatus
+ {
+ static class StatInfo implements Comparable<StatInfo>
+ {
+ public String relpath = null;
+ public char kind = ' '; // F, D, L
+ public boolean textChanged = false;
+ public boolean propsChanged = false;
+ public boolean deleted = false;
+ public Entry info = null;
+
+ StatInfo(String relpath, char kind, boolean added)
+ {
+ this.relpath = relpath;
+ this.kind = kind;
+ this.deleted = !added;
+ }
+
+ StatInfo(String relpath, char kind,
+ boolean textChanged, boolean propsChanged,
+ Entry info)
+ {
+ this.relpath = relpath;
+ this.kind = kind;
+ this.textChanged = textChanged;
+ this.propsChanged = propsChanged;
+ this.info = info;
+ }
+
+ @Override
+ public boolean equals(Object statinfo)
+ {
+ final StatInfo that = (StatInfo)statinfo;
+ return this.relpath.equals(that.relpath);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return this.relpath.hashCode();
+ }
+
+ @Override
+ public int compareTo(StatInfo that)
+ {
+ return this.relpath.compareTo(that.relpath);
+ }
+ }
+
+ private boolean debug;
+
+ public RemoteStatusReceiver()
+ {
+ this.debug = false;
+ }
+
+ public RemoteStatusReceiver(boolean debug)
+ {
+ this.debug = debug;
+ }
+
+ public ArrayList<StatInfo> status = new ArrayList<StatInfo>();
+
+ public void addedDirectory(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (dir) " +
+ relativePath);
+ status.add(new StatInfo(relativePath, 'D', true));
+ }
+
+ public void addedFile(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (file) "
+ + relativePath);
+ status.add(new StatInfo(relativePath, 'F', true));
+ }
+
+ public void addedSymlink(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (link) "
+ + relativePath);
+ status.add(new StatInfo(relativePath, 'L', true));
+ }
+
+ public void modifiedDirectory(String relativePath,
+ boolean childrenModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (childrenModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (dir) " + relativePath);
+ status.add(new StatInfo(relativePath, 'D',
+ childrenModified, propsModified,
+ nodeInfo));
+ }
+
+ public void modifiedFile(String relativePath,
+ boolean textModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (textModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (file) " + relativePath);
+ status.add(new StatInfo(relativePath, 'F',
+ textModified, propsModified,
+ nodeInfo));
+ }
+
+ public void modifiedSymlink(String relativePath,
+ boolean targetModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (targetModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (link) " + relativePath);
+ status.add(new StatInfo(relativePath, 'L',
+ targetModified, propsModified,
+ nodeInfo));
+
+ }
+
+ public void deleted(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: D "
+ + relativePath);
+ status.add(new StatInfo(relativePath, ' ', false));
+ }
+ }
+
+ public void testSimpleStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 0, Depth.infinity, true, null);
+ assertEquals(1, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+ assertEquals(21, receiver.status.size());
+ session.checkPath("", Revision.SVN_INVALID_REVNUM);
+ }
+
+ public void testTextchangeStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Content change on A/B/E/alpha";
+ }
+ };
+
+ File alpha = new File(thisTest.getWorkingCopy(), "A/B/E/alpha");
+ FileOutputStream writer = new FileOutputStream(alpha);
+ writer.write("changed alpha text".getBytes());
+ writer.close();
+ client.commit(thisTest.getWCPathSet(), Depth.infinity, false, false,
+ null, null, cmcb, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+
+ assertEquals(5, receiver.status.size());
+
+ // ra_serf returns the entries in inverted order compared to ra_local.
+ Collections.sort(receiver.status);
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(4);
+ assertEquals("A/B/E/alpha", mod.relpath);
+ assertEquals('F', mod.kind);
+ assertEquals("Text Changed", true, mod.textChanged);
+ assertEquals("Props Changed", false, mod.propsChanged);
+ assertEquals("Node Deleted", false, mod.deleted);
+ assertEquals(2, mod.info.getCommittedRevision());
+ }
+
+ public void testPropchangeStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Property change on A/D/gamma";
+ }
+ };
+ client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
+ 1L, "foo", "bar".getBytes(), cmcb,
+ false, null, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+
+ assertEquals(4, receiver.status.size());
+
+ // ra_serf returns the entries in inverted order compared to ra_local.
+ Collections.sort(receiver.status);
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(3);
+ assertEquals("A/D/gamma", mod.relpath);
+ assertEquals('F', mod.kind);
+ assertEquals("TextChanged", false, mod.textChanged);
+ assertEquals("Props Changed", true, mod.propsChanged);
+ assertEquals("Node Deleted", false, mod.deleted);
+ assertEquals(2, mod.info.getCommittedRevision());
+ }
+
+ public void testDeletedStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Delete A/mu";
+ }
+ };
+ HashSet<String> paths = new HashSet<String>(1);
+ paths.add(getTestRepoUrl() + "/A/mu");
+ client.remove(paths, false, false, null, cmcb, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+ assertEquals(3, receiver.status.size());
+
+ // ra_serf returns the entries in inverted order compared to ra_local.
+ Collections.sort(receiver.status);
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(2);
+ assertEquals("A/mu", mod.relpath);
+ assertEquals(' ', mod.kind);
+ assertEquals(false, mod.textChanged);
+ assertEquals(false, mod.propsChanged);
+ assertEquals(true, mod.deleted);
+ }
+
+ public void testTrivialMergeinfo() throws Exception
+ {
+ ISVNRemote session = getSession();
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("");
+
+ Map<String, Mergeinfo> catalog =
+ session.getMergeinfo(paths, 1L, Mergeinfo.Inheritance.explicit,
+ false);
+ assertEquals(null, catalog);
+ }
+
+ public void testBranchMergeinfo() throws Exception
+ {
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "testBranchMergeinfo";
+ }
+ };
+
+ ISVNRemote session = getSession();
+
+ // Create a branch
+ ArrayList<CopySource> dirA = new ArrayList<CopySource>(1);
+ dirA.add(new CopySource(getTestRepoUrl() + "/A",
+ Revision.HEAD, Revision.HEAD));
+ client.copy(dirA, getTestRepoUrl() + "/Abranch",
+ false, false, true, null, cmcb, null);
+
+ // Check mergeinfo on new branch
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("Abranch");
+ Map<String, Mergeinfo> catalog =
+ session.getMergeinfo(paths, 2L, Mergeinfo.Inheritance.explicit,
+ false);
+ assertEquals(null, catalog);
+
+ // Modify source and merge to branch
+ client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
+ 2L, "foo", "bar".getBytes(), cmcb,
+ false, null, null);
+ client.update(thisTest.getWCPathSet(), Revision.HEAD, Depth.infinity,
+ false, false, true, false);
+ client.merge(getTestRepoUrl() + "/A", Revision.HEAD, null,
+ thisTest.getWCPath() + "/Abranch", false, Depth.infinity,
+ false, false, false, false);
+ client.commit(thisTest.getWCPathSet(), Depth.infinity, false, false,
+ null, null, cmcb, null);
+
+ // Check inherited mergeinfo on updated branch
+ paths.set(0, "Abranch/mu");
+ catalog = session.getMergeinfo(paths, 4L,
+ Mergeinfo.Inheritance.nearest_ancestor,
+ false);
+ assertEquals(1, catalog.size());
+ List<RevisionRange> ranges =
+ catalog.get("Abranch/mu").getRevisions("/A/mu");
+ assertEquals(1, ranges.size());
+ assertEquals("1-3", ranges.get(0).toString());
+ }
+
+ public void testGetLocations() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ Long expected = new Long(1L);
+ ArrayList<Long> revs = new ArrayList<Long>(3);
+ revs.add(new Long(0L));
+ revs.add(expected);
+
+ Map<Long, String> locs = session.getLocations("A", 1, revs);
+
+ assertEquals(1, locs.size());
+ assertTrue(locs.containsKey(expected));
+ assertEquals("/A", locs.get(expected));
+ }
+
+ public void testGetLocationSegments() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ List<ISVNRemote.LocationSegment> result =
+ session.getLocationSegments("A", 1,
+ Revision.SVN_INVALID_REVNUM,
+ Revision.SVN_INVALID_REVNUM);
+ assertEquals(1, result.size());
+ ISVNRemote.LocationSegment seg = result.get(0);
+ assertEquals("A", seg.getPath());
+ assertEquals(1, seg.getStartRevision());
+ assertEquals(1, seg.getEndRevision());
+ }
+
+ public void testGetFileRevisions() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ List<ISVNRemote.FileRevision> result =
+ session.getFileRevisions("iota", 0, 1, true);
+ assertEquals(1, result.size());
+ ISVNRemote.FileRevision rev = result.get(0);
+ assertEquals("/iota", rev.getPath());
+ assertFalse(rev.isResultOfMerge());
+ assertTrue(rev.hasTextDelta());
+ }
+
+ // This test is a result of a threading bug that was identified in
+ // serf-1.3.2 and earlier. The net result was that opening two RA
+ // sessions to an https:// URL in two parallel threads would cause
+ // a crash in serf, due to the OpenSSL library not being
+ // initialized in a single-threaded context.
+ //
+ // The problem does not appear to exist with other RA methods, but
+ // the test is here just in case someone is actually pedantic
+ // enough to test JavaHL with an HTTPS setup.
+ public void testParallelOpen() throws Exception
+ {
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ ISVNRemote session = null;
+ try {
+ session = getSession();
+ assertEquals(1, session.getLatestRevision());
+ }
+ catch (ClientException ex) {
+ throw new RuntimeException(ex);
+ }
+ finally {
+ if (session != null)
+ session.dispose();
+ }
+ }
+ };
+
+ Thread thread1 = new Thread(runnable);
+ Thread thread2 = new Thread(runnable);
+
+ thread1.start();
+ thread2.start();
+
+ thread1.join();
+ thread2.join();
+ }
+}