diff options
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.java | 1444 |
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(); + } +} |