/* * 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. * */ package org.apache.tools.ant.taskdefs.optional.junit; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import junit.framework.Test; import junit.framework.TestCase; /** * Work around for some changes to the public JUnit API between * different JUnit releases. * @since Ant 1.7 */ // CheckStyle:HideUtilityClassConstructorCheck OFF (bc) public class JUnitVersionHelper { private static Method testCaseName = null; /** * Name of the JUnit4 class we look for. * {@value} * @since Ant 1.7.1 */ public static final String JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE = "junit.framework.JUnit4TestCaseFacade"; private static final String UNKNOWN_TEST_CASE_NAME = "unknown"; static { try { testCaseName = TestCase.class.getMethod("getName", new Class[0]); } catch (NoSuchMethodException e) { // pre JUnit 3.7 try { testCaseName = TestCase.class.getMethod("name", new Class[0]); } catch (NoSuchMethodException ignored) { // ignore } } } /** * JUnit 3.7 introduces TestCase.getName() and subsequent versions * of JUnit remove the old name() method. This method provides * access to the name of a TestCase via reflection that is * supposed to work with version before and after JUnit 3.7. * * <p>since Ant 1.5.1 this method will invoke "<code>public * String getName()</code>" on any implementation of Test if * it exists.</p> * * <p>Since Ant 1.7 also checks for JUnit4TestCaseFacade explicitly. * This is used by junit.framework.JUnit4TestAdapter.</p> * @param t the test. * @return the name of the test. */ public static String getTestCaseName(Test t) { if (t == null) { return UNKNOWN_TEST_CASE_NAME; } if (t.getClass().getName().equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) { // Self-describing as of JUnit 4 (#38811). But trim "(ClassName)". String name = t.toString(); if (name.endsWith(")")) { int paren = name.lastIndexOf('('); return name.substring(0, paren); } return name; } if (t instanceof TestCase && testCaseName != null) { try { return (String) testCaseName.invoke(t, new Object[0]); } catch (Throwable ignored) { // ignore } } else { try { Method getNameMethod; try { getNameMethod = t.getClass().getMethod("getName"); } catch (NoSuchMethodException e) { getNameMethod = t.getClass().getMethod("name"); } if (getNameMethod != null && getNameMethod.getReturnType() == String.class) { return (String) getNameMethod.invoke(t); } } catch (Throwable ignored) { // ignore } } return UNKNOWN_TEST_CASE_NAME; } /** * Tries to find the name of the class which a test represents * across JUnit 3 and 4. For JUnit4 it parses the toString() value of the * test, and extracts it from there. * @since Ant 1.7.1 (it was private until then) * @param test test case to look at * @return the extracted class name. */ public static String getTestCaseClassName(Test test) { String className = test.getClass().getName(); if (test instanceof JUnitTaskMirrorImpl.VmExitErrorTest) { className = ((JUnitTaskMirrorImpl.VmExitErrorTest) test).getClassName(); } else if (className.equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) { // JUnit 4 wraps solo tests this way. We can extract // the original test name with a little hack. String name = test.toString(); int paren = name.lastIndexOf('('); if (paren != -1 && name.endsWith(")")) { className = name.substring(paren + 1, name.length() - 1); } } return className; } public static String getIgnoreMessage(Test test) { String message = null; try { Class<?> junit4FacadeClass = Class.forName("junit.framework.JUnit4TestCaseFacade"); if (test != null && test.getClass().isAssignableFrom(junit4FacadeClass)) { //try and get the message coded as part of the ignore /* * org.junit.runner.Description contains a getAnnotation(Class) method... but this * wasn't in older versions of JUnit4 so we have to try and do this by reflection */ Class<?> testClass = Class.forName(JUnitVersionHelper.getTestCaseClassName(test)); Method testMethod = testClass.getMethod(JUnitVersionHelper.getTestCaseName(test)); Class<? extends Annotation> ignoreAnnotation = Class .forName("org.junit.Ignore").asSubclass(Annotation.class); Annotation annotation = testMethod.getAnnotation(ignoreAnnotation); if (annotation != null) { Method valueMethod = annotation.annotationType().getMethod("value"); String value = (String) valueMethod.invoke(annotation); if (value != null && value.length() > 0) { message = value; } } } } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | IllegalAccessException e) { // silently ignore - we'll report a skip with no message } return message; } }