Merge branch 'master' of git@git.labs.intellij.net:idea/community
[idea/community.git] / platform / testFramework / src / com / intellij / TestAll.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Created by IntelliJ IDEA.
19  * User: mike
20  * Date: Jun 7, 2002
21  * Time: 8:27:04 PM
22  * To change template for new class use
23  * Code Style | Class Templates options (Tools | IDE Options).
24  */
25 package com.intellij;
26
27 import com.intellij.openapi.application.Application;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.application.ModalityState;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.fileEditor.FileDocumentManager;
32 import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
33 import com.intellij.testFramework.*;
34 import com.intellij.tests.ExternalClasspathClassLoader;
35 import com.intellij.util.ArrayUtil;
36 import junit.framework.*;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.lang.reflect.Method;
41 import java.lang.reflect.Modifier;
42 import java.util.List;
43
44 @SuppressWarnings({"HardCodedStringLiteral"})
45 public class TestAll implements Test {
46
47   static {
48     Logger.setFactory(TestLoggerFactory.getInstance());
49   }
50
51   private static final Logger LOG = Logger.getInstance("#com.intellij.TestAll");
52
53   private TestCaseLoader myTestCaseLoader;
54   private long myStartTime = 0;
55   private final boolean myInterruptedByOutOfMemory = false;
56   private boolean myInterruptedByOutOfTime = false;
57   private long myLastTestStartTime = 0;
58   private String myLastTestClass;
59   private int myRunTests = -1;
60   private boolean mySavingMemorySnapshot;
61
62   private static final int SAVE_MEMORY_SNAPSHOT = 1;
63   private static final int START_GUARD = 2;
64   private static final int RUN_GC = 4;
65   private static final int CHECK_MEMORY = 8;
66   private static final int FILTER_CLASSES = 16;
67
68   public static int ourMode = SAVE_MEMORY_SNAPSHOT | START_GUARD /*| RUN_GC | CHECK_MEMORY*/ | FILTER_CLASSES;
69   private int myLastTestTestMethodCount = 0;
70   public static final int MAX_FAILURE_TEST_COUNT = 150;
71
72   public int countTestCases() {
73     List classes = myTestCaseLoader.getClasses();
74
75     int count = 0;
76
77     for (final Object aClass : classes) {
78       Class testCaseClass = (Class)aClass;
79       Test test = getTest(testCaseClass);
80       if (test != null) count += test.countTestCases();
81     }
82
83     return count;
84   }
85
86   private void beforeFirstTest() {
87     if ((ourMode & START_GUARD) != 0) {
88       Thread timeAndMemoryGuard = new Thread() {
89         public void run() {
90           System.out.println("Starting Time and Memory Guard");
91           while (true) {
92             try {
93               try {
94                 Thread.sleep(10000);
95               }
96               catch (InterruptedException e) {
97                 e.printStackTrace();
98               }
99               // check for time spent on current test
100               if (myLastTestStartTime != 0) {
101                 long currTime = System.currentTimeMillis();
102                 long secondsSpent = (currTime - myLastTestStartTime) / 1000L;
103                 Thread currentThread = getCurrentThread();
104                 if (!mySavingMemorySnapshot) {
105                   if (secondsSpent > PlatformTestCase.ourTestTime * myLastTestTestMethodCount) {
106                     UsefulTestCase.printThreadDump();
107                     System.out.println("Interrupting current Test (out of time)! Test class: "+ myLastTestClass +" Seconds spent = " + secondsSpent);
108                     myInterruptedByOutOfTime = true;
109                     if (currentThread != null) {
110                       currentThread.interrupt();
111                       if (!currentThread.isInterrupted()) {
112                         currentThread.stop(new RuntimeException("Current Test Interrupted: OUT OF TIME!"));
113                       }
114
115                       break;
116                     }
117                   }
118                 }
119               }
120             }
121             catch (Exception e) {
122               e.printStackTrace();
123             }
124           }
125           System.out.println("Time and Memory Guard finished.");
126         }
127       };
128       timeAndMemoryGuard.setDaemon(true);
129       timeAndMemoryGuard.start();
130     }
131     myStartTime = System.currentTimeMillis();
132   }
133
134   private static Thread getCurrentThread() {
135     if (PlatformTestCase.ourTestThread != null) {
136       return PlatformTestCase.ourTestThread;
137     }
138     else if (LightPlatformTestCase.ourTestThread != null) {
139       return LightPlatformTestCase.ourTestThread;
140     }
141     else {
142       return null;
143     }
144   }
145
146   private void addErrorMessage(TestResult testResult, String message) {
147     String processedTestsMessage = myRunTests <= 0 ? "Noone test was run" : myRunTests + " tests processed";
148     try {
149       testResult.startTest(this);
150       testResult.addError(this, new Throwable(processedTestsMessage + " before: " + message));
151       testResult.endTest(this);
152     }
153     catch (Exception e) {
154       e.printStackTrace();
155     }
156   }
157
158   public void run(final TestResult testResult) {
159     List classes = myTestCaseLoader.getClasses();
160     int totalTests = classes.size();
161     for (final Object aClass : classes) {
162       runNextTest(testResult, totalTests, (Class)aClass);
163       if (testResult.shouldStop()) break;
164     }
165     tryGc(10);
166   }
167
168   private void runNextTest(final TestResult testResult, int totalTests, Class testCaseClass) {
169     myRunTests++;
170     if (!checkAvaliableMemory(35, testResult)) {
171       testResult.stop();
172       return;
173     }
174     if (testResult.errorCount() + testResult.failureCount() > MAX_FAILURE_TEST_COUNT) {
175       addErrorMessage(testResult, "Too many errors. Tests stopped. Total " + myRunTests + " of " + totalTests + " tests run");
176       testResult.stop();
177       return;
178     }
179     if (myStartTime == 0) {
180       boolean ourClassLoader = getClass().getClassLoader().getClass().getName().startsWith("com.intellij.");
181       if (!ourClassLoader) {
182         beforeFirstTest();
183       }
184     }
185     else {
186       if (myInterruptedByOutOfMemory) {
187         addErrorMessage(testResult,
188                         "Current Test Interrupted: OUT OF MEMORY! Class = " + myLastTestClass + " Total " + myRunTests + " of " +
189                         totalTests +
190                         " tests run");
191         testResult.stop();
192         return;
193       }
194       else if (myInterruptedByOutOfTime) {
195         addErrorMessage(testResult,
196                         "Current Test Interrupted: OUT OF TIME! Class = " + myLastTestClass + " Total " + myRunTests + " of " +
197                         totalTests +
198                         " tests run");
199         testResult.stop();
200         return;
201       }
202     }
203
204     System.out.println("\nRunning " + testCaseClass.getName());
205     LOG.info("Running " + testCaseClass.getName());
206     final Test test = getTest(testCaseClass);
207
208     if (test == null) return;
209
210     myLastTestClass = null;
211
212     myLastTestClass = testCaseClass.getName();
213     myLastTestStartTime = System.currentTimeMillis();
214     myLastTestTestMethodCount = test.countTestCases();
215
216     try {
217       try {
218         Thread.sleep(100);
219       }
220       catch (InterruptedException e) {
221         e.printStackTrace();
222       }
223       test.run(testResult);
224       try {
225         final Application app = ApplicationManager.getApplication();
226         if (app != null) {
227           app.invokeAndWait(new Runnable() {
228             public void run() {
229               try {
230                 app.runWriteAction(new Runnable() {
231                   public void run() {
232                     //todo[myakovlev] is it necessary?
233                     FileDocumentManager manager = FileDocumentManager.getInstance();
234                     if (manager instanceof FileDocumentManagerImpl) {
235                       ((FileDocumentManagerImpl)manager).dropAllUnsavedDocuments();
236                     }
237                   }
238                 });
239               }
240               catch (Throwable e) {
241                 e.printStackTrace(System.err);
242               }
243             }
244           }, ModalityState.NON_MODAL);
245         }
246       }
247       catch (Exception e) {
248         e.printStackTrace();
249       }
250     }
251     catch (Throwable t) {
252       if (t instanceof OutOfMemoryError) {
253         if ((ourMode & SAVE_MEMORY_SNAPSHOT) != 0) {
254           try {
255             mySavingMemorySnapshot = true;
256             System.out.println("OutOfMemoryError detected. Saving memory snapshot started");
257           }
258           finally {
259             System.out.println("Saving memory snapshot finished");
260             mySavingMemorySnapshot = false;
261           }
262         }
263       }
264       testResult.addError(test, t);
265     }
266   }
267
268   private boolean checkAvaliableMemory(int neededMemory, TestResult testResult) {
269     if ((ourMode & CHECK_MEMORY) == 0) return true;
270
271     boolean possibleOutOfMemoryError = possibleOutOfMemory(neededMemory);
272     if (possibleOutOfMemoryError) {
273       tryGc(5);
274       possibleOutOfMemoryError = possibleOutOfMemory(neededMemory);
275       if (possibleOutOfMemoryError) {
276         System.out.println("OutOfMemoryError: dumping memory");
277         Runtime runtime = Runtime.getRuntime();
278         long total = runtime.totalMemory();
279         long free = runtime.freeMemory();
280         String errorMessage = "Too much memory used. Total: " + total + " free: " + free + " used: " + (total - free) + "\n";
281         addErrorMessage(testResult, errorMessage);
282       }
283     }
284     return !possibleOutOfMemoryError;
285   }
286
287   private static boolean possibleOutOfMemory(int neededMemory) {
288     Runtime runtime = Runtime.getRuntime();
289     long maxMemory = runtime.maxMemory();
290     long realFreeMemory = runtime.freeMemory() + (maxMemory - runtime.totalMemory());
291     long meg = 1024 * 1024;
292     long needed = neededMemory * meg;
293     boolean possibleOutOfMemoryError = realFreeMemory < needed;
294     return possibleOutOfMemoryError;
295   }
296
297   private static Test getTest(Class testCaseClass) {
298     if ((testCaseClass.getModifiers() & Modifier.PUBLIC) == 0) return null;
299
300     try {
301       Method suiteMethod = testCaseClass.getMethod("suite", ArrayUtil.EMPTY_CLASS_ARRAY);
302       return (Test)suiteMethod.invoke(null, ArrayUtil.EMPTY_CLASS_ARRAY);
303     }
304     catch (NoSuchMethodException e) {
305       if (TestRunnerUtil.isJUnit4TestClass(testCaseClass)) {
306         return new JUnit4TestAdapter(testCaseClass);
307       }
308       return new TestSuite(testCaseClass){
309         public void addTest(Test test) {
310           if (!(test instanceof TestCase))  {
311             super.addTest(test);
312           } else {
313             Method method = findTestMethod((TestCase)test);
314             if (method == null || !TestCaseLoader.isBombed(method)) {
315               super.addTest(test);
316             }
317           }
318
319         }
320
321         private Method findTestMethod(final TestCase testCase) {
322           try {
323             return testCase.getClass().getMethod(testCase.getName());
324           }
325           catch (NoSuchMethodException e1) {
326             return null;
327           }
328         }
329       };
330     }
331     catch (Exception e) {
332       System.err.println("Failed to execute suite ()");
333       e.printStackTrace();
334     }
335
336     return null;
337   }
338
339   private static String [] getClassRoots() {
340     String testRoots = System.getProperty("test.roots");
341     if (testRoots != null) {
342       return testRoots.split(";");
343     }
344     final String[] roots = ExternalClasspathClassLoader.getRoots();
345     return roots != null ? roots : System.getProperty("java.class.path").split(File.pathSeparator);
346   }
347
348   public TestAll(String packageRoot) throws Throwable {
349     this(packageRoot, getClassRoots());
350   }
351
352   public TestAll(String packageRoot, String... classRoots) throws IOException, ClassNotFoundException {
353     myTestCaseLoader = new TestCaseLoader((ourMode & FILTER_CLASSES) != 0 ? "tests/testGroups.properties" : "");
354
355     if (classRoots.length > 0) {
356       myTestCaseLoader.addClassIfTestCase(Class.forName("_FirstInSuiteTest"));
357     }
358
359     for (String classRoot : classRoots) {
360       ClassFinder classFinder = new ClassFinder(new File(classRoot), packageRoot);
361       myTestCaseLoader.loadTestCases(classFinder.getClasses());
362     }
363
364     System.out.println("Number of test classes found: " + myTestCaseLoader.getClasses().size());
365   }
366
367   // [myakovlev] Do not delete - it is for debugging
368   public static void tryGc(int times) {
369     if ((ourMode & RUN_GC) == 0) return;
370
371     for (int qqq = 1; qqq < times; qqq++) {
372       try {
373         Thread.sleep(qqq * 1000);
374       }
375       catch (InterruptedException e) {
376         e.printStackTrace();
377       }
378       System.gc();
379       //long mem = Runtime.getRuntime().totalMemory();
380       System.out.println("Runtime.getRuntime().totalMemory() = " + Runtime.getRuntime().totalMemory());
381     }
382   }
383
384 }