content: /*
   *  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
   *
   *      https://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.
   *
   */
  
  package org.apache.tools.ant.taskdefs.optional.junit;
  
  import java.io.OutputStream;
  import java.lang.reflect.Method;
  
  import junit.framework.AssertionFailedError;
  import junit.framework.Test;
  
  /**
   * Formatter that doesn't create any output but tries to invoke the
   * tearDown method on a testcase if that test was forked and caused a
   * timeout or VM crash.
   *
   * <p>This formatter has some limitations, for details see the
   * &lt;junit&gt; task's manual.</p>
   *
   * @since Ant 1.8.0
   */
  public class TearDownOnVmCrash implements JUnitResultFormatter {
  
      private String suiteName;
  
      /**
       * Records the suite's name to later determine the class to invoke
       * tearDown on.
       */
      @Override
      public void startTestSuite(final JUnitTest suite) {
          suiteName = suite.getName();
          if (suiteName != null && suiteName.endsWith(JUnitTask.NAME_OF_DUMMY_TEST)) {
              // no way to know which class caused the timeout
              suiteName = null;
          }
      }
  
      /**
       * Only invoke tearDown if the suite is known and not the dummy
       * test we get when a Batch fails and the error is an actual
       * error generated by Ant.
       */
      @Override
      public void addError(final Test fakeTest, final Throwable t) {
          if (suiteName != null && fakeTest instanceof JUnitTaskMirrorImpl.VmExitErrorTest) {
              tearDown();
          }
      }
  
      // no need to implement the rest
      public void addFailure(Test test, Throwable t) {
      }
  
      @Override
      public void addFailure(Test test, AssertionFailedError t) {
      }
  
      @Override
      public void startTest(Test test) {
      }
  
      @Override
      public void endTest(Test test) {
      }
  
      @Override
      public void endTestSuite(JUnitTest suite) {
      }
  
      @Override
      public void setOutput(OutputStream out) {
      }
  
      @Override
      public void setSystemOutput(String out) {
      }
  
      @Override
      public void setSystemError(String err) {
      }
  
      private void tearDown() {
          try {
              // first try to load the class and let's hope it is on our
              // classpath
              Class<?> testClass = null;
              if (Thread.currentThread().getContextClassLoader() != null) {
                  try {
                      testClass = Thread.currentThread().getContextClassLoader()
                          .loadClass(suiteName);
                  } catch (ClassNotFoundException cnfe) {
                      // ignore
                  }
              }
              if (testClass == null && getClass().getClassLoader() != null) {
                  try {
                      testClass = getClass().getClassLoader().loadClass(suiteName);
                  } catch (ClassNotFoundException cnfe) {
                      // ignore
                  }
              }
              if (testClass == null) {
                  // fall back to system classloader
                  testClass = Class.forName(suiteName);
              }
  
              // if the test has a suite method, then we can't know
              // which test of the executed suite timed out, ignore it
              try {
                  // check if there is a suite method
                  testClass.getMethod("suite");
                  return;
              } catch (NoSuchMethodException e) {
                  // no suite method
              }
  
              // a loadable class and no suite method
              // no reason to check for JUnit 4 since JUnit4TestAdapter
              // doesn't have any tearDown method.
  
              try {
                  Method td = testClass.getMethod("tearDown");
                  if (td.getReturnType() == Void.TYPE) {
                      td.invoke(testClass.getDeclaredConstructor().newInstance());
                  }
              } catch (NoSuchMethodException nsme) {
                  // no tearDown, fine
              }
  
          } catch (ClassNotFoundException cnfe) {
              // class probably is not in our classpath, there is
              // nothing we can do
          } catch (Throwable t) {
              System.err.println("Caught an exception while trying to invoke"
                                 + " tearDown: " + t.getMessage());
          }
      }
  }
  