blob: 21d1ca207f9dc11813293430f976f0fda5fa0f92 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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.
*/
/*
* Created by IntelliJ IDEA.
* User: mike
* Date: Jun 7, 2002
* Time: 8:30:35 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij;
import com.intellij.idea.Bombed;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.TestRunnerUtil;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;
import java.util.List;
@SuppressWarnings({"HardCodedStringLiteral", "UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "TestOnlyProblems"})
public class TestCaseLoader {
/** Holds name of JVM property that is assumed to define target test group name. */
private static final String TARGET_TEST_GROUP = "idea.test.group";
/** Holds name of JVM property that is assumed to define filtering rules for test classes. */
private static final String TARGET_TEST_PATTERNS = "idea.test.patterns";
public static final String PERFORMANCE_TESTS_ONLY_FLAG = "idea.performance.tests";
public static final String SKIP_COMMUNITY_TESTS = "idea.skip.community.tests";
private final List<Class> myClassList = new ArrayList<Class>();
private Class myFirstTestClass;
private Class myLastTestClass;
private final TestClassesFilter myTestClassesFilter;
private final boolean myIsPerformanceTestsRun;
public TestCaseLoader(String classFilterName, boolean isPerformanceTestsRun) {
myIsPerformanceTestsRun = isPerformanceTestsRun;
String patterns = System.getProperty(TARGET_TEST_PATTERNS);
if (patterns != null) {
myTestClassesFilter = new PatternListTestClassFilter(StringUtil.split(patterns, ";"));
System.out.println("Using patterns: [" + patterns +"]");
}
else {
URL excludedStream = StringUtil.isEmpty(classFilterName) ? null : getClass().getClassLoader().getResource(classFilterName);
if (excludedStream != null) {
TestClassesFilter filter;
try {
String testGroupName = System.getProperty(TARGET_TEST_GROUP, "").trim();
InputStreamReader reader = new InputStreamReader(excludedStream.openStream());
try {
filter = GroupBasedTestClassFilter.createOn(reader, testGroupName);
System.out.println("Using test group: [" + testGroupName +"]");
}
finally {
reader.close();
}
}
catch (IOException e) {
e.printStackTrace();
filter = TestClassesFilter.ALL_CLASSES;
System.out.println("Using all classes");
}
myTestClassesFilter = filter;
}
else {
myTestClassesFilter = TestClassesFilter.ALL_CLASSES;
System.out.println("Using all classes");
}
}
}
/*
* Adds <code>testCaseClass</code> to the list of classes
* if the class is a test case we wish to load. Calls
* <code>shouldLoadTestCase ()</code> to determine that.
*/
void addClassIfTestCase(Class testCaseClass) {
if (shouldAddTestCase(testCaseClass, true) &&
testCaseClass != myFirstTestClass && testCaseClass != myLastTestClass &&
PlatformTestUtil.canRunTest(testCaseClass)) {
myClassList.add(testCaseClass);
}
}
void addFirstTest(Class aClass) {
assert myFirstTestClass == null : "already added: "+aClass;
assert shouldAddTestCase(aClass, false) : "not a test: "+aClass;
myFirstTestClass = aClass;
}
void addLastTest(Class aClass) {
assert myLastTestClass == null : "already added: "+aClass;
assert shouldAddTestCase(aClass, false) : "not a test: "+aClass;
myLastTestClass = aClass;
}
/**
* Determine if we should load this test case.
*/
private boolean shouldAddTestCase(final Class<?> testCaseClass, boolean testForExcluded) {
if ((testCaseClass.getModifiers() & Modifier.ABSTRACT) != 0) return false;
if (testForExcluded && shouldExcludeTestClass(testCaseClass)) return false;
if (TestCase.class.isAssignableFrom(testCaseClass) || TestSuite.class.isAssignableFrom(testCaseClass)) {
return true;
}
try {
final Method suiteMethod = testCaseClass.getMethod("suite");
if (Test.class.isAssignableFrom(suiteMethod.getReturnType()) && (suiteMethod.getModifiers() & Modifier.STATIC) != 0) {
//System.out.println("testCaseClass = " + testCaseClass);
return true;
}
}
catch (NoSuchMethodException e) {
// can't be
}
return TestRunnerUtil.isJUnit4TestClass(testCaseClass);
}
/*
* Determine if we should exclude this test case.
*/
private boolean shouldExcludeTestClass(Class testCaseClass) {
String className = testCaseClass.getName();
if (className.toLowerCase().contains("performance") && !myIsPerformanceTestsRun) return true;
return !myTestClassesFilter.matches(className) || isBombed(testCaseClass);
}
public static boolean isBombed(final Method method) {
final Bombed bombedAnnotation = method.getAnnotation(Bombed.class);
if (bombedAnnotation == null) return false;
if (PlatformTestUtil.isRotten(bombedAnnotation)) {
String message = "Disarm the stale bomb for '" + method + "' in class '" + method.getDeclaringClass() + "'";
System.err.println(message);
//Assert.fail(message);
}
return !PlatformTestUtil.bombExplodes(bombedAnnotation);
}
public static boolean isBombed(final Class<?> testCaseClass) {
final Bombed bombedAnnotation = testCaseClass.getAnnotation(Bombed.class);
if (bombedAnnotation == null) return false;
if (PlatformTestUtil.isRotten(bombedAnnotation)) {
String message = "Disarm the stale bomb for '" + testCaseClass + "'";
System.err.println(message);
// Assert.fail(message);
}
return !PlatformTestUtil.bombExplodes(bombedAnnotation);
}
public void loadTestCases(final Collection<String> classNamesIterator) {
for (String className : classNamesIterator) {
try {
Class candidateClass = Class.forName(className);
addClassIfTestCase(candidateClass);
}
catch (ClassNotFoundException e) {
System.err.println("Cannot load class " + className + ": " + e.getMessage());
e.printStackTrace();
}
catch (ExceptionInInitializerError e) {
System.err.println("Cannot initialize class " + className + ": " + e.getException().getMessage());
e.printStackTrace();
System.err.println("Root cause:");
e.getException().printStackTrace();
}
catch (LinkageError e) {
System.err.println("Cannot load class " + className + ": " + e.getMessage());
e.printStackTrace();
}
}
}
private static final List<String> ourRankList = getTeamCityRankList();
private static List<String> getTeamCityRankList() {
final String filePath = System.getProperty("teamcity.tests.recentlyFailedTests.file", null);
if (filePath == null) {
return Collections.emptyList();
}
try {
final List<String> result = new ArrayList<String>();
final BufferedReader reader = new BufferedReader(new FileReader(filePath));
try {
do {
final String className = reader.readLine();
if (className == null) break;
result.add(className);
}
while (true);
return result;
}
finally {
reader.close();
}
}
catch (IOException e) {
return Collections.emptyList();
}
}
private int getRank(Class aClass) {
final String name = aClass.getName();
if (aClass == myFirstTestClass) return -1;
if (aClass == myLastTestClass) return myClassList.size() + ourRankList.size();
int i = ourRankList.indexOf(name);
if (i != -1) {
return i;
}
return ourRankList.size();
}
public List<Class> getClasses() {
List<Class> result = new ArrayList<Class>(myClassList.size());
if (myFirstTestClass != null) {
result.add(myFirstTestClass);
}
result.addAll(myClassList);
if (myLastTestClass != null) {
result.add(myLastTestClass);
}
if (!ourRankList.isEmpty()) {
Collections.sort(result, new Comparator<Class>() {
@Override
public int compare(final Class o1, final Class o2) {
return getRank(o1) - getRank(o2);
}
});
}
return result;
}
public void clearClasses() {
myClassList.clear();
}
}