blob: 3f842e7027d43f176cef29b4c1f1737a6aff8377 [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:27:04 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij;
import com.intellij.idea.RecordExecution;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.testFramework.*;
import com.intellij.tests.ExternalClasspathClassLoader;
import com.intellij.util.ArrayUtil;
import junit.framework.*;
import org.jetbrains.annotations.Nullable;
import org.junit.runner.Description;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@SuppressWarnings({"HardCodedStringLiteral", "CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"})
public class TestAll implements Test {
static {
Logger.setFactory(TestLoggerFactory.class);
}
private final TestCaseLoader myTestCaseLoader;
private long myStartTime = 0;
private boolean myInterruptedByOutOfTime = false;
private long myLastTestStartTime = 0;
private String myLastTestClass;
private int myRunTests = -1;
private boolean mySavingMemorySnapshot;
private static final int SAVE_MEMORY_SNAPSHOT = 1;
private static final int START_GUARD = 2;
private static final int RUN_GC = 4;
private static final int CHECK_MEMORY = 8;
private static final int FILTER_CLASSES = 16;
public static int ourMode = SAVE_MEMORY_SNAPSHOT /*| START_GUARD | RUN_GC | CHECK_MEMORY*/ | FILTER_CLASSES;
private static final boolean PERFORMANCE_TESTS_ONLY = System.getProperty(TestCaseLoader.PERFORMANCE_TESTS_ONLY_FLAG) != null;
private int myLastTestTestMethodCount = 0;
public static final int MAX_FAILURE_TEST_COUNT = 150;
private TestRecorder myTestRecorder;
private static final Filter PERFORMANCE_ONLY = new Filter() {
@Override
public boolean shouldRun(Description description) {
String className = description.getClassName();
String methodName = description.getMethodName();
return className != null && hasPerformance(className) ||
methodName != null && hasPerformance(methodName);
}
@Override
public String describe() {
return "Performance Tests Only";
}
};
private static final Filter NO_PERFORMANCE = new Filter() {
@Override
public boolean shouldRun(Description description) {
return !PERFORMANCE_ONLY.shouldRun(description);
}
@Override
public String describe() {
return "All Except Performance";
}
};
@Override
public int countTestCases() {
List<Class> classes = myTestCaseLoader.getClasses();
int count = 0;
for (final Object aClass : classes) {
Class testCaseClass = (Class)aClass;
Test test = getTest(testCaseClass);
if (test != null) count += test.countTestCases();
}
return count;
}
private void beforeFirstTest() {
if ((ourMode & START_GUARD) != 0) {
Thread timeAndMemoryGuard = new Thread() {
@Override
public void run() {
log("Starting Time and Memory Guard");
while (true) {
try {
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
// check for time spent on current test
if (myLastTestStartTime != 0) {
long currTime = System.currentTimeMillis();
long secondsSpent = (currTime - myLastTestStartTime) / 1000L;
Thread currentThread = getCurrentThread();
if (!mySavingMemorySnapshot) {
if (secondsSpent > PlatformTestCase.ourTestTime * myLastTestTestMethodCount) {
UsefulTestCase.printThreadDump();
log("Interrupting current Test (out of time)! Test class: "+ myLastTestClass +" Seconds spent = " + secondsSpent);
myInterruptedByOutOfTime = true;
if (currentThread != null) {
currentThread.interrupt();
if (!currentThread.isInterrupted()) {
//noinspection deprecation
currentThread.stop(new RuntimeException("Current Test Interrupted: OUT OF TIME!"));
}
break;
}
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
log("Time and Memory Guard finished.");
}
};
timeAndMemoryGuard.setDaemon(true);
timeAndMemoryGuard.start();
}
myStartTime = System.currentTimeMillis();
}
private static Thread getCurrentThread() {
if (PlatformTestCase.ourTestThread != null) {
return PlatformTestCase.ourTestThread;
}
else return LightPlatformTestCase.ourTestThread;
}
private void addErrorMessage(TestResult testResult, String message) {
String processedTestsMessage = myRunTests <= 0 ? "None of tests was run" : myRunTests + " tests processed";
try {
testResult.startTest(this);
testResult.addError(this, new Throwable(processedTestsMessage + " before: " + message));
testResult.endTest(this);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run(final TestResult testResult) {
loadTestRecorder();
List<Class> classes = myTestCaseLoader.getClasses();
int totalTests = classes.size();
for (final Class aClass : classes) {
boolean recording = false;
if (myTestRecorder != null && shouldRecord(aClass)) {
myTestRecorder.beginRecording(aClass, (RecordExecution) aClass.getAnnotation(RecordExecution.class));
recording = true;
}
try {
runNextTest(testResult, totalTests, aClass);
}
finally {
if (recording) {
myTestRecorder.endRecording();
}
}
if (testResult.shouldStop()) break;
}
tryGc(10);
}
private boolean shouldRecord(Class aClass) {
if (aClass.getAnnotation(RecordExecution.class) != null) {
return true;
}
return false;
}
private void loadTestRecorder() {
String recorderClassName = System.getProperty("test.recorder.class");
if (recorderClassName != null) {
try {
Class<?> recorderClass = Class.forName(recorderClassName);
myTestRecorder = (TestRecorder) recorderClass.newInstance();
}
catch (ClassNotFoundException e) {
System.out.println("CNFE loading test recorder class: " + e);
}
catch (InstantiationException e) {
System.out.println("InstantiationException loading test recorder class: " + e);
}
catch (IllegalAccessException e) {
System.out.println("IAE loading test recorder class: " + e);
}
}
}
private void runNextTest(final TestResult testResult, int totalTests, Class testCaseClass) {
myRunTests++;
if (!checkAvaliableMemory(35, testResult)) {
testResult.stop();
return;
}
if (testResult.errorCount() + testResult.failureCount() > MAX_FAILURE_TEST_COUNT) {
addErrorMessage(testResult, "Too many errors. Tests stopped. Total " + myRunTests + " of " + totalTests + " tests run");
testResult.stop();
return;
}
if (myStartTime == 0) {
boolean ourClassLoader = getClass().getClassLoader().getClass().getName().startsWith("com.intellij.");
if (!ourClassLoader) {
beforeFirstTest();
}
}
else {
if (myInterruptedByOutOfTime) {
addErrorMessage(testResult,
"Current Test Interrupted: OUT OF TIME! Class = " + myLastTestClass + " Total " + myRunTests + " of " +
totalTests +
" tests run");
testResult.stop();
return;
}
}
log("\nRunning " + testCaseClass.getName());
final Test test = getTest(testCaseClass);
if (test == null) return;
myLastTestClass = null;
myLastTestClass = testCaseClass.getName();
myLastTestStartTime = System.currentTimeMillis();
myLastTestTestMethodCount = test.countTestCases();
try {
test.run(testResult);
}
catch (Throwable t) {
if (t instanceof OutOfMemoryError) {
if ((ourMode & SAVE_MEMORY_SNAPSHOT) != 0) {
try {
mySavingMemorySnapshot = true;
log("OutOfMemoryError detected. Saving memory snapshot started");
}
finally {
log("Saving memory snapshot finished");
mySavingMemorySnapshot = false;
}
}
}
testResult.addError(test, t);
}
}
private boolean checkAvaliableMemory(int neededMemory, TestResult testResult) {
if ((ourMode & CHECK_MEMORY) == 0) return true;
boolean possibleOutOfMemoryError = possibleOutOfMemory(neededMemory);
if (possibleOutOfMemoryError) {
tryGc(5);
possibleOutOfMemoryError = possibleOutOfMemory(neededMemory);
if (possibleOutOfMemoryError) {
log("OutOfMemoryError: dumping memory");
Runtime runtime = Runtime.getRuntime();
long total = runtime.totalMemory();
long free = runtime.freeMemory();
String errorMessage = "Too much memory used. Total: " + total + " free: " + free + " used: " + (total - free) + "\n";
addErrorMessage(testResult, errorMessage);
}
}
return !possibleOutOfMemoryError;
}
private static boolean possibleOutOfMemory(int neededMemory) {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long realFreeMemory = runtime.freeMemory() + (maxMemory - runtime.totalMemory());
long meg = 1024 * 1024;
long needed = neededMemory * meg;
return realFreeMemory < needed;
}
private static boolean isPerformanceTestsRun() {
return PERFORMANCE_TESTS_ONLY;
}
@Nullable
private static Test getTest(final Class testCaseClass) {
if ((testCaseClass.getModifiers() & Modifier.PUBLIC) == 0) return null;
Method suiteMethod = safeFindMethod(testCaseClass, "suite");
if (suiteMethod != null && !isPerformanceTestsRun()) {
try {
return (Test)suiteMethod.invoke(null, ArrayUtil.EMPTY_CLASS_ARRAY);
}
catch (Exception e) {
System.err.println("Failed to execute suite ()");
e.printStackTrace();
}
}
else {
if (TestRunnerUtil.isJUnit4TestClass(testCaseClass)) {
JUnit4TestAdapter adapter = new JUnit4TestAdapter(testCaseClass);
if (!hasPerformance(testCaseClass.getSimpleName()) || !isPerformanceTestsRun()) {
try {
adapter.filter(isPerformanceTestsRun() ? PERFORMANCE_ONLY : NO_PERFORMANCE);
}
catch (NoTestsRemainException e1) {
// Ignore
}
}
return adapter;
}
final int[] testsCount = {0};
TestSuite suite = new TestSuite(testCaseClass) {
@Override
public void addTest(Test test) {
if (!(test instanceof TestCase)) {
testsCount[0]++;
super.addTest(test);
}
else {
String name = ((TestCase)test).getName();
if ("warning".equals(name)) return; // Mute TestSuite's "no tests found" warning
if (isPerformanceTestsRun() ^ (hasPerformance(name) || hasPerformance(testCaseClass.getSimpleName())))
return;
Method method = findTestMethod((TestCase)test);
if (method == null || !TestCaseLoader.isBombed(method)) {
testsCount[0]++;
super.addTest(test);
}
}
}
@Nullable
private Method findTestMethod(final TestCase testCase) {
return safeFindMethod(testCase.getClass(), testCase.getName());
}
};
return testsCount[0] > 0 ? suite : null;
}
return null;
}
private static boolean hasPerformance(String name) {
return name.toLowerCase().contains("performance");
}
@Nullable
private static Method safeFindMethod(Class klass, String name) {
try {
return klass.getMethod(name);
}
catch (NoSuchMethodException e) {
return null;
}
}
private static Set<String> normalizePaths(String[] array) {
Set<String> answer = new LinkedHashSet<String>(array.length);
for (String path : array) {
answer.add(path.replace('\\', '/'));
}
return answer;
}
public static String[] getClassRoots() {
String testRoots = System.getProperty("test.roots");
if (testRoots != null) {
System.out.println("Collecting tests from roots specified by test.roots property: " + testRoots);
return testRoots.split(";");
}
String[] roots = ExternalClasspathClassLoader.getRoots();
if (roots != null) {
if (Comparing.equal(System.getProperty(TestCaseLoader.SKIP_COMMUNITY_TESTS), "true")) {
System.out.println("Skipping community tests");
Set<String> set = normalizePaths(roots);
set.removeAll(normalizePaths(ExternalClasspathClassLoader.getExcludeRoots()));
roots = set.toArray(new String[set.size()]);
}
System.out.println("Collecting tests from roots specified by classpath.file property: " + Arrays.toString(roots));
return roots;
}
else {
final ClassLoader loader = TestAll.class.getClassLoader();
if (loader instanceof URLClassLoader) {
final URL[] urls = ((URLClassLoader)loader).getURLs();
final String[] classLoaderRoots = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
classLoaderRoots[i] = VfsUtil.urlToPath(VfsUtil.convertFromUrl(urls[i]));
}
System.out.println("Collecting tests from classloader: " + Arrays.toString(classLoaderRoots));
return classLoaderRoots;
}
return System.getProperty("java.class.path").split(File.pathSeparator);
}
}
public TestAll(String packageRoot) throws Throwable {
this(packageRoot, getClassRoots());
}
public TestAll(String packageRoot, String... classRoots) throws IOException, ClassNotFoundException {
String classFilterName = "tests/testGroups.properties";
if (Boolean.parseBoolean(System.getProperty("idea.ignore.predefined.groups")) || (ourMode & FILTER_CLASSES) == 0) {
classFilterName = "";
}
myTestCaseLoader = new TestCaseLoader(classFilterName, isPerformanceTestsRun());
myTestCaseLoader.addFirstTest(Class.forName("_FirstInSuiteTest"));
myTestCaseLoader.addLastTest(Class.forName("_LastInSuiteTest"));
fillTestCases(myTestCaseLoader, packageRoot, classRoots);
}
public static void fillTestCases(TestCaseLoader testCaseLoader, String packageRoot, String... classRoots) throws IOException {
for (String classRoot : classRoots) {
int oldCount = testCaseLoader.getClasses().size();
ClassFinder classFinder = new ClassFinder(new File(FileUtil.toSystemDependentName(classRoot)), packageRoot);
testCaseLoader.loadTestCases(classFinder.getClasses());
int newCount = testCaseLoader.getClasses().size();
if (newCount != oldCount) {
System.out.println("Loaded " + (newCount - oldCount) + " tests from class root " + classRoot);
}
}
if (testCaseLoader.getClasses().size() == 1) {
testCaseLoader.clearClasses();
}
log("Number of test classes found: " + testCaseLoader.getClasses().size());
}
private static void log(String message) {
TeamCityLogger.info(message);
}
// [myakovlev] Do not delete - it is for debugging
public static void tryGc(int times) {
if ((ourMode & RUN_GC) == 0) return;
for (int qqq = 1; qqq < times; qqq++) {
try {
Thread.sleep(qqq * 1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.gc();
//long mem = Runtime.getRuntime().totalMemory();
log("Runtime.getRuntime().totalMemory() = " + Runtime.getRuntime().totalMemory());
}
}
}