Author: rhuijben
Date: Sun Mar  8 01:05:14 2015
New Revision: 1664938

URL: http://svn.apache.org/r1664938
Log:
In JavaHL: Properly wrap Java exceptions as svn_error_t * when returning from
many callbacks and add the wrapped exception (when not cleared by Subversion)
as cause to the returned exception.

This avoids keeping the JVM in the semi-broken 'exception' state until we
somehow return and also makes it possible to do things in further callbacks
(which we previously skipped by just returning an SVN_NO_ERROR on the still
 pending exceptions).

Before this patch a status call that returned an exception in the first
callback would continue the entire status walk and only after returning
it would return the exception, potentially breaking the api contract
of the function.

After this patch an error is returned in Subversion... which builds
the usual error chain in subversion.... Then when the call returns
in JavaHL the cause of the ClientException is set to the originally
raised exception in the callback and intermediate errors are added
if necessary.

* subversion/bindings/javahl/native/BlameCallback.cpp
  (BlameCallback::singleLine): Update exit handling.

* subversion/bindings/javahl/native/CommitCallback.cpp
  (CommitCallback::commitInfo): Update exit handling.

* subversion/bindings/javahl/native/CommitMessage.cpp
  (CommitMessage::getCommitMessage): Return svn_error_t * on error instead of
    trying the commit with no log message.

* subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
  (DiffSummaryReceiver::onSummary): Update exit handling.

* subversion/bindings/javahl/native/InfoCallback.cpp
  (InfoCallback::singleInfo): Update exit handling.

* subversion/bindings/javahl/native/JNIUtil.cpp
  (SVN_ERR_JAVAHL_WRAPPED): New constant.
  (assemble_error_message): Don't try to write standard text for
    SVN_ERR_JAVAHL_WRAPPED.
  (JNIUtil::wrappedHandleSVNError): If there is no jcause passed, check if we
    have a Java exception we can use stored in the error.
  (WrapExceptionTag): New variable.
  (WrappedException): New class.
  (JNIUtil::wrapJavaException,
   JNIUtil::unwrapJavaException): New functions.

* subversion/bindings/javahl/native/JNIUtil.h
  (JNIUtil::wrapJavaException,
   JNIUtil::unwrapJavaException): New functions.
  (POP_AND_RETURN_EXCEPTION_AS_SVNERROR): New define.

* subversion/bindings/javahl/native/ListCallback.cpp
  (ListCallback::doList): Update exit handling.

* subversion/bindings/javahl/native/LogMessageCallback.cpp
  (LogMessageCallback::singleMessage): Update exit handling.

* subversion/bindings/javahl/native/StatusCallback.cpp
  (StatusCallback::doStatus): Update exit handling.

* 
subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
  New file.

* subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java
  (SVNTestSuite.suite): Add ExceptionTests.

Added:
    
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
   (with props)
Modified:
    subversion/trunk/subversion/bindings/javahl/native/BlameCallback.cpp
    subversion/trunk/subversion/bindings/javahl/native/CommitCallback.cpp
    subversion/trunk/subversion/bindings/javahl/native/CommitMessage.cpp
    subversion/trunk/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
    subversion/trunk/subversion/bindings/javahl/native/InfoCallback.cpp
    subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
    subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h
    subversion/trunk/subversion/bindings/javahl/native/ListCallback.cpp
    subversion/trunk/subversion/bindings/javahl/native/LogMessageCallback.cpp
    subversion/trunk/subversion/bindings/javahl/native/StatusCallback.cpp
    
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java

Modified: subversion/trunk/subversion/bindings/javahl/native/BlameCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/BlameCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/BlameCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/BlameCallback.cpp Sun 
Mar  8 01:05:14 2015
@@ -128,8 +128,6 @@ BlameCallback::singleLine(svn_revnum_t s
   env->CallVoidMethod(m_callback, mid, (jlong)line_no, (jlong)revision,
                       jrevProps, (jlong)mergedRevision, jmergedRevProps,
                       jmergedPath, jline, (jboolean)localChange);
-  // No need to check for an exception here, because we return anyway.
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }

Modified: subversion/trunk/subversion/bindings/javahl/native/CommitCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/CommitCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/CommitCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/CommitCallback.cpp Sun 
Mar  8 01:05:14 2015
@@ -96,10 +96,8 @@ CommitCallback::commitInfo(const svn_com
     POP_AND_RETURN(SVN_NO_ERROR);
 
   env->CallVoidMethod(m_callback, sm_mid, jcommitInfo);
-  // No need to check for an exception here, because we return anyway.
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }
 
 

Modified: subversion/trunk/subversion/bindings/javahl/native/CommitMessage.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/CommitMessage.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/CommitMessage.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/CommitMessage.cpp Sun 
Mar  8 01:05:14 2015
@@ -106,7 +106,7 @@ CommitMessage::getCommitMessage(const ch
                                                     midCallback,
                                                     CreateJ::Set(jitems));
   if (JNIUtil::isJavaExceptionThrown())
-    POP_AND_RETURN(SVN_NO_ERROR);
+    POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 
   if (jmessage != NULL)
     {

Modified: 
subversion/trunk/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp 
Sun Mar  8 01:05:14 2015
@@ -117,8 +117,5 @@ DiffSummaryReceiver::onSummary(const svn
 
   // Invoke the Java DiffSummaryReceiver callback.
   env->CallVoidMethod(m_receiver, callback, jDiffSummary);
-  // We return whether an exception was thrown or not.
-
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }

Modified: subversion/trunk/subversion/bindings/javahl/native/InfoCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/InfoCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/InfoCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/InfoCallback.cpp Sun Mar 
 8 01:05:14 2015
@@ -95,8 +95,6 @@ InfoCallback::singleInfo(const char *pat
     POP_AND_RETURN(SVN_NO_ERROR);
 
   env->CallVoidMethod(m_callback, mid, jinfo2);
-  // Return SVN_NO_ERROR here regardless of an exception or not.
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp Sun Mar  8 
01:05:14 2015
@@ -77,6 +77,11 @@ bool JNIUtil::g_initException;
 int JNIUtil::g_logLevel = JNIUtil::noLog;
 std::ofstream JNIUtil::g_logStream;
 
+/* The error code we will use to signal a Java exception */
+static const apr_status_t
+SVN_ERR_JAVAHL_WRAPPED = SVN_ERR_MALFUNC_CATEGORY_START
+                         + SVN_ERR_CATEGORY_SIZE - 10;
+
 /**
  * Return the JNI environment to use
  * @return the JNI environment
@@ -446,7 +451,8 @@ ErrorMessageStack assemble_error_message
     {
       /* When we're recursing, don't repeat the top-level message if its
        * the same as before. */
-      if (depth == 0 || err->apr_err != parent_apr_err)
+      if ((depth == 0 || err->apr_err != parent_apr_err)
+          && err->apr_err != SVN_ERR_JAVAHL_WRAPPED)
         {
           const char *message;
           /* Is this a Subversion-specific error code? */
@@ -586,6 +592,9 @@ void JNIUtil::wrappedHandleSVNError(svn_
 #endif
 #endif
 
+  if (!jcause)
+    jcause = JNIUtil::unwrapJavaException(err);
+
   // Much of the following is stolen from throwNativeException().  As much as
   // we'd like to call that function, we need to do some manual stack
   // unrolling, so it isn't feasible.
@@ -1062,3 +1071,77 @@ svn_error_t *JNIUtil::preprocessPath(con
 
   return NULL;
 }
+
+/* Tag to use on the apr_pool_t to store a WrappedException reference */
+static const char *WrapExceptionTag = "org.apache.subversion.JavaHL.svnerror";
+
+class WrappedException
+{
+  JNIEnv *m_env;
+  jthrowable m_exception;
+public:
+  WrappedException(JNIEnv *env)
+  {
+    m_env = env;
+
+    // Fetch exception inside local frame
+    jthrowable exceptionObj = env->ExceptionOccurred();
+
+    // Now clear exception status
+    env->ExceptionClear();
+
+    // As adding a reference in exception state fails
+    m_exception = static_cast<jthrowable>(env->NewGlobalRef(exceptionObj));
+  }
+
+  static jthrowable get_exception(apr_pool_t *pool)
+  {
+      void *data;
+      if (! apr_pool_userdata_get(&data, WrapExceptionTag, pool))
+      {
+          WrappedException *we = reinterpret_cast<WrappedException *>(data);
+
+          if (we)
+          {
+              // Create reference in local frame, as the pool will be cleared
+              return static_cast<jthrowable>(
+                            we->m_env->NewLocalRef(we->m_exception));
+          }
+      }
+      return NULL;
+  }
+
+private:
+  ~WrappedException()
+  {
+      m_env->DeleteGlobalRef(m_exception);
+  }
+public:
+  static apr_status_t cleanup(void *data)
+  {
+    WrappedException *we = reinterpret_cast<WrappedException *>(data);
+
+    delete we;
+    return APR_SUCCESS;
+  }
+};
+
+svn_error_t* JNIUtil::wrapJavaException()
+{
+  if (!isExceptionThrown())
+    return SVN_NO_ERROR;
+
+  svn_error_t *err = svn_error_create(SVN_ERR_JAVAHL_WRAPPED, NULL,
+                                      "Wrapped Java Exception");
+  apr_pool_userdata_set(new WrappedException(getEnv()), WrapExceptionTag,
+                        WrappedException::cleanup, err->pool);
+  return err;
+}
+
+jthrowable JNIUtil::unwrapJavaException(const svn_error_t *err)
+{
+    if (!err)
+        return NULL;
+    return
+        WrappedException::get_exception(err->pool);
+}

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h Sun Mar  8 
01:05:14 2015
@@ -86,6 +86,9 @@ class JNIUtil
       return getEnv()->ExceptionCheck();
     }
 
+  static svn_error_t *wrapJavaException();
+  static jthrowable unwrapJavaException(const svn_error_t *err);
+
   static void handleAPRError(int error, const char *op);
 
   /**
@@ -265,6 +268,16 @@ class JNIUtil
     }                                   \
   while (0)
 
+#define POP_AND_RETURN_EXCEPTION_AS_SVNERROR()                            \
+  do                                                                      \
+    {                                                                     \
+      svn_error_t *svn__err_for_exception = JNIUtil::wrapJavaException(); \
+                                                                          \
+      env->PopLocalFrame(NULL);                                           \
+      return svn__err_for_exception;                                      \
+    }                                                                     \
+  while (0)
+
 
 /**
  * A useful macro.

Modified: subversion/trunk/subversion/bindings/javahl/native/ListCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/ListCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/ListCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/ListCallback.cpp Sun Mar 
 8 01:05:14 2015
@@ -113,10 +113,8 @@ ListCallback::doList(const char *path,
 
   // call the Java method
   env->CallVoidMethod(m_callback, mid, jdirentry, jlock);
-  // No need to check for exception here, because we'll just return anyway
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }
 
 /**

Modified: 
subversion/trunk/subversion/bindings/javahl/native/LogMessageCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/LogMessageCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/LogMessageCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/LogMessageCallback.cpp 
Sun Mar  8 01:05:14 2015
@@ -125,8 +125,6 @@ LogMessageCallback::singleMessage(svn_lo
                       (jlong)log_entry->revision,
                       jrevprops,
                       (jboolean)log_entry->has_children);
-  // No need to check for an exception here, because we return anyway.
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }

Modified: subversion/trunk/subversion/bindings/javahl/native/StatusCallback.cpp
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/StatusCallback.cpp?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/StatusCallback.cpp 
(original)
+++ subversion/trunk/subversion/bindings/javahl/native/StatusCallback.cpp Sun 
Mar  8 01:05:14 2015
@@ -101,11 +101,8 @@ StatusCallback::doStatus(const char *loc
     POP_AND_RETURN(SVN_NO_ERROR);
 
   env->CallVoidMethod(m_callback, mid, jPath, jStatus);
-  // We return here regardless of whether an exception is thrown or not,
-  // so we do not need to explicitly check for one.
 
-  env->PopLocalFrame(NULL);
-  return SVN_NO_ERROR;
+  POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
 }
 
 void

Added: 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java?rev=1664938&view=auto
==============================================================================
--- 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
 (added)
+++ 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
 Sun Mar  8 01:05:14 2015
@@ -0,0 +1,315 @@
+/**
+ * @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.callback.*;
+import org.apache.subversion.javahl.types.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+
+/**
+ * Tests the basic functionality of javahl binding (inspired by the
+ * tests in subversion/tests/cmdline/basic_tests.py).
+ */
+public class ExceptionTests extends SVNTests
+{
+    /**
+     * Base name of all our tests.
+     */
+    public final static String testName = "exception_test";
+
+    public ExceptionTests()
+    {
+        init();
+    }
+
+    public ExceptionTests(String name)
+    {
+        super(name);
+        init();
+    }
+
+    /**
+     * Initialize the testBaseName and the testCounter, if this is the
+     * first test of this class.
+     */
+    private void init()
+    {
+        if (!testName.equals(testBaseName))
+        {
+            testCounter = 0;
+            testBaseName = testName;
+        }
+    }
+
+    final class TestException extends RuntimeException
+    {
+        private static final long serialVersionUID = 1L;
+
+        public TestException(String message)
+        {
+            super(message);
+        }
+
+        public TestException(String message, Throwable cause)
+        {
+            super(message, cause);
+        }
+    }
+
+    public void testStatusCallback() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            client.status(thisTest.getWorkingCopy() + "/A", Depth.immediates,
+                          false, true, true, false, false, false, null,
+                          new StatusCallback()
+                          {
+                            public void doStatus(String path, Status status)
+                            {
+                                throw new TestException("inner", theException);
+                            }
+                          });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    public void testInfoCallback() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            client.info(thisTest.getWorkingCopy() + "/A", null, null, 
Depth.immediates,
+                        true, true, false, null,
+                          new InfoCallback()
+                          {
+                            public void singleInfo(Info info)
+                            {
+                                throw new TestException("inner", theException);
+                            }
+                          });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    public void testListCallback() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            client.list(thisTest.getWorkingCopy() + "/A", null, null, 
Depth.immediates,
+                        7, false,
+                        new ListCallback()
+                          {
+                            public void doEntry(DirEntry dirent, Lock lock)
+                            {
+                                throw new TestException("inner", theException);
+                            }
+                          });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    public void testBlameCallback() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            client.blame(thisTest.getWorkingCopy() + "/iota",
+                         Revision.getInstance(1), Revision.getInstance(1),
+                         Revision.getInstance(1), false, false,
+                         new BlameCallback()
+                          {
+                            public void singleLine(long lineNum, long revision,
+                                                   Map<String, byte[]> 
revProps, long mergedRevision,
+                                                   Map<String, byte[]> 
mergedRevProps,
+                                                   String mergedPath, String 
line,
+                                                   boolean localChange)
+                            {
+                                throw new TestException("inner", theException);
+                            }
+                          });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    public void testLogMessageCallback() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            List<RevisionRange> ranges = new ArrayList<RevisionRange>(1);
+            ranges.add(new RevisionRange(null, null));
+            client.logMessages(thisTest.getWorkingCopy() + "/iota",
+                               Revision.getInstance(1), ranges,
+                               false, false, false,
+                               null, 2,
+                               new LogMessageCallback()
+                               {
+                                    public void singleMessage(
+                                                  Set<ChangePath> changedPaths,
+                                                  long revision,
+                                                  Map<String, byte[]> revprops,
+                                                  boolean hasChildren)
+                                    {
+                                        throw new TestException("inner",
+                                                                theException);
+                                    }
+                               });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    public void testDiffSummaryReceiver() throws Throwable
+    {
+        // build the test setup
+        OneTest thisTest = new OneTest();
+
+        final TestException theException = new TestException("The Exception");
+        boolean handled = false;
+        // Test status of non-existent file
+        try
+        {
+            List<RevisionRange> ranges = new ArrayList<RevisionRange>(1);
+            ranges.add(new RevisionRange(null, null));
+            client.diffSummarize(thisTest.getUrl() + "/A",
+                                 Revision.getInstance(1),
+                                 thisTest.getUrl() + "/A",
+                                 Revision.getInstance(0),
+                                 Depth.infinity, null, true,
+                                 new DiffSummaryCallback()
+                                 {
+                                    public void onSummary(
+                                                  DiffSummary descriptor)
+                                    {
+                                        throw new TestException("inner",
+                                                                theException);
+                                    }
+                               });
+        }
+        catch (ClientException e)
+        {
+            if (VerifyCause(e, theException))
+                handled = true;
+            else
+                throw e;
+        }
+        assertTrue(handled);
+    }
+
+    private boolean VerifyCause(Throwable caught, Throwable needle) throws 
TestException
+    {
+        if (caught == needle)
+            return true;
+
+        Throwable cause = caught.getCause();
+        if (cause == null)
+            return false;
+
+        return VerifyCause(cause, needle);
+    }
+}

Propchange: 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java?rev=1664938&r1=1664937&r2=1664938&view=diff
==============================================================================
--- 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java
 (original)
+++ 
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java
 Sun Mar  8 01:05:14 2015
@@ -95,6 +95,7 @@ public class RunTests
                 suite.addTestSuite(UtilTests.class);
                 suite.addTestSuite(SVNRemoteTests.class);
                 suite.addTestSuite(SVNReposTests.class);
+                suite.addTestSuite(ExceptionTests.class);
             }
             else
             {


Reply via email to