Snapshot aea001abfc1b38fec3a821bcd5174cc77dc75787 from master branch of git://git.jetbrains.org/idea/community.git

Change-Id: Icdea2a2bd7ad43b4d05967b1f0479db3bda1c93c
diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java
index 6d40070..ccaa2cb 100644
--- a/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java
+++ b/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java
@@ -119,16 +119,17 @@
 
       @Override
       @NotNull
-      public InspectionTool[] getInspectionTools(PsiElement element) {
+      public InspectionToolWrapper[] getInspectionTools(PsiElement element) {
         Collection<InspectionToolWrapper> values = myAvailableTools.values();
-        return values.toArray(new InspectionTool[values.size()]);
+        return values.toArray(new InspectionToolWrapper[values.size()]);
       }
 
+      @NotNull
       @Override
-      public List<ToolsImpl> getAllEnabledInspectionTools(Project project) {
-        List<ToolsImpl> result = new ArrayList<ToolsImpl>();
-        for (InspectionProfileEntry entry : getInspectionTools(null)) {
-          result.add(new ToolsImpl(entry, entry.getDefaultLevel(), true));
+      public List<Tools> getAllEnabledInspectionTools(Project project) {
+        List<Tools> result = new ArrayList<Tools>();
+        for (InspectionToolWrapper toolWrapper : getInspectionTools(null)) {
+          result.add(new ToolsImpl(toolWrapper, toolWrapper.getDefaultLevel(), true));
         }
         return result;
       }
@@ -140,12 +141,12 @@
 
       @Override
       public HighlightDisplayLevel getErrorLevel(@NotNull HighlightDisplayKey key, PsiElement element) {
-        final InspectionProfileEntry localInspectionTool = myAvailableTools.get(key.toString());
+        final InspectionToolWrapper localInspectionTool = myAvailableTools.get(key.toString());
         return localInspectionTool != null ? localInspectionTool.getDefaultLevel() : HighlightDisplayLevel.WARNING;
       }
 
       @Override
-      public InspectionTool getInspectionTool(@NotNull String shortName, @NotNull PsiElement element) {
+      public InspectionToolWrapper getInspectionTool(@NotNull String shortName, @NotNull PsiElement element) {
         return myAvailableTools.get(shortName);
       }
     };
@@ -192,14 +193,9 @@
     //((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersDisposed();
   }
 
-  protected void enableInspectionTool(InspectionProfileEntry tool){
-    InspectionToolWrapper wrapper = InspectionToolRegistrar.wrapTool(tool);
-    final String shortName = wrapper.getShortName();
-    final HighlightDisplayKey key = HighlightDisplayKey.find(shortName);
-    if (key == null) {
-      HighlightDisplayKey.register(shortName, wrapper.getDisplayName(), ((LocalInspectionToolWrapper)wrapper).getID());
-    }
-    myAvailableTools.put(shortName, wrapper);
+  protected void enableInspectionTool(@NotNull InspectionProfileEntry tool) {
+    InspectionToolWrapper toolWrapper = InspectionToolRegistrar.wrapTool(tool);
+    LightPlatformTestCase.enableInspectionTool(myAvailableTools, toolWrapper);
   }
 
   protected void enableInspectionToolsFromProvider(InspectionToolProvider toolProvider){
diff --git a/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java
new file mode 100644
index 0000000..e1f9857
--- /dev/null
+++ b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java
@@ -0,0 +1,531 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.RemoteStateState;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.impl.*;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.*;
+import com.intellij.execution.executors.DefaultDebugExecutor;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.ui.UIUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.StringTokenizer;
+
+public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCase {
+  protected DebuggerSession myDebuggerSession;
+  private StringBuffer myConsoleBuffer;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+  }
+
+  @Override
+  protected void initApplication() throws Exception {
+    super.initApplication();
+    setTestJDK();
+    DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
+    DebuggerSettings.getInstance().SKIP_CONSTRUCTORS = false;
+    DebuggerSettings.getInstance().SKIP_GETTERS      = false;
+    NodeRendererSettings.getInstance().getClassRenderer().SHOW_DECLARED_TYPE = true;
+  }
+
+  @Override
+  protected void runTest() throws Throwable {
+    super.runTest();
+    if(getDebugProcess() != null) {
+      getDebugProcess().getExecutionResult().getProcessHandler().startNotify();
+      waitProcess(getDebugProcess().getExecutionResult().getProcessHandler());
+      waitForCompleted();
+      disposeSession(myDebuggerSession);
+      assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getExecutionResult().getProcessHandler()));
+      myDebuggerSession = null;
+    }
+    if(myConsoleBuffer != null) {
+      //println("", b);
+      //println("Console output:", b);
+      //println(myConsoleBuffer.toString(), b);
+    }
+    checkTestOutput();
+  }
+
+  protected void checkTestOutput() throws Exception {
+    getChecker().checkValid(getTestProjectJdk());
+  }
+
+  protected void disposeSession(final DebuggerSession debuggerSession) throws InterruptedException, InvocationTargetException {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        debuggerSession.dispose();
+      }
+    });
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    super.tearDown();
+    myConsoleBuffer = null;
+  }
+
+  protected void createLocalProcess(String className) throws ExecutionException, InterruptedException, InvocationTargetException {
+    LOG.assertTrue(myDebugProcess == null);
+    myDebuggerSession = createLocalProcess(DebuggerSettings.SOCKET_TRANSPORT, createJavaParameters(className));
+    myDebugProcess = myDebuggerSession.getProcess();
+  }
+
+  protected DebuggerSession createLocalSession(final JavaParameters javaParameters, final String sessionName) throws ExecutionException, InterruptedException {
+    createBreakpoints(javaParameters.getMainClass());
+    DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT;
+
+    GenericDebuggerRunnerSettings debuggerRunnerSettings = new GenericDebuggerRunnerSettings();
+    debuggerRunnerSettings.LOCAL      = true;
+
+    final RemoteConnection debugParameters = DebuggerManagerImpl.createDebugParameters(javaParameters, debuggerRunnerSettings, false);
+
+    RunnerSettings<JDOMExternalizable> runnerSettings = new RunnerSettings<JDOMExternalizable>(debuggerRunnerSettings, null);
+
+    final JavaCommandLineState javaCommandLineState = new JavaCommandLineState(new ExecutionEnvironment(new MockConfiguration(), myProject,
+                                                                                                        runnerSettings, null, null)){
+      @Override
+      protected JavaParameters createJavaParameters() {
+        return javaParameters;
+      }
+
+      @Override
+      protected GeneralCommandLine createCommandLine() throws ExecutionException {
+        return CommandLineBuilder.createFromJavaParameters(getJavaParameters());
+      }
+    };
+
+    final GenericDebuggerRunner runner = new GenericDebuggerRunner();
+
+    ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          myDebuggerSession = DebuggerManagerEx.getInstanceEx(myProject).attachVirtualMachine(new DefaultDebugExecutor(),
+            runner, new MockConfiguration(), javaCommandLineState, debugParameters, false);
+        }
+        catch (ExecutionException e) {
+          LOG.error(e);
+        }
+      }
+    }, ModalityState.defaultModalityState());
+    myDebugProcess = myDebuggerSession.getProcess();
+
+    //myConsoleBuffer = new StringBuffer();
+
+    myDebugProcess.addProcessListener(new ProcessAdapter() {
+      @Override
+      public void onTextAvailable(ProcessEvent event, Key outputType) {
+        //myConsoleBuffer.append(event.getText());
+        print(event.getText(), outputType);
+      }
+    });
+
+    assertNotNull(myDebuggerSession);
+    assertNotNull(myDebugProcess);
+
+    return myDebuggerSession;
+  }
+
+
+  protected DebuggerSession createLocalProcess(int transport, final JavaParameters javaParameters) throws ExecutionException, InterruptedException, InvocationTargetException {
+    createBreakpoints(javaParameters.getMainClass());
+    final DebuggerSession[] debuggerSession = new DebuggerSession[]{null};
+
+    DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = transport;
+
+    GenericDebuggerRunnerSettings debuggerRunnerSettings = new GenericDebuggerRunnerSettings();
+    debuggerRunnerSettings.LOCAL = true;
+    debuggerRunnerSettings.DEBUG_PORT = "3456";
+
+    RunnerSettings<JDOMExternalizable> runnerSettings = new RunnerSettings<JDOMExternalizable>(debuggerRunnerSettings, null);
+
+    final JavaCommandLineState javaCommandLineState = new JavaCommandLineState(new ExecutionEnvironment(new MockConfiguration(), myProject,
+                                                                                                        runnerSettings, null, null)) {
+      @Override
+      protected JavaParameters createJavaParameters() {
+        return javaParameters;
+      }
+
+      @Override
+      protected GeneralCommandLine createCommandLine() throws ExecutionException {
+        return CommandLineBuilder.createFromJavaParameters(getJavaParameters());
+      }
+    };
+
+    final RemoteConnection debugParameters =
+      DebuggerManagerImpl.createDebugParameters(javaCommandLineState.getJavaParameters(), debuggerRunnerSettings, true);
+
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          debuggerSession[0] = attachVirtualMachine(javaCommandLineState, debugParameters, false);
+        }
+        catch (ExecutionException e) {
+          fail(e.getMessage());
+        }
+      }
+    });
+
+    final ExecutionResult executionResult = debuggerSession[0].getProcess().getExecutionResult();
+    debuggerSession[0].getProcess().addProcessListener(new ProcessAdapter() {
+      @Override
+      public void onTextAvailable(ProcessEvent event, Key outputType) {
+        print(event.getText(), outputType);
+      }
+    });
+
+    DebugProcessImpl process =
+      (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(executionResult.getProcessHandler());
+    assertNotNull(process);
+    return debuggerSession[0];
+  }
+
+
+  protected DebuggerSession createRemoteProcess(final int transport, final boolean serverMode, JavaParameters javaParameters)
+          throws ExecutionException, InterruptedException, InvocationTargetException {
+    boolean useSockets = transport == DebuggerSettings.SOCKET_TRANSPORT;
+
+    RemoteConnection remoteConnection = new RemoteConnection(
+      useSockets,
+      "127.0.0.1",
+      "3456",
+      serverMode);
+
+    String launchCommandLine = remoteConnection.getLaunchCommandLine();
+
+    launchCommandLine = StringUtil.replace(launchCommandLine,  RemoteConnection.ONTHROW, "");
+    launchCommandLine = StringUtil.replace(launchCommandLine,  RemoteConnection.ONUNCAUGHT, "");
+
+    launchCommandLine = StringUtil.replace(launchCommandLine, "suspend=n", "suspend=y");
+
+    println(launchCommandLine, ProcessOutputTypes.SYSTEM);
+
+    JavaParameters parameters = javaParameters;
+
+    for(StringTokenizer tokenizer = new StringTokenizer(launchCommandLine);tokenizer.hasMoreTokens();) {
+      String token = tokenizer.nextToken();
+      parameters.getVMParametersList().add(token);
+    }
+
+    GeneralCommandLine commandLine = CommandLineBuilder.createFromJavaParameters(parameters);
+
+
+    DebuggerSession debuggerSession;
+
+    if(serverMode) {
+      debuggerSession = attachVM(remoteConnection, false);
+      commandLine.createProcess();
+    } else {
+      commandLine.createProcess();
+      debuggerSession = attachVM(remoteConnection, true);
+    }
+
+    ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult();
+    DebugProcessImpl process = (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject)
+      .getDebugProcess(executionResult.getProcessHandler());
+
+    assertNotNull(process);
+    return debuggerSession;
+  }
+
+  protected DebuggerSession attachVM(final RemoteConnection remoteConnection, final boolean pollConnection)
+          throws InvocationTargetException, InterruptedException {
+    final RunnerSettings<JDOMExternalizable> runnerSettings = new RunnerSettings<JDOMExternalizable>(null, null);
+    final RemoteState remoteState = new RemoteStateState(myProject, remoteConnection, runnerSettings, null);
+
+    final DebuggerSession[] debuggerSession = new DebuggerSession[1];
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          debuggerSession[0] = attachVirtualMachine(remoteState, remoteConnection, pollConnection);
+        }
+        catch (ExecutionException e) {
+          fail(e.getMessage());
+        }
+      }
+    });
+    debuggerSession[0].getProcess().getExecutionResult().getProcessHandler().addProcessListener(new ProcessAdapter() {
+      @Override
+      public void onTextAvailable(ProcessEvent event, Key outputType) {
+        print(event.getText(), outputType);
+      }
+    });
+    return debuggerSession[0];
+  }
+
+  protected void createBreakpoints(final String className) {
+    final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
+      @Override
+      public PsiClass compute() {
+        return JavaPsiFacade.getInstance(myProject).findClass(className, GlobalSearchScope.allScope(myProject));
+      }
+    });
+
+    createBreakpoints(psiClass.getContainingFile());
+
+  }
+
+  protected EvaluationContextImpl createEvaluationContext(final SuspendContextImpl suspendContext) {
+    try {
+      return new EvaluationContextImpl(
+        suspendContext,
+        suspendContext.getFrameProxy(),
+        suspendContext.getFrameProxy().thisObject());
+    }
+    catch (EvaluateException e) {
+      error(e);
+      return null;
+    }
+  }
+
+  protected void waitForCompleted() {
+    final SynchronizationBasedSemaphore s = new SynchronizationBasedSemaphore();
+    s.down();
+
+    final InvokeThread.WorkerThreadRequest request = getDebugProcess().getManagerThread().getCurrentRequest();
+    final Thread thread = new Thread("Joining "+request) {
+      @Override
+      public void run() {
+        try {
+          request.join();
+        }
+        catch (Exception e) {
+        }
+      }
+    };
+    thread.start();
+    if(request.isDone()) {
+      thread.interrupt();
+    }
+      waitFor(new Runnable() {
+        @Override
+        public void run() {
+          try {
+            thread.join();
+          }
+          catch (InterruptedException e) {
+          }
+        }
+      });
+
+    invokeRatherLater(new DebuggerCommandImpl() {
+      @Override
+      protected void action() throws Exception {
+        LOG.assertTrue(false);
+      }
+
+      @Override
+      protected void commandCancelled() {
+        //We wait for invokeRatherLater's
+        invokeRatherLater(new DebuggerCommandImpl() {
+          @Override
+          protected void action() throws Exception {
+            LOG.assertTrue(false);
+          }
+
+          @Override
+          protected void commandCancelled() {
+            s.up();
+          }
+        });
+      }
+    });
+
+    waitFor(new Runnable() {
+      @Override
+      public void run() {
+        s.waitFor();
+      }
+    });
+  }
+
+  public DebuggerContextImpl createDebuggerContext(final SuspendContextImpl suspendContext, StackFrameProxyImpl stackFrame) {
+    final DebuggerSession[] session = new DebuggerSession[1];
+
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        session[0] = DebuggerManagerEx.getInstanceEx(myProject).getSession(suspendContext.getDebugProcess());
+      }
+    });
+
+    DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(
+            session[0],
+            suspendContext,
+            stackFrame != null ? stackFrame.threadProxy() : null,
+            stackFrame);
+    debuggerContext.initCaches();
+    return debuggerContext;
+  }
+
+  public DebuggerContextImpl createDebuggerContext(final SuspendContextImpl suspendContext) {
+    return createDebuggerContext(suspendContext, suspendContext.getFrameProxy());
+  }
+
+  protected void createBreakpointInHelloWorld() {
+    DebuggerInvocationUtil.invokeAndWait(myProject, new Runnable() {
+      @Override
+      public void run() {
+        BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
+        PsiClass psiClass = JavaPsiFacade.getInstance(myProject).findClass("HelloWorld", GlobalSearchScope.allScope(myProject));
+        Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiClass.getContainingFile());
+        breakpointManager.addLineBreakpoint(document, 3);
+      }
+    }, ApplicationManager.getApplication().getDefaultModalityState());
+  }
+
+  protected void createHelloWorldProcessWithBreakpoint() throws ExecutionException, InterruptedException, InvocationTargetException {
+    createLocalProcess("HelloWorld");
+
+    createBreakpointInHelloWorld();
+  }
+
+  @Override
+  protected DebugProcessImpl getDebugProcess() {
+    return myDebuggerSession != null ? myDebuggerSession.getProcess() : null;
+  }
+
+  public DebuggerSession getDebuggerSession() {
+    return myDebuggerSession;
+  }
+
+  protected DebuggerSession attachVirtualMachine(RunProfileState state, RemoteConnection remoteConnection, boolean pollConnection) throws ExecutionException {
+    return DebuggerManagerEx.getInstanceEx(myProject).attachVirtualMachine(new DefaultDebugExecutor(), new GenericDebuggerRunner(), new MockConfiguration(), state, remoteConnection, pollConnection);
+  }
+
+  private static class MockConfiguration implements ModuleRunConfiguration {
+    @Override
+    @NotNull
+    public Module[] getModules() {
+      return Module.EMPTY_ARRAY;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public Icon getIcon() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public ConfigurationFactory getFactory() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void setName(final String name) {
+      //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public Project getProject() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    @NotNull
+    public ConfigurationType getType() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public JDOMExternalizable createRunnerSettings(final ConfigurationInfoProvider provider) {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public SettingsEditor<JDOMExternalizable> getRunnerSettingsEditor(final ProgramRunner runner) {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public RunConfiguration clone() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public int getUniqueID() {
+      return 0;
+    }
+
+    @Override
+    public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public String getName() {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void checkConfiguration() throws RuntimeConfigurationException {
+      //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void readExternal(final Element element) throws InvalidDataException {
+      //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void writeExternal(final Element element) throws WriteExternalException {
+      //To change body of implemented methods use File | Settings | File Templates.
+    }
+  }
+
+}
diff --git a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java
new file mode 100644
index 0000000..bccbfe5
--- /dev/null
+++ b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger;
+
+import com.intellij.debugger.engine.*;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.debugger.engine.events.DebuggerCommandImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.impl.DebuggerManagerImpl;
+import com.intellij.debugger.impl.PositionUtil;
+import com.intellij.debugger.impl.PrioritizedTask;
+import com.intellij.debugger.impl.SynchronizationBasedSemaphore;
+import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.breakpoints.Breakpoint;
+import com.intellij.debugger.ui.breakpoints.BreakpointManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionTestCase;
+import com.intellij.execution.configurations.RemoteConnection;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.ui.classFilter.ClassFilter;
+import com.intellij.util.IJSwingUtilities;
+import com.intellij.util.ui.UIUtil;
+import com.sun.jdi.Method;
+import com.sun.jdi.ThreadReference;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCase {
+  private DebugProcessListener myPauseScriptListener = null;
+  private final List<SuspendContextRunnable> myScriptRunnables = new ArrayList<SuspendContextRunnable>();
+  private final SynchronizationBasedSemaphore myScriptRunnablesSema = new SynchronizationBasedSemaphore();
+  protected static final int RATHER_LATER_INVOKES_N = 10;
+  public DebugProcessImpl myDebugProcess = null;
+
+
+  private class InvokeRatherLaterRequest {
+    private final DebuggerCommandImpl myDebuggerCommand;
+    private final DebugProcessImpl myDebugProcess;
+    int invokesN = 0;
+
+    public InvokeRatherLaterRequest(DebuggerCommandImpl debuggerCommand, DebugProcessImpl debugProcess) {
+      myDebuggerCommand = debuggerCommand;
+      myDebugProcess = debugProcess;
+    }
+  }
+
+  public List<InvokeRatherLaterRequest> myRatherLaterRequests = new ArrayList<InvokeRatherLaterRequest>();
+
+  protected DebugProcessImpl getDebugProcess() {
+    return myDebugProcess;
+  }
+
+  protected String readValue(String comment, String valueName) {
+    int valueStart = comment.indexOf(valueName);
+    if (valueStart == -1) return null;
+
+    int valueEnd = comment.indexOf(')', valueStart);
+    return comment.substring(valueStart + valueName.length() + 1, valueEnd);
+  }
+
+  protected void resume(SuspendContextImpl context) {
+    DebugProcessImpl debugProcess = context.getDebugProcess();
+    debugProcess.getManagerThread().schedule(debugProcess.createResumeCommand(context, PrioritizedTask.Priority.LOW));
+  }
+
+  protected void waitBreakpoints() {
+    myScriptRunnablesSema.down();
+    waitFor(new Runnable() {
+      @Override
+      public void run() {
+        myScriptRunnablesSema.waitFor();
+      }
+    });
+  }
+
+  protected void onBreakpoint(SuspendContextRunnable runnable) {
+    if (myPauseScriptListener == null) {
+      final DebugProcessImpl debugProcess = getDebugProcess();
+      
+      assertTrue("Debug process was not started", debugProcess != null);
+      
+      myPauseScriptListener = new DelayedEventsProcessListener(
+        new DebugProcessAdapterImpl() {
+          @Override
+          public void paused(SuspendContextImpl suspendContext) {
+            try {
+              if (myScriptRunnables.isEmpty()) {
+                print("resuming ", ProcessOutputTypes.SYSTEM);
+                printContext(suspendContext);
+                resume(suspendContext);
+                return;
+              }
+              SuspendContextRunnable suspendContextRunnable = myScriptRunnables.remove(0);
+              suspendContextRunnable.run(suspendContext);
+            }
+            catch (Exception e) {
+              error(e);
+            }
+
+            if (myScriptRunnables.isEmpty()) {
+              myScriptRunnablesSema.up();
+            }
+          }
+
+          //executed in manager thread
+          @Override
+          public void resumed(SuspendContextImpl suspendContext) {
+            final SuspendContextImpl pausedContext = debugProcess.getSuspendManager().getPausedContext();
+            if (pausedContext != null) {
+              debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(pausedContext) {
+                @Override
+                public void contextAction() throws Exception {
+                  paused(pausedContext);
+                }
+              });
+            }
+          }
+        }
+      );
+      debugProcess.addDebugProcessListener(myPauseScriptListener);
+    }
+    myScriptRunnables.add(runnable);
+  }
+
+  protected void printFrameProxy(StackFrameProxyImpl frameProxy) throws EvaluateException {
+    int frameIndex = frameProxy.getFrameIndex();
+    Method method = frameProxy.location().method();
+
+    println("frameProxy(" + frameIndex + ") = " + method, ProcessOutputTypes.SYSTEM);
+  }
+
+  protected void printContext(final StackFrameContext context) {
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      @Override
+      public void run() {
+        if (context.getFrameProxy() != null) {
+          SourcePosition sourcePosition = PositionUtil.getSourcePosition(context);
+          println(sourcePosition.getFile().getVirtualFile().getName() + ":" + sourcePosition.getLine(), ProcessOutputTypes.SYSTEM);
+        }
+        else {
+          println("Context thread is null", ProcessOutputTypes.SYSTEM);
+        }
+      }
+    });
+  }
+
+  protected void invokeRatherLater(SuspendContextImpl context, final Runnable runnable) {
+    invokeRatherLater(new SuspendContextCommandImpl(context) {
+      @Override
+      public void contextAction() throws Exception {
+        DebuggerInvocationUtil.invokeLater(myProject, runnable);
+      }
+    });
+  }
+
+  protected void pumpSwingThread() {
+    LOG.assertTrue(SwingUtilities.isEventDispatchThread());
+
+    final InvokeRatherLaterRequest request = myRatherLaterRequests.get(0);
+    request.invokesN++;
+
+    if (request.invokesN == RATHER_LATER_INVOKES_N) {
+      myRatherLaterRequests.remove(0);
+      if (!myRatherLaterRequests.isEmpty()) pumpSwingThread();
+    }
+
+    if (request.myDebuggerCommand instanceof SuspendContextCommandImpl) {
+      request.myDebugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(
+          ((SuspendContextCommandImpl)request.myDebuggerCommand).getSuspendContext()) {
+          @Override
+          public void contextAction() throws Exception {
+            pumpDebuggerThread(request);
+          }
+
+          @Override
+          protected void commandCancelled() {
+            pumpDebuggerThread(request);
+          }
+        });
+    }
+    else {
+      request.myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() {
+          @Override
+          protected void action() throws Exception {
+            pumpDebuggerThread(request);
+          }
+
+          @Override
+          protected void commandCancelled() {
+            pumpDebuggerThread(request);
+          }
+        });
+    }
+  }
+
+  private void pumpDebuggerThread(final InvokeRatherLaterRequest request) {
+    if (request.invokesN == RATHER_LATER_INVOKES_N) {
+      request.myDebugProcess.getManagerThread().schedule(request.myDebuggerCommand);
+    }
+    else {
+      if (!SwingUtilities.isEventDispatchThread()) {
+        UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+          @Override
+          public void run() {
+            pumpSwingThread();
+          }
+        });
+      }
+      else {
+        SwingUtilities.invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            pumpSwingThread();
+          }
+        });
+      }
+    }
+  }
+
+  protected void invokeRatherLater(final DebuggerCommandImpl command) {
+    IJSwingUtilities.invoke(new Runnable() {
+      @Override
+      public void run() {
+        InvokeRatherLaterRequest request = new InvokeRatherLaterRequest(command, getDebugProcess());
+        myRatherLaterRequests.add(request);
+
+        if (myRatherLaterRequests.size() == 1) pumpSwingThread();
+      }
+    });
+  }
+
+
+  protected void error(Throwable th) {
+    fail(StringUtil.getThrowableText(th));
+  }
+
+  public void createBreakpoints(final PsiFile file) {
+    Runnable runnable = new Runnable() {
+      @Override
+      public void run() {
+        BreakpointManager breakpointManager = DebuggerManagerImpl.getInstanceEx(myProject).getBreakpointManager();
+        Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
+        int offset = -1;
+        for (; ;) {
+          offset = document.getText().indexOf("Breakpoint!", offset + 1);
+          if (offset == -1) break;
+
+          int commentLine = document.getLineNumber(offset);
+
+          String comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine));
+
+          Breakpoint breakpoint;
+
+          if (comment.indexOf("Method") != -1) {
+            breakpoint = breakpointManager.addMethodBreakpoint(document, commentLine + 1);
+            println("MethodBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
+          }
+          else if (comment.indexOf("Field") != -1) {
+            breakpoint = breakpointManager.addFieldBreakpoint(document, commentLine + 1, readValue(comment, "Field"));
+            println("FieldBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
+          }
+          else if (comment.indexOf("Exception") != -1) {
+            breakpoint = breakpointManager.addExceptionBreakpoint(readValue(comment, "Exception"), "");
+            println("ExceptionBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
+          }
+          else {
+            breakpoint = breakpointManager.addLineBreakpoint(document, commentLine + 1);
+            println("LineBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
+          }
+
+          String suspendPolicy = readValue(comment, "suspendPolicy");
+          if (suspendPolicy != null) {
+            breakpoint.SUSPEND = !DebuggerSettings.SUSPEND_NONE.equals(suspendPolicy);
+            breakpoint.SUSPEND_POLICY = suspendPolicy;
+            println("SUSPEND_POLICY = " + suspendPolicy, ProcessOutputTypes.SYSTEM);
+          }
+          String condition = readValue(comment, "Condition");
+          if (condition != null) {
+            breakpoint.CONDITION_ENABLED = true;
+            breakpoint.setCondition(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, condition));
+            println("Condition = " + condition, ProcessOutputTypes.SYSTEM);
+          }
+          String passCount = readValue(comment, "Pass count");
+          if (passCount != null) {
+            breakpoint.COUNT_FILTER_ENABLED = true;
+            breakpoint.COUNT_FILTER = Integer.parseInt(passCount);
+            println("Pass count = " + passCount, ProcessOutputTypes.SYSTEM);
+          }
+
+          String classFilters = readValue(comment, "Class filters");
+          if (classFilters != null) {
+            breakpoint.CLASS_FILTERS_ENABLED = true;
+            StringTokenizer tokenizer = new StringTokenizer(classFilters, " ,");
+            ArrayList<ClassFilter> lst = new ArrayList<ClassFilter>();
+
+            while (tokenizer.hasMoreTokens()) {
+              ClassFilter filter = new ClassFilter();
+              filter.setEnabled(true);
+              filter.setPattern(tokenizer.nextToken());
+              lst.add(filter);
+            }
+
+            breakpoint.setClassFilters(lst.toArray(new ClassFilter[lst.size()]));
+            println("Class filters = " + classFilters, ProcessOutputTypes.SYSTEM);
+          }
+        }
+      }
+    };
+    if (!SwingUtilities.isEventDispatchThread()) {
+      DebuggerInvocationUtil.invokeAndWait(myProject, runnable, ModalityState.defaultModalityState());
+    }
+    else {
+      runnable.run();
+    }
+  }
+
+  private Sdk getTestJdk() {
+    try {
+      ProjectJdkImpl jdk = (ProjectJdkImpl)JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().clone();
+      jdk.setName("JDK");
+      return jdk;
+    }
+    catch (CloneNotSupportedException e) {
+      LOG.error(e);
+      return null;
+    }
+  }
+
+  protected void setTestJDK() {
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+        Sdk jdk = ProjectJdkTable.getInstance().findJdk("JDK");
+        if (jdk != null) {
+          ProjectJdkTable.getInstance().removeJdk(jdk);
+        }
+
+        ProjectJdkTable.getInstance().addJdk(getTestJdk());
+      }
+    });
+  }
+
+  private class DelayedEventsProcessListener implements DebugProcessListener {
+    private final DebugProcessAdapterImpl myTarget;
+
+    public DelayedEventsProcessListener(DebugProcessAdapterImpl target) {
+      myTarget = target;
+    }
+
+    @Override
+    public void threadStarted(DebugProcess proc, ThreadReference thread) {
+    }
+
+    @Override
+    public void threadStopped(DebugProcess proc, ThreadReference thread) {
+    }
+
+    @Override
+    public void paused(final SuspendContext suspendContext) {
+      pauseExecution();
+      myTarget.paused(suspendContext);
+    }
+
+    @Override
+    public void resumed(final SuspendContext suspendContext) {
+      pauseExecution();
+      myTarget.resumed(suspendContext);
+    }
+
+    @Override
+    public void processDetached(final DebugProcess process, final boolean closedByUser) {
+      myTarget.processDetached(process, closedByUser);
+    }
+
+    @Override
+    public void processAttached(final DebugProcess process) {
+      myTarget.processAttached(process);
+    }
+
+    @Override
+    public void connectorIsReady() {
+      myTarget.connectorIsReady();
+    }
+
+    @Override
+    public void attachException(final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) {
+      myTarget.attachException(state, exception, remoteConnection);
+    }
+
+    private void pauseExecution() {
+      try {
+        Thread.sleep(10);
+      }
+      catch (InterruptedException ignored) {
+      }
+    }
+  }
+}
diff --git a/java/testFramework/src/com/intellij/debugger/impl/DescriptorTestCase.java b/java/testFramework/src/com/intellij/debugger/impl/DescriptorTestCase.java
new file mode 100644
index 0000000..0d6624d
--- /dev/null
+++ b/java/testFramework/src/com/intellij/debugger/impl/DescriptorTestCase.java
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.debugger.DebuggerTestCase;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.SuspendContextImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.debugger.settings.NodeRendererSettings;
+import com.intellij.debugger.ui.impl.watch.DebuggerTree;
+import com.intellij.debugger.ui.impl.watch.LocalVariableDescriptorImpl;
+import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
+import com.intellij.debugger.ui.tree.DebuggerTreeNode;
+import com.intellij.debugger.ui.tree.NodeDescriptor;
+import com.intellij.debugger.ui.tree.ValueDescriptor;
+import com.intellij.debugger.ui.tree.render.NodeRenderer;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.util.Pair;
+import com.sun.jdi.Value;
+
+import java.util.*;
+
+public abstract class DescriptorTestCase extends DebuggerTestCase {
+  private final List<Pair<NodeDescriptorImpl,List<String>>> myDescriptorLog = new ArrayList<Pair<NodeDescriptorImpl, List<String>>>();
+
+  public DescriptorTestCase() {
+    super();
+  }
+
+  protected NodeRenderer getToStringRenderer() {
+    return NodeRendererSettings.getInstance().getToStringRenderer();
+  }
+
+  protected NodeRenderer getMapRenderer() {
+    return getAlternateCollectionRenderer("Map");
+  }
+
+  private NodeRenderer getAlternateCollectionRenderer(final String name) {
+    final NodeRenderer[] renderers = NodeRendererSettings.getInstance().getAlternateCollectionRenderers();
+    for (int idx = 0; idx < renderers.length; idx++) {
+      NodeRenderer renderer = renderers[idx];
+      if (name.equals(renderer.getName())) {
+        return renderer;
+      }
+    }
+    return null;
+  }
+
+  protected NodeRenderer getMapEntryRenderer() {
+    return getAlternateCollectionRenderer("Map.Entry");
+  }
+
+  protected NodeRenderer getHexRenderer() {
+    return NodeRendererSettings.getInstance().getHexRenderer();
+  }
+
+  protected NodeRenderer getCollectionRenderer() {
+    return getAlternateCollectionRenderer("Collection");
+  }
+
+  @Override
+  protected void resume(final SuspendContextImpl suspendContext) {
+    final DebugProcessImpl localProcess = suspendContext.getDebugProcess();
+    invokeRatherLater(new SuspendContextCommandImpl(suspendContext) {
+      @Override
+      public void contextAction() throws Exception {
+        flushDescriptors();
+        localProcess.getManagerThread().schedule(localProcess.createResumeCommand(suspendContext, Priority.LOW));
+      }
+    });
+  }
+
+  protected void logDescriptor(final NodeDescriptorImpl descriptor, String text) {
+    Pair<NodeDescriptorImpl, List<String>> descriptorText = findDescriptorLog(descriptor);
+
+    if(descriptorText == null) {
+      ArrayList<String> allText = new ArrayList<String>();
+      allText.add(text);
+      descriptorText = new Pair<NodeDescriptorImpl, List<String>>(descriptor, allText);
+      myDescriptorLog.add(descriptorText);
+    }
+    else {
+      List<String> allText = descriptorText.getSecond();
+      if(!allText.get(allText.size() - 1).equals(text)) {
+        allText.add(text);
+      }
+    }
+  }
+
+  private Pair<NodeDescriptorImpl, List<String>> findDescriptorLog(final NodeDescriptorImpl descriptor) {
+    Pair<NodeDescriptorImpl, List<String>> descriptorText = null;
+    for (Iterator<Pair<NodeDescriptorImpl, List<String>>> iterator = myDescriptorLog.iterator(); iterator.hasNext();) {
+      Pair<NodeDescriptorImpl, List<String>> pair = iterator.next();
+      if(pair.getFirst() == descriptor) {
+        descriptorText = pair;
+        break;
+      }
+    }
+    return descriptorText;
+  }
+
+  protected void flushDescriptor(final NodeDescriptorImpl descriptor) {
+    Pair<NodeDescriptorImpl, List<String>> descriptorLog = findDescriptorLog(descriptor);
+    if(descriptorLog != null) {
+      printDescriptorLog(descriptorLog);
+      myDescriptorLog.remove(descriptorLog);
+    }
+  }
+
+  protected void flushDescriptors() {
+    for (Iterator<Pair<NodeDescriptorImpl, List<String>>> iterator = myDescriptorLog.iterator(); iterator.hasNext();) {
+      printDescriptorLog(iterator.next());
+    }
+    myDescriptorLog.clear();
+  }
+
+  private void printDescriptorLog(Pair<NodeDescriptorImpl, List<String>> pair) {
+    for (Iterator<String> it = pair.getSecond().iterator(); it.hasNext();) {
+      String text =  it.next();
+      print(text, ProcessOutputTypes.SYSTEM);
+    }
+  }
+
+  protected void disableRenderer(NodeRenderer renderer) {
+    renderer.setEnabled(false);
+  }
+
+  protected void enableRenderer(NodeRenderer renderer) {
+    renderer.setEnabled(true);
+  }
+
+  protected LocalVariableDescriptorImpl localVar(DebuggerTree frameTree,
+                                               EvaluationContextImpl evaluationContext,
+                                               String name) {
+    try {
+      StackFrameProxy frameProxy = evaluationContext.getFrameProxy();
+      LocalVariableDescriptorImpl local = frameTree.getNodeFactory().getLocalVariableDescriptor(null, frameProxy.visibleVariableByName(name));
+      local.setContext(evaluationContext);
+      return local;
+    } catch (EvaluateException e) {
+      error(e);
+      return null;
+    }
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    flushDescriptors();
+    super.tearDown();
+  }
+
+  protected void expandAll(final DebuggerTree tree, final Runnable runnable) {
+    doExpandAll(tree, runnable, new HashSet<Value>(), null);
+  }
+
+  protected static interface NodeFilter {
+    boolean shouldExpand(DebuggerTreeNode node);
+  }
+  
+  protected void expandAll(final DebuggerTree tree, final Runnable runnable, NodeFilter filter) {
+    doExpandAll(tree, runnable, new HashSet<Value>(), filter);
+  }
+
+  private void doExpandAll(final DebuggerTree tree, final Runnable runnable, final Set<Value> alreadyExpanded, final NodeFilter filter) {
+    invokeRatherLater(tree.getDebuggerContext().getSuspendContext(), new Runnable() {
+      @Override
+      public void run() {
+        boolean anyCollapsed = false;
+        for(int i = 0; i < tree.getRowCount(); i++) {
+          final DebuggerTreeNode treeNode = (DebuggerTreeNode)tree.getPathForRow(i).getLastPathComponent();
+          if(tree.isCollapsed(i) && !treeNode.isLeaf()) {
+            final NodeDescriptor nodeDescriptor = treeNode.getDescriptor();
+            boolean shouldExpand = filter == null? true : filter.shouldExpand(treeNode);
+            if (shouldExpand) {
+              // additional checks to prevent infinite expand
+              if (nodeDescriptor instanceof ValueDescriptor) {
+                final Value value = ((ValueDescriptor)nodeDescriptor).getValue();
+                shouldExpand = !alreadyExpanded.contains(value);
+                if (shouldExpand) {
+                  alreadyExpanded.add(value);
+                }
+              }
+            }
+            if (shouldExpand) {
+              anyCollapsed = true;
+              tree.expandRow(i);
+            }
+          }
+        }
+
+        if (anyCollapsed) {
+          doExpandAll(tree, runnable, alreadyExpanded, filter);
+        }
+        else {
+          runnable.run();
+        }
+      }
+    });
+  }
+}
diff --git a/java/testFramework/src/com/intellij/debugger/impl/OutputChecker.java b/java/testFramework/src/com/intellij/debugger/impl/OutputChecker.java
new file mode 100644
index 0000000..9c48ade
--- /dev/null
+++ b/java/testFramework/src/com/intellij/debugger/impl/OutputChecker.java
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.idea.IdeaLogger;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.HashMap;
+import junit.framework.Assert;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class OutputChecker {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.OutputChecker");
+
+  protected final String myAppPath;
+
+  public static final Key[] OUTPUT_ORDER = new Key[] {
+    ProcessOutputTypes.SYSTEM, ProcessOutputTypes.STDOUT, ProcessOutputTypes.STDERR
+  };
+  private Map<Key, StringBuffer> myBuffers;
+  protected String myTestName;
+
+
+  //ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
+  private static final Pattern JDI_BUG_OUTPUT_PATTERN_1 =
+    Pattern.compile("ERROR\\:\\s+JDWP\\s+Unable\\s+to\\s+get\\s+JNI\\s+1\\.2\\s+environment,\\s+jvm-\\>GetEnv\\(\\)\\s+return\\s+code\\s+=\\s+-2\n");
+  //JDWP exit error AGENT_ERROR_NO_JNI_ENV(183):  [../../../src/share/back/util.c:820]
+  private static final Pattern JDI_BUG_OUTPUT_PATTERN_2 =
+    Pattern.compile("JDWP\\s+exit\\s+error\\s+AGENT_ERROR_NO_JNI_ENV.*\\]\n");
+
+  public OutputChecker(String appPath) {
+    myAppPath = appPath;
+  }
+
+  public void init(String testName) {
+    IdeaLogger.ourErrorsOccurred = null;
+
+    testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
+    myTestName = testName;
+    synchronized (this) {
+      myBuffers = new HashMap<Key, StringBuffer>();
+    }
+  }
+
+  public void print(String s, Key outputType) {
+    synchronized (this) {
+      if (myBuffers != null) {
+        StringBuffer buffer = myBuffers.get(outputType);
+        if (buffer == null) {
+          myBuffers.put(outputType, buffer = new StringBuffer());
+        }
+        buffer.append(s);
+      }
+    }
+  }
+
+  public void println(String s, Key outputType) {
+    print(s + "\n", outputType);
+  }
+
+  public void checkValid(Sdk jdk) throws Exception {
+    checkValid(jdk, false);
+  }
+
+  public void checkValid(Sdk jdk, boolean sortClassPath) throws Exception {
+    if (IdeaLogger.ourErrorsOccurred != null) {
+      throw IdeaLogger.ourErrorsOccurred;
+    }
+
+    String actual = preprocessBuffer(jdk, buildOutputString(), sortClassPath);
+
+    File outs = new File(myAppPath + File.separator + "outs");
+    assert outs.exists() || outs.mkdirs() : outs;
+
+    File outFile = new File(outs, myTestName + ".out");
+    if (!outFile.exists()) {
+      if (SystemInfo.isWindows) {
+        final File winOut = new File(outs, myTestName + ".win.out");
+        if (winOut.exists()) {
+          outFile = winOut;
+        }
+      }
+      else if (SystemInfo.isUnix) {
+        final File unixOut = new File(outs, myTestName + ".unx.out");
+        if (unixOut.exists()) {
+          outFile = unixOut;
+        }
+      }
+    }
+
+    if (!outFile.exists()) {
+      FileOutputStream fos = new FileOutputStream(outFile, false);
+      try {
+        fos.write(actual.getBytes());
+      }
+      finally {
+        fos.close();
+      }
+      LOG.error("Test file created " + outFile.getPath() + "\n" + "**************** Don't forget to put it into VCS! *******************");
+    }
+    else {
+      String originalText = FileUtil.loadFile(outFile);
+      String expected = originalText;
+
+      expected = StringUtil.replace(expected, "\r\n", "\n");
+      expected = StringUtil.replace(expected, "\r", "\n");
+
+      if (!expected.equals(actual)) {
+        System.out.println("expected:");
+        System.out.println(originalText);
+        System.out.println("actual:");
+        System.out.println(actual);
+
+        Assert.assertEquals(originalText, actual);
+      }
+    }
+  }
+
+  private synchronized String buildOutputString() {
+    final StringBuilder result = new StringBuilder();
+    for (Key key : OUTPUT_ORDER) {
+      final StringBuffer buffer = myBuffers.get(key);
+      if (buffer != null) {
+        result.append(buffer.toString());
+      }
+    }
+    return result.toString();
+  }
+
+  private String preprocessBuffer(final Sdk testJdk, final String buffer, final boolean sortClassPath) throws Exception {
+    Application application = ApplicationManager.getApplication();
+
+    if (application == null) return buffer;
+
+    final Exception[] ex = new Exception[]{null};
+
+    String actual = application.runReadAction(new Computable<String>() {
+      @Override
+      public String compute() {
+        try {
+          String internalJdkHome = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().getHomeDirectory().getPath();
+          //System.out.println("internalJdkHome = " + internalJdkHome);
+
+          String result = buffer;
+          //System.out.println("Original Output = " + result);
+          final boolean shouldIgnoreCase = !SystemInfo.isFileSystemCaseSensitive;
+
+          result = StringUtil.replace(result, "\r\n", "\n");
+          result = StringUtil.replace(result, "\r", "\n");
+          result = StringUtil.replace(result, testJdk.getHomePath(), "!TEST_JDK!", shouldIgnoreCase);
+          result = StringUtil.replace(result, myAppPath, "!APP_PATH!", shouldIgnoreCase);
+          result = StringUtil.replace(result, myAppPath.replace(File.separatorChar, '/'), "!APP_PATH!", shouldIgnoreCase);
+          result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath(), "!RT_JAR!", shouldIgnoreCase);
+          result = StringUtil.replace(result, JavaSdkUtil.getJunit4JarPath(), "!JUNIT4_JAR!", shouldIgnoreCase);
+          result = StringUtil.replace(result, InetAddress.getLocalHost().getCanonicalHostName(), "!HOST_NAME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, InetAddress.getLocalHost().getHostName(), "!HOST_NAME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, "127.0.0.1", "!HOST_NAME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath().replace('/', File.separatorChar), "!RT_JAR!", shouldIgnoreCase);
+          result = StringUtil.replace(result, internalJdkHome.replace('/', File.separatorChar), "!JDK_HOME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, internalJdkHome, "!JDK_HOME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, PathManager.getHomePath(), "!IDEA_HOME!", shouldIgnoreCase);
+          result = StringUtil.replace(result, "Process finished with exit code 255", "Process finished with exit code -1");
+
+          result = replaceAdditionalInOutput(result);
+
+//          result = result.replaceAll(" +\n", "\n");
+          result = result.replaceAll("!HOST_NAME!:\\d*", "!HOST_NAME!:!HOST_PORT!");
+          result = result.replaceAll("at \\'.*?\\'", "at '!HOST_NAME!:PORT_NAME!'");
+          result = result.replaceAll("address: \\'.*?\\'", "address: '!HOST_NAME!:PORT_NAME!'");
+          result = result.replaceAll("file.*AppletPage.*\\.html", "file:/!APPLET_HTML!");
+          result = result.replaceAll("\"(!JDK_HOME!.*?)\"", "$1");
+          result = result.replaceAll("\"(!APP_PATH!.*?)\"", "$1");
+
+          result = result.replaceAll("-Didea.launcher.port=\\d*", "-Didea.launcher.port=!IDEA_LAUNCHER_PORT!");
+          result = result.replaceAll("-Dfile.encoding=[\\w\\d-]*", "-Dfile.encoding=!FILE_ENCODING!");
+          result = result.replaceAll("\\((.*)\\:\\d+\\)", "($1:!LINE_NUMBER!)");
+
+          int commandLineStart = result.indexOf("!JDK_HOME!");
+          while (commandLineStart != -1) {
+            final StringBuilder builder = new StringBuilder(result);
+            int i = commandLineStart + 1;
+            while (i < builder.length()) {
+              char c = builder.charAt(i);
+              if (c == '\n') break;
+              else if (c == File.separatorChar) builder.setCharAt(i, '\\');
+              i++;
+            }
+            result = builder.toString();
+            commandLineStart = result.indexOf("!JDK_HOME!", commandLineStart + 1);
+          }
+
+          result = stripQuotesAroundClasspath(result);
+
+          final Matcher matcher = Pattern.compile("-classpath\\s+(\\S+)\\s+").matcher(result);
+          while (matcher.find()) {
+            final String classPath = matcher.group(1);
+            final String[] classPathElements = classPath.split(File.pathSeparator);
+            if (sortClassPath) {
+              Arrays.sort(classPathElements);
+            }
+            final String sortedPath = StringUtil.join(classPathElements, ";");
+            result = StringUtil.replace(result, classPath, sortedPath);
+          }
+
+          result = JDI_BUG_OUTPUT_PATTERN_1.matcher(result).replaceAll("");
+          result = JDI_BUG_OUTPUT_PATTERN_2.matcher(result).replaceAll("");
+
+          return result;
+        }
+        catch (Exception exception) {
+          ex[0] = exception;
+          return null;
+        }
+      }
+    });
+
+
+    if (ex[0] != null) throw ex[0];
+
+    return actual;
+  }
+
+  protected String replaceAdditionalInOutput(String str) {
+    return str;
+  }
+
+  //do not depend on spaces in jdk path
+  private static String stripQuotesAroundClasspath(String result) {
+    final String clsp = "-classpath ";
+    int clspIdx = 0;
+    while (true) {
+      clspIdx = result.indexOf(clsp, clspIdx);
+      if (clspIdx <= -1) {
+        break;
+      }
+
+      final int spaceIdx = result.indexOf(" ", clspIdx + clsp.length());
+      if (spaceIdx > -1) {
+        result = result.substring(0, clspIdx) +
+                 clsp +
+                 StringUtil.stripQuotesAroundValue(result.substring(clspIdx + clsp.length(), spaceIdx)) +
+                 result.substring(spaceIdx);
+        clspIdx += clsp.length();
+      } else {
+        break;
+      }
+    }
+    return result;
+  }
+}
diff --git a/java/testFramework/src/com/intellij/debugger/impl/SynchronizationBasedSemaphore.java b/java/testFramework/src/com/intellij/debugger/impl/SynchronizationBasedSemaphore.java
new file mode 100644
index 0000000..8c57cd3
--- /dev/null
+++ b/java/testFramework/src/com/intellij/debugger/impl/SynchronizationBasedSemaphore.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package com.intellij.debugger.impl;
+
+import com.intellij.openapi.diagnostic.Logger;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Nov 17, 2009
+ */
+public class SynchronizationBasedSemaphore {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.Semaphore");
+  private int mySemaphore = 0;
+
+  public synchronized void down() {
+    mySemaphore++;
+    if (mySemaphore == 0) {
+      notifyAll();
+    }
+  }
+
+  public synchronized void up() {
+    mySemaphore--;
+    if (mySemaphore == 0) {
+      notifyAll();
+    }
+  }
+
+  public synchronized void waitFor() {
+    try {
+      while (mySemaphore > 0) {
+        wait();
+      }
+    }
+    catch (InterruptedException e) {
+      LOG.debug(e);
+      throw new RuntimeException(e);
+    }
+  }
+
+  public synchronized boolean waitFor(final long timeout) {
+    try {
+      if (mySemaphore == 0) return true;
+      final long startTime = System.currentTimeMillis();
+      long waitTime = timeout;
+      while (mySemaphore > 0) {
+        wait(waitTime);
+        final long elapsed = System.currentTimeMillis() - startTime;
+        if (elapsed < timeout) {
+          waitTime = timeout - elapsed;
+        }
+        else {
+          break;
+        }
+      }
+      return mySemaphore == 0;
+    }
+    catch (InterruptedException e) {
+      LOG.debug(e);
+      throw new RuntimeException(e);
+    }
+  }
+
+}
diff --git a/java/testFramework/src/com/intellij/execution/ExecutionTestCase.java b/java/testFramework/src/com/intellij/execution/ExecutionTestCase.java
new file mode 100644
index 0000000..075c681
--- /dev/null
+++ b/java/testFramework/src/com/intellij/execution/ExecutionTestCase.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+package com.intellij.execution;
+
+import com.intellij.debugger.impl.OutputChecker;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PsiTestUtil;
+import com.intellij.util.Alarm;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.ui.UIUtil;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ExecutionTestCase extends IdeaTestCase {
+  private OutputChecker myChecker;
+
+  private int myTimeout;
+
+  private static AssertionFailedError ourAssertion;
+  private static final String CLASSES = "classes";
+  private static final String SRC = "src";
+
+  public ExecutionTestCase() {
+    setTimeout(300000); //30 seconds
+  }
+
+  public void setTimeout(int timeout) {
+    myTimeout = timeout;
+  }
+
+  protected abstract OutputChecker initOutputChecker();
+
+  protected abstract String getTestAppPath();
+
+  @Override
+  protected void setUp() throws Exception {
+    ourAssertion = null;
+    ensureCompiledAppExists();
+    myChecker = initOutputChecker();
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          ExecutionTestCase.super.setUp();
+        }
+        catch (Throwable e) {
+          e.printStackTrace();
+          assertTrue(false);
+        }
+      }
+    });
+  }
+
+  @Override
+  protected void setUpModule() {
+    super.setUpModule();
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      @Override
+      public void run() {
+        final String modulePath = getTestAppPath();
+        final String srcPath = modulePath + File.separator + "src";
+        VirtualFile moduleDir = LocalFileSystem.getInstance().findFileByPath(modulePath.replace(File.separatorChar, '/'));
+        VirtualFile srcDir = LocalFileSystem.getInstance().findFileByPath(srcPath.replace(File.separatorChar, '/'));
+
+        final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule);
+        PsiTestUtil.removeAllRoots(myModule, rootManager.getSdk());
+        PsiTestUtil.addContentRoot(myModule, moduleDir);
+        PsiTestUtil.addSourceRoot(myModule, srcDir);
+        PsiTestUtil.setCompilerOutputPath(myModule, VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(getAppClassesPath())), false);
+      }
+    });
+  }
+
+  public void println(@NonNls String s, Key outputType) {
+    myChecker.println(s, outputType);
+  }
+
+  public void print(String s, Key outputType) {
+    myChecker.print(s, outputType);
+  }
+
+  @Override
+  protected void runBareRunnable(Runnable runnable) throws Throwable {
+    runnable.run();
+  }
+
+  @Override
+  protected void runTest() throws Throwable {
+    myChecker.init(getTestName(true));
+    super.runTest();
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          ExecutionTestCase.super.tearDown();
+        }
+        catch (Exception e) {
+          LOG.error(e);
+        }
+      }
+    });
+    if (ourAssertion != null) {
+      throw ourAssertion;
+    }
+    //myChecker.checkValid(getTestProjectJdk());
+    //probably some thread is destroyed right now because of log exception
+    //wait a little bit
+    synchronized (this) {
+      wait(300);
+    }
+  }
+
+  protected JavaParameters createJavaParameters(String mainClass) {
+    JavaParameters parameters = new JavaParameters();
+    parameters.getClassPath().add(getAppClassesPath());
+    parameters.setMainClass(mainClass);
+    parameters.setJdk(JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk());
+    return parameters;
+  }
+
+  protected OutputChecker getChecker() {
+    return myChecker;
+  }
+
+  protected String getAppDataPath() {
+    return getTestAppPath() + File.separator + "data";
+  }
+
+  protected String getAppOptionsPath() {
+    return getTestAppPath() + File.separator + "config" + File.separator + "options";
+  }
+
+  protected String getAppClassesPath() {
+    return getTestAppPath() + File.separator + "classes";
+  }
+
+  public void waitProcess(final ProcessHandler processHandler) {
+    Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, getTestRootDisposable());
+
+    final boolean[] isRunning = {true};
+    alarm.addRequest(new Runnable() {
+      @Override
+      public void run() {
+        boolean b;
+        synchronized (isRunning) {
+          b = isRunning[0];
+        }
+        if (b) {
+          processHandler.destroyProcess();
+          LOG.error("process was running over " + myTimeout / 1000 + " seconds. Interrupted. ");
+        }
+      }
+    }, myTimeout);
+    processHandler.waitFor();
+    synchronized (isRunning) {
+      isRunning[0] = false;
+    }
+    alarm.dispose();
+  }
+
+  public void waitFor(Runnable r) {
+    Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, getTestRootDisposable());
+    final Thread thread = Thread.currentThread();
+
+    final boolean[] isRunning = {true};
+    alarm.addRequest(new Runnable() {
+      @Override
+      public void run() {
+        boolean b;
+        synchronized (isRunning) {
+          b = isRunning[0];
+        }
+        if (b) {
+          thread.interrupt();
+          LOG.error("test was running over " + myTimeout / 1000 + " seconds. Interrupted. ");
+        }
+      }
+    }, myTimeout);
+    r.run();
+    synchronized (isRunning) {
+      isRunning[0] = false;
+    }
+    Thread.interrupted();
+  }
+
+//  public static void fail(String message) {
+//    ourAssertion = new AssertionFailedError(message);
+//  }
+//
+//  static public void assertTrue(String message, boolean condition) {
+//    if (!condition)
+//      fail(message);
+//  }
+//
+//  static public void assertTrue(boolean condition) {
+//    assertTrue(null, condition);
+//  }
+
+  private static final int CURRENT_VERSION = 6;
+  private static final String VERSION_FILE_NAME = "version-" + CURRENT_VERSION;
+  protected void ensureCompiledAppExists() throws Exception {
+    final String appPath = getTestAppPath();
+    final File classesDir = new File(appPath, CLASSES);
+    final File versionFile = new File(classesDir, VERSION_FILE_NAME);
+    if (!classesDir.exists() || !versionFile.exists() || !hasCompiledClasses(classesDir)) {
+      FileUtil.delete(classesDir);
+      classesDir.mkdirs();
+      if (compileTinyApp(appPath) != 0) {
+        throw new Exception("Failed to compile debugger test application.\nIt must be compiled in order to run debugger tests.\n" + appPath);
+      }
+      versionFile.createNewFile();
+    }
+  }
+
+  private int compileTinyApp(String appPath) {
+    final List<String> args = new ArrayList<String>();
+    args.add("-g");
+    args.add("-d");
+    args.add(new File(appPath, CLASSES).getPath());
+    
+    final Class<TestCase> testCaseClass = TestCase.class;
+    final String junitLibRoot = PathManager.getResourceRoot(testCaseClass, "/" + testCaseClass.getName().replace('.', '/') + ".class");
+    if (junitLibRoot != null) {
+      args.add("-cp");
+      args.add(junitLibRoot);
+    }
+    
+    final File[] files = new File(appPath, SRC).listFiles(new FilenameFilter() {
+      @Override
+      public boolean accept(File dir, String name) {
+        return name.endsWith(".java");
+      }
+    });
+    if (files == null) return 0; // Nothing to compile
+
+    for (File file : files) {
+      args.add(file.getPath());
+    }
+    return com.sun.tools.javac.Main.compile(ArrayUtil.toStringArray(args));
+  }
+
+  private boolean hasCompiledClasses(final File classesDir) {
+    for (File file : classesDir.listFiles()) {
+      if (file.isFile() && file.getName().endsWith(".class")) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java b/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java
index a93ea6b..bfeac04 100644
--- a/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java
+++ b/java/testFramework/src/com/intellij/testFramework/InspectionTestCase.java
@@ -76,11 +76,11 @@
     doTest(folderName, new GlobalInspectionToolWrapper(tool), "java 1.4", checkRange, runDeadCodeFirst);
   }
 
-  public void doTest(@NonNls String folderName, InspectionTool tool) {
+  public void doTest(@NonNls String folderName, InspectionToolWrapper tool) {
     doTest(folderName, tool, "java 1.4");
   }
 
-  public void doTest(@NonNls String folderName, InspectionTool tool, final boolean checkRange) {
+  public void doTest(@NonNls String folderName, InspectionToolWrapper tool, final boolean checkRange) {
     doTest(folderName, tool, "java 1.4", checkRange);
   }
 
@@ -88,35 +88,35 @@
     doTest(folderName, new LocalInspectionToolWrapper(tool), jdkName);
   }
 
-  public void doTest(@NonNls String folderName, InspectionTool tool, @NonNls final String jdkName) {
+  public void doTest(@NonNls String folderName, InspectionToolWrapper tool, @NonNls final String jdkName) {
     doTest(folderName, tool, jdkName, false);
   }
 
-  public void doTest(@NonNls String folderName, InspectionTool tool, @NonNls final String jdkName, boolean checkRange) {
+  public void doTest(@NonNls String folderName, InspectionToolWrapper tool, @NonNls final String jdkName, boolean checkRange) {
     doTest(folderName, tool, jdkName, checkRange, false);
   }
 
   public void doTest(@NonNls String folderName,
-                     InspectionTool tool,
+                     InspectionToolWrapper toolWrapper,
                      @NonNls final String jdkName,
                      boolean checkRange,
                      boolean runDeadCodeFirst,
-                     InspectionTool... additional) {
+                     InspectionToolWrapper... additional) {
     final String testDir = getTestDataPath() + "/" + folderName;
-    runTool(testDir, jdkName, runDeadCodeFirst, tool, additional);
+    runTool(testDir, jdkName, runDeadCodeFirst, toolWrapper, additional);
 
-    InspectionTestUtil.compareToolResults(tool, checkRange, testDir);
+    InspectionTestUtil.compareToolResults(toolWrapper, checkRange, testDir);
   }
 
-  protected void runTool(@NonNls final String testDir, @NonNls final String jdkName, final InspectionTool tool) {
+  protected void runTool(@NonNls final String testDir, @NonNls final String jdkName, final InspectionToolWrapper tool) {
     runTool(testDir, jdkName, false, tool);
   }
 
   protected void runTool(final String testDir,
                          final String jdkName,
                          boolean runDeadCodeFirst,
-                         final InspectionTool tool,
-                         InspectionTool... additional) {
+                         @NotNull InspectionToolWrapper toolWrapper,
+                         @NotNull InspectionToolWrapper... additional) {
     final VirtualFile[] sourceDir = new VirtualFile[1];
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       @Override
@@ -132,14 +132,15 @@
     AnalysisScope scope = createAnalysisScope(sourceDir[0].getParent());
 
     InspectionManagerEx inspectionManager = (InspectionManagerEx)InspectionManager.getInstance(getProject());
-    InspectionTool[] tools = runDeadCodeFirst ? new InspectionTool[]{new UnusedDeclarationInspection(), tool} : new InspectionTool[]{tool};
-    tools = ArrayUtil.mergeArrays(tools, additional);
+    InspectionToolWrapper[] toolWrappers = runDeadCodeFirst ? new InspectionToolWrapper []{new GlobalInspectionToolWrapper(new UnusedDeclarationInspection()), toolWrapper} : new InspectionToolWrapper []{toolWrapper};
+    toolWrappers = ArrayUtil.mergeArrays(toolWrappers, additional);
     final GlobalInspectionContextImpl globalContext =
-      CodeInsightTestFixtureImpl.createGlobalContextForTool(scope, getProject(), inspectionManager, tools);
+      CodeInsightTestFixtureImpl.createGlobalContextForTool(scope, getProject(), inspectionManager, toolWrappers);
 
-    InspectionTestUtil.runTool(tool, scope, globalContext, inspectionManager);
+    InspectionTestUtil.runTool(toolWrapper, scope, globalContext, inspectionManager);
   }
 
+  @NotNull
   protected AnalysisScope createAnalysisScope(VirtualFile sourceDir) {
     PsiManager psiManager = PsiManager.getInstance(myProject);
     return new AnalysisScope(psiManager.findDirectory(sourceDir));
diff --git a/java/testFramework/testFramework-java.iml b/java/testFramework/testFramework-java.iml
index 9d48d3d..ed67da5 100644
--- a/java/testFramework/testFramework-java.iml
+++ b/java/testFramework/testFramework-java.iml
@@ -22,6 +22,8 @@
     <orderEntry type="module" module-name="relaxng" exported="" scope="TEST" />
     <orderEntry type="module" module-name="idea-ui" exported="" />
     <orderEntry type="module" module-name="external-system-impl" exported="" scope="TEST" />
+    <orderEntry type="module" module-name="debugger-impl" exported="" />
+    <orderEntry type="module" module-name="execution-openapi" exported="" />
   </component>
   <component name="copyright">
     <Base>