/*
 * JMockit
 * Copyright (c) 2006-2009 Rogério Liesenfeld
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package mockit.integration.junit3.internal;

import java.lang.reflect.*;

import junit.framework.*;

import mockit.*;
import mockit.integration.*;
import mockit.internal.expectations.*;
import mockit.internal.state.*;
import mockit.internal.util.*;

/**
 * Provides an startup mock that modifies the JUnit 3.8 test runner so that it calls back to JMockit
 * for each test execution.
 * When that happens, JMockit will assert any expectations set during the test, including
 * expectations specified through {@link Mock} as well as in {@link Expectations} subclasses.
 * <p/>
 * This class is not supposed to be accessed from user code. JMockit will automatically load it at
 * startup.
 */
@MockClass(realClass = TestCase.class)
public final class JUnitTestCaseDecorator extends TestRunnerDecorator
{
   private static final Object[] NO_ARGS = new Object[0];

   private static final Method setUpMethod;
   private static final Method tearDownMethod;

   static
   {
      Method m1;
      Method m2;

      try {
         m1 = TestCase.class.getDeclaredMethod("setUp");
         m2 = TestCase.class.getDeclaredMethod("tearDown");
      }
      catch (NoSuchMethodException e) {
         // OK, won't happen.
         throw new RuntimeException(e);
      }

      setUpMethod = m1;
      setUpMethod.setAccessible(true);

      tearDownMethod = m2;
      tearDownMethod.setAccessible(true);
   }

   public TestCase it;

   @Mock
   public void runBare() throws Throwable
   {
      updateTestClassState(it, it.getClass());

      TestRun.setRunningIndividualTest(it);
      AssertionError error;

      try {
         originalRunBare();

         error = RecordAndReplayExecution.endCurrentReplayIfAny();
         TestRun.finishCurrentTestExecution();
      }
      catch (Throwable t) {
         //noinspection ThrowableResultOfMethodCallIgnored
         RecordAndReplayExecution.endCurrentReplayIfAny();
         TestRun.finishCurrentTestExecution();
         Utilities.filterStackTrace(t);
         throw t;
      }

      if (error != null) {
         Utilities.filterStackTrace(error);
         throw error;
      }

      try {
         TestRun.verifyExpectationsOnAnnotatedMocks();
      }
      catch (AssertionError t) {
         Utilities.filterStackTrace(t);
         throw t;
      }
   }

   private void originalRunBare() throws Throwable
   {
      setUpMethod.invoke(it);

      Throwable exception = null;

      try {
         runTest();
      }
      catch (Throwable running) {
         exception = running;
      }
      finally {
         try {
            tearDownMethod.invoke(it);
         }
         catch (Throwable tearingDown) {
            if (exception == null) {
               exception = tearingDown;
            }
         }
      }

      if (exception != null) {
         throw exception;
      }
   }

   @Mock
   public void runTest() throws Throwable
   {
      String testMethodName = it.getName();
      Method runMethod = findTestMethod(testMethodName);

      if (runMethod == null) {
         Assert.fail("Method \"" + testMethodName + "\" not found");
      }

      SavePoint savePoint = new SavePoint();
      Object[] args = createInstancesForMockParametersIfAny(it, runMethod, NO_ARGS);

      try {
         runMethod.invoke(it, args);
      }
      catch (InvocationTargetException e) {
         e.fillInStackTrace();
         throw e.getTargetException();
      }
      catch (IllegalAccessException e) {
         e.fillInStackTrace();
         throw e;
      }
      finally {
         savePoint.rollback();
      }
   }

   private Method findTestMethod(String testMethodName)
   {
      for (Method publicMethod : it.getClass().getMethods()) {
         if (publicMethod.getName().equals(testMethodName)) {
            return publicMethod;
         }
      }

      return null;
   }
}

@@ -2166,63 +2166,63 @@
 d;%0D%0A
-%0D%0A   static%0D%0A   %7B%0D%0A      Method m1;%0D%0A      Method m2;%0D%0A
+   private static final Field fName;%0D%0A%0D%0A   static%0D%0A   %7B
 %0D%0A  
@@ -2241,18 +2241,27 @@
         
-m1
+setUpMethod
  = TestC
@@ -2308,18 +2308,30 @@
         
-m2
+tearDownMethod
  = TestC
@@ -2368,24 +2368,84 @@
 earDown%22);%0D%0A
+         fName = TestCase.class.getDeclaredField(%22fName%22);%0D%0A
       %7D%0D%0A   
@@ -2567,33 +2567,129 @@
  %7D%0D%0A
-%0D%0A      setUpMethod = m1;
+      catch (NoSuchFieldException e) %7B%0D%0A         // OK, won't happen.%0D%0A         throw new RuntimeException(e);%0D%0A      %7D%0D%0A
 %0D%0A  
@@ -2722,26 +2722,24 @@
 ble(true);%0D%0A
-%0D%0A
       tearDo
@@ -2750,36 +2750,42 @@
 thod
- = m2;%0D%0A      tearDownMethod
+.setAccessible(true);%0D%0A      fName
 .set
@@ -4414,19 +4414,29 @@
 e = 
-it.getName(
+(String) fName.get(it
 );%0D%0A

Fix for issue 21: support for JUnit 3.8 test methods with mock parameters was incompatible with other tools which happen to override the TestCase#getName(), returning something other than the value of the "fName" field.