Package com.google.jstestdriver.idea

Source Code of com.google.jstestdriver.idea.TestRunner$NullOutputStream

/*
* Copyright 2009 Google Inc.
*
* 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.google.jstestdriver.idea;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.multibindings.Multibinder;
import com.google.jstestdriver.*;
import com.google.jstestdriver.config.Configuration;
import com.google.jstestdriver.config.ConfigurationException;
import com.google.jstestdriver.config.ParsedConfiguration;
import com.google.jstestdriver.config.ResolvedConfiguration;
import com.google.jstestdriver.embedded.JsTestDriverBuilder;
import com.google.jstestdriver.hooks.PluginInitializer;
import com.google.jstestdriver.hooks.ResourcePreProcessor;
import com.google.jstestdriver.hooks.TestListener;
import com.google.jstestdriver.idea.coverage.CoverageReport;
import com.google.jstestdriver.idea.coverage.CoverageSerializationUtils;
import com.google.jstestdriver.idea.coverage.CoverageSession;
import com.google.jstestdriver.idea.execution.tree.TreeManager;
import com.google.jstestdriver.idea.util.EscapeUtils;
import com.google.jstestdriver.idea.util.JstdConfigParsingUtils;
import com.google.jstestdriver.idea.util.JstdUtils;
import com.google.jstestdriver.output.TestResultHolder;
import com.google.jstestdriver.runner.RunnerMode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joda.time.DateTime;

import java.io.*;
import java.util.*;

/**
* Main class of JsTestDriver test runner, that runs tests in a separate process and streams messages
* via stdout/stderr to IDEA process, which will update the UI with our results.
*
* @author alexeagle@google.com (Alex Eagle)
*/
public class TestRunner {

  public static final String DEBUG_SESSION_STARTED = "debug session started";

  public enum ParameterKey {
    SERVER_URL,
    CONFIG_FILES,
    ALL_CONFIGS_IN_DIRECTORY,
    TESTS,
    COVERAGE_OUTPUT_FILE,
    COVERAGE_EXCLUDED_PATHS,
    DEBUG
  }

  private final JstdSettings mySettings;
  private final TreeManager myTreeManager;
  private final CoverageSession myCoverageSession;

  public TestRunner(@NotNull JstdSettings settings, @NotNull TreeManager treeManager) {
    mySettings = settings;
    myTreeManager = treeManager;
    File ideCoverageFile = mySettings.getIdeCoverageFile();
    if (ideCoverageFile != null) {
      myCoverageSession = new CoverageSession(ideCoverageFile);
    } else {
      myCoverageSession = null;
    }
  }

  public void executeAll() {
    for (File config : mySettings.getConfigFiles()) {
      executeTests(config);
    }
    if (myCoverageSession != null) {
      myCoverageSession.finish();
    }
  }

  private void executeTests(@NotNull File config) {
    Exception exception = null;
    PrintStream nullSystemOut = new PrintStream(new NullOutputStream());
    try {
      System.setOut(nullSystemOut);
      myTreeManager.onJstdConfigRunningStarted(config);
      String runScope = mySettings.getTestFileScope().toJstdStr();
      runTests(config, new String[]{"--dryRunFor", runScope}, true);
      myTreeManager.reportTotalTestCount();
      runTests(config, new String[]{"--tests", runScope}, false);
    }
    catch (ConfigurationException ce) {
      exception = ce;
    }
    catch (RuntimeException re) {
      String haltErrorMessage = getErrorMessageIfNoServerRunning(re, mySettings);
      if (haltErrorMessage == null) {
        haltErrorMessage = getErrorMessageIfNoCapturedBrowsersFound(re, mySettings);
      }
      if (haltErrorMessage != null) {
        myTreeManager.onJstdConfigRunningFinished(haltErrorMessage, mySettings.getTestFileScope());
        System.exit(1);
      }
      exception = new Exception("Can't run tests.", re);
    }
    catch (Exception e) {
      exception = new Exception("Can't run tests.", e);
    }
    finally {
      String errorMessage = null;
      if (exception != null) {
        errorMessage = TreeManager.formatMessage(exception.getMessage(), exception.getCause());
      }
      myTreeManager.onJstdConfigRunningFinished(errorMessage, mySettings.getTestFileScope());
      nullSystemOut.close();
      System.setOut(myTreeManager.getSystemOutStream());
    }
  }

  @SuppressWarnings("deprecation")
  private void runTests(@NotNull final File configFile, @NotNull String[] extraArgs, final boolean dryRun) throws ConfigurationException {
    JsTestDriverBuilder builder = new JsTestDriverBuilder();

    final ParsedConfiguration parsedConfiguration;
    try {
      parsedConfiguration = JstdConfigParsingUtils.parseConfiguration(configFile);
    } catch (Exception e) {
      throw new ConfigurationException("Configuration file parsing failed.\n" +
                                       "See http://code.google.com/p/js-test-driver/wiki/ConfigurationFile for clarification.\n\n" +
                                       "Details:", e);
    }
    final File singleBasePath = JstdConfigParsingUtils.getSingleBasePath(parsedConfiguration.getBasePaths(), configFile);
    myTreeManager.setCurrentBasePath(singleBasePath.getAbsolutePath());
    JstdConfigParsingUtils.wipeCoveragePlugin(parsedConfiguration);
    builder.setDefaultConfiguration(parsedConfiguration);
    builder.withPluginInitializer(new PluginInitializer() {
      @Override
      public Module initializeModule(Flags flags, Configuration config) {
        return new AbstractModule() {
          @Override
          public void configure() {
            Multibinder<TestListener> testListeners = Multibinder.newSetBinder(binder(), TestListener.class);
            testListeners.addBinding().to(TestResultHolder.class);
            testListeners.addBinding().toInstance(new IdeaTestListener(
              myTreeManager,
              configFile,
              singleBasePath,
              dryRun,
              mySettings.getTestFileScope()
            ));
          }
        };
      }
    });

    builder.setRunnerMode(RunnerMode.QUIET);
    builder.setServer(mySettings.getServerUrl());

    List<String> flagArgs = Lists.newArrayList("--captureConsole", "--server", mySettings.getServerUrl());
    ResolvedConfiguration resolvedConfiguration = JstdConfigParsingUtils.resolveConfiguration(parsedConfiguration);
    if (dryRun && JstdUtils.isJasmineTests(resolvedConfiguration)) {
      // https://github.com/ibolmo/jasmine-jstd-adapter/pull/21
      flagArgs.add("--reset");
    }
    flagArgs.addAll(Arrays.asList(extraArgs));
    List<String> coverageExcludedFiles = null;
    File emptyOutputDir = null;
    boolean runCoverage = false;
    if (myCoverageSession != null && !dryRun) {
      emptyOutputDir = createTempDir();
      if (emptyOutputDir != null) {
        flagArgs.add("--testOutput");
        flagArgs.add(emptyOutputDir.getAbsolutePath());
        List<String> testPaths = getTestFilePaths(resolvedConfiguration);
        coverageExcludedFiles = Lists.newArrayList(testPaths);
        coverageExcludedFiles.addAll(mySettings.getFilesExcludedFromCoverageRec());
        PluginInitializer coverageInitializer = getCoverageInitializer(coverageExcludedFiles);
        if (coverageInitializer != null) {
          builder.withPluginInitializer(coverageInitializer);
          builder.withPluginInitializer(new DependenciesTouchFix());
          runCoverage = true;
        }
      }
    }

    builder.setFlags(toStringArray(flagArgs));
    builder.setFlagsParser(new IntelliJFlagParser(mySettings, dryRun));
    JsTestDriver jstd = builder.build();
    jstd.runConfiguration();
    if (runCoverage) {
      File[] coverageReportFiles = emptyOutputDir.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
          return name.endsWith("-coverage.dat");
        }
      });
      if (coverageReportFiles != null && coverageReportFiles.length == 1) {
        try {
          CoverageReport coverageReport = CoverageSerializationUtils.readLCOV(coverageReportFiles[0]);
          for (String excludedPath : coverageExcludedFiles) {
            coverageReport.clearReportByFilePath(excludedPath);
          }
          myCoverageSession.mergeReport(coverageReport);
        }
        catch (Exception e) {
          myTreeManager.printThrowable(e);
        }
      }
    }
  }

  @NotNull
  private static List<String> getTestFilePaths(@NotNull ResolvedConfiguration resolvedConfiguration) {
    List<FileInfo> testFileInfos = resolvedConfiguration.getTests();
    List<String> paths = Lists.newArrayListWithExpectedSize(testFileInfos.size());
    for (FileInfo fileInfo : testFileInfos) {
      paths.add(fileInfo.getFilePath());
    }
    return paths;
  }

  @Nullable
  private static PluginInitializer getCoverageInitializer(List<String> filesExcludedFromCoverage) {
    File[] coverageJarFiles = new File(".").listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.startsWith("coverage") && name.endsWith(".jar");
      }
    });
    if (coverageJarFiles != null && coverageJarFiles.length == 1) {
      Plugin plugin = new Plugin(
        "coverage",
        coverageJarFiles[0].getAbsolutePath(),
        "com.google.jstestdriver.coverage.CoverageModule",
        filesExcludedFromCoverage
      );
      PluginLoader pluginLoader = new PluginLoader();
      final List<Module> modules = pluginLoader.load(Collections.singletonList(plugin));
      if (modules.size() == 1) {
        return new PluginInitializer() {
          @Override
          public Module initializeModule(Flags flags, Configuration config) {
            return modules.get(0);
          }
        };
      }
    }
    return null;
  }

  @SuppressWarnings("SSBasedInspection")
  @Nullable
  private static File createTempDir() {
    try {
      File file = File.createTempFile("jstestdriver-coverage-", "-tmp");
      if (!file.delete()) {
        return null;
      }
      if (!file.mkdir()) {
        return null;
      }
      file.deleteOnExit();
      return file;
    } catch (IOException e) {
      return null;
    }
  }

  public static void main(String[] args) throws Exception {
    Map<ParameterKey, String> paramMap = parseParams(args);
    JstdSettings settings = JstdSettings.build(paramMap);
    TreeManager treeManager = new TreeManager(settings.getRunAllConfigsInDirectory());
    if (settings.isDebug()) {
      @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
      String line;
      while ((line = reader.readLine()) != null) {
        if (DEBUG_SESSION_STARTED.equals(line)) {
          break;
        }
      }
    }
    try {
      new TestRunner(settings, treeManager).executeAll();
    } catch (Exception ex) {
      treeManager.printThrowable("Unexpected crash!", ex);
    } finally {
      treeManager.onTestingFinished();
    }
  }

  @Nullable
  private static String getErrorMessageIfNoServerRunning(@NotNull RuntimeException ex,
                                                         @NotNull JstdSettings settings) {
    // java.lang.RuntimeException: Connection error on: sun.net.www.protocol.http.HttpURLConnection:http://localhost:9876/jstd/gateway
    //    at com.google.jstestdriver.HttpServer.postJson(HttpServer.java:161)
    String exMessage = ex.getMessage();
    if (exMessage != null && exMessage.startsWith("Connection error on: ") && ex.getCause() instanceof IOException) {
      return "Could not connect to JsTestDriver server running at " + settings.getServerUrl() + "\n" +
             "Check that the server is running.";
    }
    return null;
  }

  @Nullable
  private static String getErrorMessageIfNoCapturedBrowsersFound(@NotNull RuntimeException ex,
                                                                 @NotNull JstdSettings settings) {
    // at com.google.jstestdriver.browser.BrowserActionExecutorAction.run(BrowserActionExecutorAction.java:94)
    String exMessage = ex.getMessage();
    if (exMessage != null &&
        exMessage.startsWith("No browsers available, yet actions") &&
        exMessage.endsWith("If running against a persistent server please capture browsers. Otherwise, ensure that browsers are defined.")) {
      return "No captured browsers found.\n" +
             "To capture a browser open " + settings.getServerUrl() + "/capture";
    }
    return null;
  }

  private static Map<ParameterKey, String> parseParams(String[] args) {
    Map<ParameterKey, String> params = Maps.newHashMap();
    for (String arg : args) {
      List<String> elements = EscapeUtils.split(arg, '=');
      if (arg.startsWith("--") && elements.size() == 2) {
        String key = elements.get(0).substring(2);
        String value = elements.get(1);
        ParameterKey parameterKey = ParameterKey.valueOf(key.toUpperCase(Locale.ENGLISH));
        params.put(parameterKey, value);
      }
    }
    return params;
  }

  @SuppressWarnings("SSBasedInspection")
  @NotNull
  private static String[] toStringArray(@NotNull List<String> list) {
    return list.toArray(new String[list.size()]);
  }

  private static class DependenciesTouchFix implements PluginInitializer {
    @Override
    public Module initializeModule(Flags flags, Configuration config) {
      return new AbstractModule() {
        @Override
        protected void configure() {
          Multibinder.newSetBinder(binder(), ResourcePreProcessor.class)
            .addBinding().toInstance(new ResourcePreProcessor() {
            @Override
            public List<FileInfo> processDependencies(List<FileInfo> files) {
              List<FileInfo> out = Lists.newArrayList();
              for (FileInfo file : files) {
                FileInfo touchedFileInfo = file.fromResolvedPath(
                  file.getFilePath(),
                  file.getDisplayPath(),
                  new DateTime().toInstant().getMillis()
                );
                out.add(touchedFileInfo);
              }
              return out;
            }

            @Override
            public List<FileInfo> processTests(List<FileInfo> files) {
              return files;
            }

            @Override
            public List<FileInfo> processPlugins(List<FileInfo> files) {
              return files;
            }
          });
        }
      };
    }
  }

  private static final class NullOutputStream extends OutputStream {

    /** Discards the specified byte. */
    @Override public void write(int b) {
    }

    /** Discards the specified byte array. */
    @Override public void write(@NotNull byte[] b, int off, int len) {
    }
  }

}
TOP

Related Classes of com.google.jstestdriver.idea.TestRunner$NullOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.