All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.rt.coverage.instrumentation.Instrumentator Maven / Gradle / Ivy

/*
 * Copyright 2000-2018 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.rt.coverage.instrumentation;

import com.intellij.rt.coverage.data.ProjectData;
import com.intellij.rt.coverage.instrumentation.testTracking.NoTestTrackingMode;
import com.intellij.rt.coverage.instrumentation.testTracking.TestTrackingArrayMode;
import com.intellij.rt.coverage.instrumentation.testTracking.TestTrackingClassDataMode;
import com.intellij.rt.coverage.instrumentation.testTracking.TestTrackingMode;
import com.intellij.rt.coverage.util.ErrorReporter;
import com.intellij.rt.coverage.util.classFinder.ClassFinder;

import java.io.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class Instrumentator {
  public static void premain(String argsString, Instrumentation instrumentation) throws Exception {
    new Instrumentator().performPremain(argsString, instrumentation);
  }

  public void performPremain(String argsString, Instrumentation instrumentation) throws Exception {
    checkLogLevel();

    String[] args;
    if (argsString != null) {
      File argsFile = new File(argsString);
      if (argsFile.isFile()) {
        try {
          args = readArgsFromFile(argsString);
        } catch (IOException e) {
          ErrorReporter.reportError("Arguments were not passed correctly", e);
          return;
        }
      } else {
        args = tokenize(argsString);
      }
    } else {
      ErrorReporter.reportError("Argument string should be passed");
      return;
    }

    if (0 < args.length && args.length < 5) {
      ErrorReporter.logError("At least 5 arguments expected but " + args.length + " found.\n"
          + '\'' + argsString + "'\n"
          + "Expected arguments are:\n"
          + "1) data file to save coverage result\n"
          + "2) a flag to enable tracking per test coverage\n"
          + "3) a flag to calculate coverage for unloaded classes\n"
          + "4) a flag to use data file as initial coverage\n"
          + "5) a flag to run coverage in sampling mode or in tracing mode otherwise\n");
      System.exit(1);
    }

    final File dataFile = args.length > 0 ? new File(args[0]) : null;
    final boolean traceLines = args.length > 0 && Boolean.parseBoolean(args[1]);
    final boolean calcUnloaded = args.length > 0 && Boolean.parseBoolean(args[2]);
    final boolean mergeData = args.length > 0 && Boolean.parseBoolean(args[3]) && dataFile.isFile();
    final boolean sampling = args.length == 0 || Boolean.parseBoolean(args[4]);
    if (dataFile != null) {
      ErrorReporter.setBasePath(dataFile.getParent());
    }
    int i = 5;

    final File sourceMapFile;
    if (args.length > 5 && Boolean.parseBoolean(args[5])) {
      sourceMapFile = new File(args[6]);
      i = 7;
    } else {
      sourceMapFile = null;
    }

    final List includePatterns = new ArrayList();
    ErrorReporter.logInfo("---- IntelliJ IDEA coverage runner ---- ");
    ErrorReporter.logInfo(sampling ? "sampling ..." : ("tracing " + (traceLines ? "and tracking per test coverage ..." : "...")));
    final String excludes = "-exclude";
    ErrorReporter.logInfo("include patterns:");
    for (; i < args.length; i++) {
      if (excludes.equals(args[i])) break;
      try {
        includePatterns.add(Pattern.compile(args[i]));
        ErrorReporter.logInfo(args[i]);
      } catch (PatternSyntaxException ex) {
        ErrorReporter.reportError("Problem occurred with include pattern " + args[i] +
            ". This may cause no tests run and no coverage collected", ex);
        System.exit(1);
      }
    }
    ErrorReporter.logInfo("exclude patterns:");
    i++;
    final List excludePatterns = new ArrayList();
    for (; i < args.length; i++) {
      try {
        final Pattern pattern = Pattern.compile(args[i]);
        excludePatterns.add(pattern);
        ErrorReporter.logInfo(pattern.pattern());
      } catch (PatternSyntaxException ex) {
        ErrorReporter.reportError("Problem occurred with exclude pattern " + args[i] +
            ". This may cause no tests run and no coverage collected", ex);
        System.exit(1);
      }
    }

    final TestTrackingMode testTrackingMode = createTestTrackingMode(traceLines);
    final ProjectData data = ProjectData.createProjectData(dataFile, null, traceLines, sampling, includePatterns, excludePatterns, testTrackingMode.createTestTrackingCallback());
    final ClassFinder cf = new ClassFinder(includePatterns, excludePatterns);
    if (dataFile != null) {
      final SaveHook hook = new SaveHook(dataFile, calcUnloaded, cf, mergeData);
      hook.setSourceMapFile(sourceMapFile);
      Runtime.getRuntime().addShutdownHook(new Thread(hook));
    }

    final boolean shouldCalculateSource = sourceMapFile != null;
    final CoverageClassfileTransformer transformer = new CoverageClassfileTransformer(data, shouldCalculateSource, excludePatterns, includePatterns, cf, testTrackingMode);
    addTransformer(instrumentation, transformer);
  }

  private void checkLogLevel() {
    final String logLevel = System.getProperty("idea.coverage.log.level");
    if ("error".equals(logLevel)) {
      ErrorReporter.setLogLevel(ErrorReporter.ERROR);
    } else {
      ErrorReporter.setLogLevel(ErrorReporter.INFO);
    }
  }

  /**
   * Add transformer with re-transformation enabled when possible.
   * Reflection is used for 1.5 compatibility.
   */
  private void addTransformer(Instrumentation instrumentation, CoverageClassfileTransformer transformer) {
    try {
      final Method method = Instrumentation.class.getMethod("addTransformer", ClassFileTransformer.class, boolean.class);
      method.invoke(instrumentation, transformer, true);
    } catch (NoSuchMethodException e) {
      instrumentation.addTransformer(transformer);
    } catch (Exception e) {
      ErrorReporter.reportError("Adding transformer failed.", e);
      System.exit(1);
    }
  }

  private TestTrackingMode createTestTrackingMode(boolean traceLines) {
    if (!traceLines) return new NoTestTrackingMode();
    if (System.getProperty("idea.new.tracing.coverage") != null &&
        "true".equals(System.getProperty("idea.new.test.tracking.coverage", "true"))) {
      return new TestTrackingArrayMode();
    }
    return new TestTrackingClassDataMode();
  }

  private String[] readArgsFromFile(String arg) throws IOException {
    final List result = new ArrayList();
    final File file = new File(arg);
    final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
    try {
      while (reader.ready()) {
        result.add(reader.readLine());
      }
    } finally {
      reader.close();
    }
    return result.toArray(new String[0]);
  }

  private static String[] tokenize(String argumentString) {
    List tokenizedArgs = new ArrayList();
    StringBuffer currentArg = new StringBuffer();
    for (int i = 0; i < argumentString.length(); i++) {
      char c = argumentString.charAt(i);
      switch (c) {
        default:
          currentArg.append(c);
          break;
        case ' ':
          String arg = currentArg.toString();
          if (arg.length() > 0) {
            tokenizedArgs.add(arg);
          }
          currentArg = new StringBuffer();
          break;
        case '\"':
          for (i++; i < argumentString.length(); i++) {
            char d = argumentString.charAt(i);
            if (d == '\"') {
              break;
            }
            currentArg.append(d);
          }
      }
    }

    String arg = currentArg.toString();
    if (arg.length() > 0) {
      tokenizedArgs.add(arg);
    }
    return tokenizedArgs.toArray(new String[0]);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy