/**
   * @param testRunName
   * @param project may be <code>null</code>
   */
  public TestRunSession(String testRunName, IScriptProject project) {
    // TODO: check assumptions about non-null fields

    fLaunch = null;
    fProject = null; // TODO

    Assert.isNotNull(testRunName);
    fTestRunName = testRunName;
    fTestingEngine = NullTestingEngine.getInstance();
    testRunnerUI = NullTestRunnerUI.getInstance();
    categoryEngines = null;

    fTestRoot = new TestRoot(this);
    fIdToTest = new HashMap<String, TestElement>();
    fCategoryMap = new HashMap<String, TestCategoryElement>();

    fTestRunnerClient = null;

    fSessionListeners = new ListenerList();
  }
  public TestRunSession(ILaunch launch, IScriptProject project, ITestRunnerClient runnerClient) {
    Assert.isNotNull(launch);
    Assert.isNotNull(runnerClient);

    fLaunch = launch;
    fProject = project;

    ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
    if (launchConfiguration != null) {
      fTestRunName = launchConfiguration.getName();
      fTestingEngine = DLTKTestingConstants.getTestingEngine(launchConfiguration);
      testRunnerUI = fTestingEngine.getTestRunnerUI(project, launchConfiguration);
      categoryEngines = TestCategoryEngineManager.getCategoryEngines(testRunnerUI);
    } else {
      fTestRunName = project.getElementName();
      fTestingEngine = NullTestingEngine.getInstance();
      testRunnerUI = NullTestRunnerUI.getInstance();
      categoryEngines = null;
    }

    fTestRoot = new TestRoot(this);
    fIdToTest = new HashMap<String, TestElement>();
    fCategoryMap = new HashMap<String, TestCategoryElement>();

    fTestRunnerClient = runnerClient;
    fTestRunnerClient.startListening(new TestSessionNotifier());

    final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
    launchManager.addLaunchListener(
        new ILaunchesListener2() {
          public void launchesTerminated(ILaunch[] launches) {
            if (Arrays.asList(launches).contains(fLaunch)) {
              if (fTestRunnerClient != null) {
                fTestRunnerClient.stopWaiting();
              }
              launchManager.removeLaunchListener(this);
              scheduleTestRunTerminated();
            }
          }

          public void launchesRemoved(ILaunch[] launches) {
            if (Arrays.asList(launches).contains(fLaunch)) {
              if (fTestRunnerClient != null) {
                fTestRunnerClient.stopWaiting();
              }
              launchManager.removeLaunchListener(this);
              scheduleTestRunTerminated();
            }
          }

          public void launchesChanged(ILaunch[] launches) {}

          public void launchesAdded(ILaunch[] launches) {}

          private void scheduleTestRunTerminated() {
            if (!fIsRunning) return;
            final Job job = new Job("TestRunSession - notify launch terminated") { // $NON-NLS-1$
                  @Override
                  protected IStatus run(IProgressMonitor monitor) {
                    testRunTerminated();
                    return org.eclipse.core.runtime.Status.OK_STATUS;
                  }
                };
            job.setSystem(true);
            // small delay, giving a chance for the client to notify in a
            // normal way.
            job.schedule(750);
          }
        });

    fSessionListeners = new ListenerList();
    addTestSessionListener(new TestRunListenerAdapter(this));
  }