diff options
140 files changed, 1720 insertions, 1123 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/LineMeasureSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/LineMeasureSensor.java index fb8f267eca2..7d4c2bc0273 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/LineMeasureSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/LineMeasureSensor.java @@ -26,7 +26,6 @@ import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; @@ -46,13 +45,10 @@ public class LineMeasureSensor implements Sensor { private static final String MEASURES_EXTENSION = ".linemeasures"; - private MetricFinder metricFinder; - private FileLinesContextFactory contextFactory; public LineMeasureSensor(FileLinesContextFactory contextFactory) { this.contextFactory = contextFactory; - this.metricFinder = metricFinder; } private void processFileMeasures(InputFile inputFile, SensorContext context) { diff --git a/sonar-core/src/main/java/org/sonar/core/metric/ScannerMetrics.java b/sonar-core/src/main/java/org/sonar/core/metric/ScannerMetrics.java index 6406336c580..3052cefcffb 100644 --- a/sonar-core/src/main/java/org/sonar/core/metric/ScannerMetrics.java +++ b/sonar-core/src/main/java/org/sonar/core/metric/ScannerMetrics.java @@ -24,6 +24,9 @@ import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Stream; + +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.ScannerSide; import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.measures.Metric; @@ -70,6 +73,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet; * <p/> * Scanners should not send other metrics, and the Compute Engine should not allow other metrics. */ +@Immutable @ComputeEngineSide @ScannerSide public class ScannerMetrics { diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java index f992b942982..4438e197abe 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java @@ -21,6 +21,9 @@ package org.sonar.duplications.internal.pmd; import java.util.ArrayList; import java.util.List; + +import javax.annotation.concurrent.Immutable; + import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; @@ -29,6 +32,7 @@ import org.sonar.duplications.block.ByteArray; * works with {@link TokensLine}, * sets {@link Block#getStartUnit() startUnit} and {@link Block#getEndUnit() endUnit} - indexes of first and last token for this block. */ +@Immutable public class PmdBlockChunker { private static final long PRIME_BASE = 31; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java index bea756e4bc2..de0cfa41cee 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java @@ -19,6 +19,8 @@ */ package org.sonar.api; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; @@ -141,6 +143,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide; @ServerSide @ComputeEngineSide @SonarLintSide +@Immutable public interface SonarRuntime { /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java index eac17aefa31..7d557241ee0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java @@ -33,17 +33,20 @@ import org.sonar.api.batch.ScannerSide; * <li>Change project metadata like description or source directories.</li> * </ul> * + * @deprecated since 6.5. It won't be possible to manipulate the project's structure. * @since 2.9 */ @ScannerSide @InstantiationStrategy(InstantiationStrategy.PER_BATCH) @ExtensionPoint +@Deprecated public abstract class ProjectBuilder { /** * Plugins can use the implementation {@link org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext} * for their unit tests. */ + @Deprecated public interface Context { ProjectReactor projectReactor(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java index 19cc04bdaaf..c3c75506131 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java @@ -36,6 +36,8 @@ import org.sonar.api.CoreProperties; * {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used * by other standard extensions. * + * Since 6.5, plugins should no longer manipulate the project's structure. + * * @since 2.9 */ public class ProjectDefinition { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java index 46ea9aba68a..8aba5c679a2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectReactor.java @@ -26,8 +26,11 @@ import java.util.List; /** * Mutable project definitions that can be modified by {@link ProjectBuilder} extensions. + * + * @deprecated since 6.5 plugins should no longer modify the project's structure * @since 2.9 */ +@Deprecated @ScannerSide public class ProjectReactor implements ProjectKey { @@ -41,7 +44,7 @@ public class ProjectReactor implements ProjectKey { } public List<ProjectDefinition> getProjects() { - return collectProjects(root, new ArrayList<ProjectDefinition>()); + return collectProjects(root, new ArrayList<>()); } /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java index 6d2024037ba..b3093aa7011 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/internal/ProjectBuilderContext.java @@ -27,8 +27,10 @@ import org.sonar.api.batch.bootstrap.ProjectReactor; * Context that is passed to {@link org.sonar.api.batch.bootstrap.ProjectBuilder} as parameter. * Important - plugins must use this class only for unit test needs. * + * @deprecated since 6.5 * @since 3.7 */ +@Deprecated public class ProjectBuilderContext implements ProjectBuilder.Context { private final ProjectReactor reactor; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java index a0b2e24404d..cbfc1368779 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/debt/DebtRemediationFunction.java @@ -22,12 +22,14 @@ package org.sonar.api.batch.debt; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.utils.Duration; +import javax.annotation.concurrent.Immutable; /** * @since 4.3 * @deprecated since 6.5 debt model will soon be unavailable on batch side */ @Deprecated +@Immutable public class DebtRemediationFunction { public enum Type { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java index 373ba890055..c1aaf9fa36f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java @@ -19,6 +19,9 @@ */ package org.sonar.api.batch.fs; + +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.sensor.SensorContext; /** @@ -26,5 +29,6 @@ import org.sonar.api.batch.sensor.SensorContext; * * @since 5.2 */ +@Immutable public interface InputModule extends InputComponent { } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java index d526e7f57cc..b3a0dbbe9ee 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultIndexedFile.java @@ -24,8 +24,11 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.fs.IndexedFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.utils.PathUtils; @@ -33,35 +36,34 @@ import org.sonar.api.utils.PathUtils; /** * @since 6.3 */ +@Immutable public class DefaultIndexedFile extends DefaultInputComponent implements IndexedFile { private final String relativePath; private final String moduleKey; private final Path moduleBaseDir; - private String language; + private final String language; private final Type type; + private final Path path; /** * Testing purposes only! */ - public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath) { - this(moduleKey, moduleBaseDir, relativePath, TestInputFileBuilder.nextBatchId()); + public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language) { + this(moduleKey, moduleBaseDir, relativePath, language, TestInputFileBuilder.nextBatchId()); } - public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, int batchId) { - this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, batchId); + public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language, int batchId) { + this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, language, batchId); } - public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, int batchId) { + public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, @Nullable String language, int batchId) { super(batchId); this.moduleKey = moduleKey; this.relativePath = PathUtils.sanitize(relativePath); this.moduleBaseDir = moduleBaseDir.normalize(); this.type = type; - } - - public DefaultIndexedFile setLanguage(@Nullable String language) { this.language = language; - return this; + this.path = this.moduleBaseDir.resolve(this.relativePath); } @Override @@ -81,7 +83,7 @@ public class DefaultIndexedFile extends DefaultInputComponent implements Indexed @Override public Path path() { - return moduleBaseDir.resolve(relativePath); + return path; } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index ff881a6f75d..b3ea8263c0d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -47,12 +47,13 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; private final DefaultIndexedFile indexedFile; + private final String contents; private final Consumer<DefaultInputFile> metadataGenerator; + private Status status; private Charset charset; private Metadata metadata; private boolean publish; - private String contents; public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) { this(indexedFile, metadataGenerator, null); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java index ac8eced2bbd..1165e54d494 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java @@ -19,13 +19,36 @@ */ package org.sonar.api.batch.fs.internal; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputModule; /** * @since 5.2 */ +@Immutable public class DefaultInputModule extends DefaultInputComponent implements InputModule { + private final File baseDir; + private final File workDir; + private final String name; + private final String version; + private final String originalName; + private final String originalVersion; + private final String description; + private final String keyWithBranch; + private final String branch; + private final List<String> sources; + private final List<String> tests; + private final Map<String, String> properties; private final String moduleKey; private final ProjectDefinition definition; @@ -36,9 +59,29 @@ public class DefaultInputModule extends DefaultInputComponent implements InputMo public DefaultInputModule(String moduleKey) { this(ProjectDefinition.create().setKey(moduleKey), TestInputFileBuilder.nextBatchId()); } + + /** + * For testing only! + */ + public DefaultInputModule(ProjectDefinition definition) { + this(definition, TestInputFileBuilder.nextBatchId()); + } public DefaultInputModule(ProjectDefinition definition, int batchId) { super(batchId); + this.baseDir = definition.getBaseDir(); + this.workDir = definition.getWorkDir(); + this.name = definition.getName(); + this.originalName = definition.getOriginalName(); + this.version = definition.getVersion(); + this.originalVersion = definition.getOriginalVersion(); + this.description = definition.getDescription(); + this.keyWithBranch = definition.getKeyWithBranch(); + this.branch = definition.getBranch(); + this.sources = Collections.unmodifiableList(new ArrayList<>(definition.sources())); + this.tests = Collections.unmodifiableList(new ArrayList<>(definition.tests())); + this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties())); + this.definition = definition; this.moduleKey = definition.getKey(); } @@ -59,5 +102,59 @@ public class DefaultInputModule extends DefaultInputComponent implements InputMo public ProjectDefinition definition() { return definition; } + + public File getBaseDir() { + return baseDir; + } + + public File getWorkDir() { + return workDir; + } + + public String getKeyWithBranch() { + return keyWithBranch; + } + + @CheckForNull + public String getBranch() { + return branch; + } + + public Map<String, String> properties() { + return properties; + } + + @CheckForNull + public String getOriginalVersion() { + return originalVersion; + } + + public String getVersion() { + return version; + } + + @CheckForNull + public String getOriginalName() { + return originalName; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + /** + * @return Source files and folders. + */ + public List<String> sources() { + return sources; + } + + public List<String> tests() { + return tests; + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java index f444c26bd0d..4a96ce61591 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java @@ -20,252 +20,34 @@ package org.sonar.api.batch.fs.internal; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.codec.digest.DigestUtils; -import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; +import org.sonar.api.batch.fs.internal.charhandler.CharHandler; +import org.sonar.api.batch.fs.internal.charhandler.FileHashComputer; +import org.sonar.api.batch.fs.internal.charhandler.LineCounter; +import org.sonar.api.batch.fs.internal.charhandler.LineHashComputer; +import org.sonar.api.batch.fs.internal.charhandler.LineOffsetCounter; /** * Computes hash of files. Ends of Lines are ignored, so files with * same content but different EOL encoding have the same hash. */ @ScannerSide +@Immutable public class FileMetadata { - - private static final Logger LOG = Loggers.get(FileMetadata.class); - private static final char LINE_FEED = '\n'; private static final char CARRIAGE_RETURN = '\r'; - public abstract static class CharHandler { - - protected void handleAll(char c) { - } - - protected void handleIgnoreEoL(char c) { - } - - protected void newLine() { - } - - protected void eof() { - } - } - - private static class LineCounter extends CharHandler { - private int lines = 1; - private int nonBlankLines = 0; - private boolean blankLine = true; - boolean alreadyLoggedInvalidCharacter = false; - private final String filePath; - private final Charset encoding; - - LineCounter(String filePath, Charset encoding) { - this.filePath = filePath; - this.encoding = encoding; - } - - @Override - protected void handleAll(char c) { - if (!alreadyLoggedInvalidCharacter && c == '\ufffd') { - LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath, - lines, encoding, CoreProperties.ENCODING_PROPERTY); - alreadyLoggedInvalidCharacter = true; - } - } - - @Override - protected void newLine() { - lines++; - if (!blankLine) { - nonBlankLines++; - } - blankLine = true; - } - - @Override - protected void handleIgnoreEoL(char c) { - if (!Character.isWhitespace(c)) { - blankLine = false; - } - } - - @Override - protected void eof() { - if (!blankLine) { - nonBlankLines++; - } - } - - public int lines() { - return lines; - } - - public int nonBlankLines() { - return nonBlankLines; - } - - } - - private static class FileHashComputer extends CharHandler { - private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest(); - private StringBuilder sb = new StringBuilder(); - private final CharsetEncoder encoder; - private final String filePath; - - public FileHashComputer(String filePath) { - encoder = StandardCharsets.UTF_8.newEncoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - this.filePath = filePath; - } - - @Override - protected void handleIgnoreEoL(char c) { - sb.append(c); - } - - @Override - protected void newLine() { - sb.append(LINE_FEED); - processBuffer(); - sb.setLength(0); - } - - @Override - protected void eof() { - if (sb.length() > 0) { - processBuffer(); - } - } - - private void processBuffer() { - try { - if (sb.length() > 0) { - ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb)); - globalMd5Digest.update(encoded.array(), 0, encoded.limit()); - } - } catch (CharacterCodingException e) { - throw new IllegalStateException("Error encoding line hash in file: " + filePath, e); - } - } - - @CheckForNull - public String getHash() { - return Hex.encodeHexString(globalMd5Digest.digest()); - } - } - - private static class LineHashComputer extends CharHandler { - private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest(); - private final CharsetEncoder encoder; - private final StringBuilder sb = new StringBuilder(); - private final LineHashConsumer consumer; - private final File file; - private int line = 1; - - public LineHashComputer(LineHashConsumer consumer, File f) { - this.consumer = consumer; - this.file = f; - this.encoder = StandardCharsets.UTF_8.newEncoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - } - - @Override - protected void handleIgnoreEoL(char c) { - if (!Character.isWhitespace(c)) { - sb.append(c); - } - } - - @Override - protected void newLine() { - processBuffer(); - sb.setLength(0); - line++; - } - - @Override - protected void eof() { - if (this.line > 0) { - processBuffer(); - } - } - - private void processBuffer() { - try { - if (sb.length() > 0) { - ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb)); - lineMd5Digest.update(encoded.array(), 0, encoded.limit()); - consumer.consume(line, lineMd5Digest.digest()); - } - } catch (CharacterCodingException e) { - throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e); - } - } - } - - private static class LineOffsetCounter extends CharHandler { - private long currentOriginalOffset = 0; - private IntArrayList originalLineOffsets = new IntArrayList(); - private long lastValidOffset = 0; - - public LineOffsetCounter() { - originalLineOffsets.add(0); - } - - @Override - protected void handleAll(char c) { - currentOriginalOffset++; - } - - @Override - protected void newLine() { - if (currentOriginalOffset > Integer.MAX_VALUE) { - throw new IllegalStateException("File is too big: " + currentOriginalOffset); - } - originalLineOffsets.add((int) currentOriginalOffset); - } - - @Override - protected void eof() { - lastValidOffset = currentOriginalOffset; - } - - public int[] getOriginalLineOffsets() { - return originalLineOffsets.trimAndGet(); - } - - public int getLastValidOffset() { - if (lastValidOffset > Integer.MAX_VALUE) { - throw new IllegalStateException("File is too big: " + lastValidOffset); - } - return (int) lastValidOffset; - } - - } - /** * Compute hash of a file ignoring line ends differences. * Maximum performance is needed. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java index 468a052f507..b258fb04dd2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/InputModuleHierarchy.java @@ -22,10 +22,11 @@ package org.sonar.api.batch.fs.internal; import java.util.Collection; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; import org.sonar.api.batch.fs.InputModule; -import org.sonar.api.batch.fs.internal.DefaultInputModule; +@Immutable public interface InputModuleHierarchy { DefaultInputModule root(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java index 71d79d1007a..9323c73b062 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/Metadata.java @@ -19,6 +19,11 @@ */ package org.sonar.api.batch.fs.internal; +import java.util.Arrays; + +import javax.annotation.concurrent.Immutable; + +@Immutable public class Metadata { private final int lines; private final int nonBlankLines; @@ -30,7 +35,7 @@ public class Metadata { this.lines = lines; this.nonBlankLines = nonBlankLines; this.hash = hash; - this.originalLineOffsets = originalLineOffsets; + this.originalLineOffsets = Arrays.copyOf(originalLineOffsets, originalLineOffsets.length); this.lastValidOffset = lastValidOffset; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java index 556ce3f5de0..5ec259f0021 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPattern.java @@ -19,11 +19,13 @@ */ package org.sonar.api.batch.fs.internal; +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.fs.IndexedFile; import org.sonar.api.utils.WildcardPattern; +@ThreadSafe public abstract class PathPattern { final WildcardPattern pattern; @@ -32,9 +34,9 @@ public abstract class PathPattern { this.pattern = WildcardPattern.create(pattern); } - public abstract boolean match(IndexedFile inputFile); + public abstract boolean match(String absolutePath, String relativePath); - public abstract boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension); + public abstract boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension); public static PathPattern create(String s) { String trimmed = StringUtils.trim(s); @@ -58,15 +60,15 @@ public abstract class PathPattern { } @Override - public boolean match(IndexedFile inputFile) { - return match(inputFile, true); + public boolean match(String absolutePath, String relativePath) { + return match(absolutePath, relativePath, true); } @Override - public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) { - String path = inputFile.absolutePath(); + public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) { + String path = absolutePath; if (!caseSensitiveFileExtension) { - String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName())); + String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath)); if (StringUtils.isNotBlank(extension)) { path = StringUtils.removeEndIgnoreCase(path, extension); path = path + extension; @@ -90,15 +92,15 @@ public abstract class PathPattern { } @Override - public boolean match(IndexedFile inputFile) { - return match(inputFile, true); + public boolean match(String absolutePath, String relativePath) { + return match(absolutePath, relativePath, true); } @Override - public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) { - String path = inputFile.relativePath(); + public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) { + String path = relativePath; if (!caseSensitiveFileExtension) { - String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName())); + String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath)); if (StringUtils.isNotBlank(extension)) { path = StringUtils.removeEndIgnoreCase(path, extension); path = path + extension; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java index 400fd64f37b..e6b4a9246e3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java @@ -34,7 +34,7 @@ class PathPatternPredicate extends AbstractFilePredicate { @Override public boolean apply(InputFile f) { - return pattern.match(f); + return pattern.match(f.absolutePath(), f.relativePath()); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java index 6fc584fdfbc..fa86a9e17e6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java @@ -66,7 +66,7 @@ public class TestInputFileBuilder { private int lastValidOffset = -1; private String hash; private int nonBlankLines; - private int[] originalLineOffsets; + private int[] originalLineOffsets = new int[0]; private boolean publish = true; private String contents; @@ -194,8 +194,7 @@ public class TestInputFileBuilder { } public DefaultInputFile build() { - DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, id); - indexedFile.setLanguage(language); + DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, id); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)), contents); @@ -206,8 +205,11 @@ public class TestInputFileBuilder { } public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) { - ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey); - definition.setBaseDir(baseDir); - return new DefaultInputModule(definition, TestInputFileBuilder.nextBatchId()); + ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey).setBaseDir(baseDir); + return newDefaultInputModule(definition); + } + + public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) { + return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId()); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java new file mode 100644 index 00000000000..76941323906 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/CharHandler.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal.charhandler; + +public abstract class CharHandler { + + public void handleAll(char c) { + } + + public void handleIgnoreEoL(char c) { + } + + public void newLine() { + } + + public void eof() { + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java new file mode 100644 index 00000000000..d1bfa79207e --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/FileHashComputer.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal.charhandler; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +import javax.annotation.CheckForNull; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; + +public class FileHashComputer extends CharHandler { + private static final char LINE_FEED = '\n'; + + + private MessageDigest globalMd5Digest = DigestUtils.getMd5Digest(); + private StringBuilder sb = new StringBuilder(); + private final CharsetEncoder encoder; + private final String filePath; + + public FileHashComputer(String filePath) { + encoder = StandardCharsets.UTF_8.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + this.filePath = filePath; + } + + @Override + public void handleIgnoreEoL(char c) { + sb.append(c); + } + + @Override + public void newLine() { + sb.append(LINE_FEED); + processBuffer(); + sb.setLength(0); + } + + @Override + public void eof() { + if (sb.length() > 0) { + processBuffer(); + } + } + + private void processBuffer() { + try { + if (sb.length() > 0) { + ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb)); + globalMd5Digest.update(encoded.array(), 0, encoded.limit()); + } + } catch (CharacterCodingException e) { + throw new IllegalStateException("Error encoding line hash in file: " + filePath, e); + } + } + + @CheckForNull + public String getHash() { + return Hex.encodeHexString(globalMd5Digest.digest()); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/IntArrayList.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java index 913c0a2433b..2bdcfb6a852 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/IntArrayList.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayList.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.api.batch.fs.internal; +package org.sonar.api.batch.fs.internal.charhandler; import java.util.Arrays; import java.util.Collection; @@ -72,11 +72,12 @@ class IntArrayList { } private void ensureCapacityInternal(int minCapacity) { + int capacity = minCapacity; if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + capacity = Math.max(DEFAULT_CAPACITY, minCapacity); } - ensureExplicitCapacity(minCapacity); + ensureExplicitCapacity(capacity); } private void ensureExplicitCapacity(int minCapacity) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java new file mode 100644 index 00000000000..c17a867f295 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineCounter.java @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal.charhandler; + +import java.nio.charset.Charset; + +import org.sonar.api.CoreProperties; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +public class LineCounter extends CharHandler { + private static final Logger LOG = Loggers.get(LineCounter.class); + + private int lines = 1; + private int nonBlankLines = 0; + private boolean blankLine = true; + boolean alreadyLoggedInvalidCharacter = false; + private final String filePath; + private final Charset encoding; + + public LineCounter(String filePath, Charset encoding) { + this.filePath = filePath; + this.encoding = encoding; + } + + @Override + public void handleAll(char c) { + if (!alreadyLoggedInvalidCharacter && c == '\ufffd') { + LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", filePath, + lines, encoding, CoreProperties.ENCODING_PROPERTY); + alreadyLoggedInvalidCharacter = true; + } + } + + @Override + public void newLine() { + lines++; + if (!blankLine) { + nonBlankLines++; + } + blankLine = true; + } + + @Override + public void handleIgnoreEoL(char c) { + if (!Character.isWhitespace(c)) { + blankLine = false; + } + } + + @Override + public void eof() { + if (!blankLine) { + nonBlankLines++; + } + } + + public int lines() { + return lines; + } + + public int nonBlankLines() { + return nonBlankLines; + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java new file mode 100644 index 00000000000..f371c71f5a1 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineHashComputer.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal.charhandler; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +import org.apache.commons.codec.digest.DigestUtils; +import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer; + +public class LineHashComputer extends CharHandler { + private final MessageDigest lineMd5Digest = DigestUtils.getMd5Digest(); + private final CharsetEncoder encoder; + private final StringBuilder sb = new StringBuilder(); + private final LineHashConsumer consumer; + private final File file; + private int line = 1; + + public LineHashComputer(LineHashConsumer consumer, File f) { + this.consumer = consumer; + this.file = f; + this.encoder = StandardCharsets.UTF_8.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + } + + @Override + public void handleIgnoreEoL(char c) { + if (!Character.isWhitespace(c)) { + sb.append(c); + } + } + + @Override + public void newLine() { + processBuffer(); + sb.setLength(0); + line++; + } + + @Override + public void eof() { + if (this.line > 0) { + processBuffer(); + } + } + + private void processBuffer() { + try { + if (sb.length() > 0) { + ByteBuffer encoded = encoder.encode(CharBuffer.wrap(sb)); + lineMd5Digest.update(encoded.array(), 0, encoded.limit()); + consumer.consume(line, lineMd5Digest.digest()); + } + } catch (CharacterCodingException e) { + throw new IllegalStateException("Error encoding line hash in file: " + file.getAbsolutePath(), e); + } + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java new file mode 100644 index 00000000000..cf39d16267f --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/LineOffsetCounter.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal.charhandler; + +public class LineOffsetCounter extends CharHandler { + private long currentOriginalOffset = 0; + private IntArrayList originalLineOffsets = new IntArrayList(); + private long lastValidOffset = 0; + + public LineOffsetCounter() { + originalLineOffsets.add(0); + } + + @Override + public void handleAll(char c) { + currentOriginalOffset++; + } + + @Override + public void newLine() { + if (currentOriginalOffset > Integer.MAX_VALUE) { + throw new IllegalStateException("File is too big: " + currentOriginalOffset); + } + originalLineOffsets.add((int) currentOriginalOffset); + } + + @Override + public void eof() { + lastValidOffset = currentOriginalOffset; + } + + public int[] getOriginalLineOffsets() { + return originalLineOffsets.trimAndGet(); + } + + public int getLastValidOffset() { + if (lastValidOffset > Integer.MAX_VALUE) { + throw new IllegalStateException("File is too big: " + lastValidOffset); + } + return (int) lastValidOffset; + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java new file mode 100644 index 00000000000..b8d3c63fcff --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/charhandler/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.api.batch.fs.internal.charhandler; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java index 7e8dffb5903..c51c5ddf969 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/measure/MetricFinder.java @@ -23,12 +23,15 @@ import java.io.Serializable; import java.util.Collection; import java.util.List; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.ScannerSide; /** * @since 4.5 */ @ScannerSide +@ThreadSafe public interface MetricFinder { @CheckForNull diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java index bf96a7a1103..c0df514efc0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java @@ -21,12 +21,15 @@ package org.sonar.api.batch.rule; import java.util.Map; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.rule.RuleKey; /** * Configuration of a rule activated on a Quality profile * @since 4.2 */ +@Immutable public interface ActiveRule { RuleKey ruleKey(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java index 1ea2b013ed7..f25c75a5af8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRules.java @@ -23,6 +23,7 @@ import org.sonar.api.batch.ScannerSide; import org.sonar.api.rule.RuleKey; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; import java.util.Collection; @@ -35,6 +36,7 @@ import java.util.Collection; * * @since 4.2 */ +@Immutable @ScannerSide public interface ActiveRules { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java index 7c06501dfbe..4781bb39fdd 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/RuleParam.java @@ -19,10 +19,14 @@ */ package org.sonar.api.batch.rule; +import javax.annotation.concurrent.Immutable; + /** * @since 4.2 */ +@Immutable public interface RuleParam { String key(); + String description(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java index 20b755c6fc7..c73cc867eee 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java @@ -23,6 +23,7 @@ import org.sonar.api.batch.ScannerSide; import org.sonar.api.rule.RuleKey; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; import java.util.Collection; @@ -33,6 +34,7 @@ import java.util.Collection; * @since 4.2 */ @ScannerSide +@Immutable public interface Rules { /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java index 9f4fd4bdbb0..981ebbbedde 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java @@ -20,7 +20,6 @@ package org.sonar.api.batch.rule.internal; import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.rule.RuleKey; @@ -28,35 +27,32 @@ import org.sonar.api.rule.RuleKey; import javax.annotation.concurrent.Immutable; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @Immutable public class DefaultActiveRules implements ActiveRules { - - // TODO use disk-backed cache (persistit) instead of full in-memory cache ? - private final ListMultimap<String, ActiveRule> activeRulesByRepository; + private final ImmutableListMultimap<String, ActiveRule> activeRulesByRepository; private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndKey = new HashMap<>(); private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndInternalKey = new HashMap<>(); - private final ListMultimap<String, ActiveRule> activeRulesByLanguage; + private final ImmutableListMultimap<String, ActiveRule> activeRulesByLanguage; public DefaultActiveRules(Collection<NewActiveRule> newActiveRules) { ImmutableListMultimap.Builder<String, ActiveRule> repoBuilder = ImmutableListMultimap.builder(); ImmutableListMultimap.Builder<String, ActiveRule> langBuilder = ImmutableListMultimap.builder(); for (NewActiveRule newAR : newActiveRules) { DefaultActiveRule ar = new DefaultActiveRule(newAR); - repoBuilder.put(ar.ruleKey().repository(), ar); + String repo = ar.ruleKey().repository(); + repoBuilder.put(repo, ar); if (ar.language() != null) { langBuilder.put(ar.language(), ar); } - if (!activeRulesByRepositoryAndKey.containsKey(ar.ruleKey().repository())) { - activeRulesByRepositoryAndKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>()); - activeRulesByRepositoryAndInternalKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>()); - } - activeRulesByRepositoryAndKey.get(ar.ruleKey().repository()).put(ar.ruleKey().rule(), ar); + + activeRulesByRepositoryAndKey.computeIfAbsent(repo, r -> new HashMap<>()).put(ar.ruleKey().rule(), ar); String internalKey = ar.internalKey(); if (internalKey != null) { - activeRulesByRepositoryAndInternalKey.get(ar.ruleKey().repository()).put(internalKey, ar); + activeRulesByRepositoryAndInternalKey.computeIfAbsent(repo, r -> new HashMap<>()).put(internalKey, ar); } } activeRulesByRepository = repoBuilder.build(); @@ -65,11 +61,8 @@ public class DefaultActiveRules implements ActiveRules { @Override public ActiveRule find(RuleKey ruleKey) { - Map<String, ActiveRule> map = activeRulesByRepositoryAndKey.get(ruleKey.repository()); - if(map != null) { - return map.get(ruleKey.rule()); - } - return null; + return activeRulesByRepositoryAndKey.getOrDefault(ruleKey.repository(), Collections.emptyMap()) + .get(ruleKey.rule()); } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java index 37795039e83..1b46101fe24 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRules.java @@ -25,7 +25,6 @@ import com.google.common.collect.HashBasedTable; import org.sonar.api.batch.rule.Rule; import com.google.common.collect.Table; import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.rule.Rules; import org.sonar.api.rule.RuleKey; @@ -39,9 +38,7 @@ import java.util.List; @Immutable class DefaultRules implements Rules { - - // TODO use disk-backed cache (persistit) instead of full in-memory cache ? - private final ListMultimap<String, Rule> rulesByRepository; + private final ImmutableListMultimap<String, Rule> rulesByRepository; private final ImmutableTable<String, String, List<Rule>> rulesByRepositoryAndInternalKey; DefaultRules(Collection<NewRule> newRules) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java index abdb03c9ecc..88e9c1d6b13 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java @@ -57,7 +57,7 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { this.inputFile = requireNonNull(inputFile, "file can't be null"); String[] cpdExclusions = config.getStringArray(CoreProperties.CPD_EXCLUSIONS); for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) { - if (cpdExclusion.match(inputFile)) { + if (cpdExclusion.match(inputFile.absolutePath(), inputFile.relativePath())) { this.excluded = true; } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java index 4cb9ded0ef5..65752a177c6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java @@ -28,6 +28,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.component.Component; import org.sonar.api.scan.filesystem.PathResolver; @@ -39,6 +40,10 @@ import org.sonar.api.scan.filesystem.PathResolver; public class Project extends Resource implements Component { private final ProjectDefinition definition; + public Project(DefaultInputModule module) { + this(module.definition()); + } + public Project(ProjectDefinition definition) { this.definition = definition; this.setKey(definition.getKey()); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java index fdc1dd19afd..17698835e8e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java @@ -25,6 +25,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.io.FilenameUtils; import org.sonar.api.batch.ScannerSide; import org.sonar.api.utils.PathUtils; @@ -35,6 +37,7 @@ import static java.util.stream.Collectors.joining; * @since 3.5 */ @ScannerSide +@Immutable public class PathResolver { public File relativeFile(File dir, String path) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java index 6928b75e036..76e1a74ec29 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java @@ -22,12 +22,14 @@ package org.sonar.api.scan.issue.filter; import java.util.Date; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; import org.sonar.api.rule.RuleKey; /** * @since 5.3 */ +@ThreadSafe public interface FilterableIssue { String componentKey(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java index 230ad24605d..0c2847f7a1e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java @@ -19,6 +19,9 @@ */ package org.sonar.api.scan.issue.filter; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.ExtensionPoint; import org.sonar.api.batch.ScannerSide; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -27,6 +30,7 @@ import org.sonarsource.api.sonarlint.SonarLintSide; @SonarLintSide @ExtensionPoint @FunctionalInterface +@ThreadSafe /** * @since 5.3 */ @@ -40,6 +44,9 @@ public interface IssueFilter { * </ul> * The <code>chain</code> parameter allows for fine control of the filtering logic: it is each filter's duty to either pass the issue to the next filter, by calling * the {@link IssueFilterChain#accept} method, or return directly if the issue has to be accepted or not + * + * Implementations should be thread safe. + * * @param issue the issue being filtered * @param chain the rest of the filters * @return <code>true</code> to accept the issue, <code>false</code> to reject it, {@link IssueFilterChain#accept} to let the other filters decide. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java index aab918618ce..02b6943b63f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilterChain.java @@ -19,6 +19,8 @@ */ package org.sonar.api.scan.issue.filter; +import javax.annotation.concurrent.ThreadSafe; + /** * A filter chain is an object provided to issues filters for fine control over the filtering logic. Each filter has the choice to: * <ul> @@ -29,6 +31,7 @@ package org.sonar.api.scan.issue.filter; * * @since 5.3 */ +@ThreadSafe public interface IssueFilterChain { /** * Called by a filter to let downstream filters decide the fate of the issue diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java index a1a0f064569..310e788d36d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java @@ -19,10 +19,13 @@ */ package org.sonar.api.utils; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang.StringUtils; /** @@ -54,12 +57,12 @@ import org.apache.commons.lang.StringUtils; * <a href="https://github.com/JetBrains/intellij-community/blob/idea/107.743/platform/util/src/com/intellij/openapi/util/io/FileUtil.java#L847">FileUtil</a> * from IntelliJ OpenAPI. * - * * @since 1.10 */ +@ThreadSafe public class WildcardPattern { - private static final Map<String, WildcardPattern> CACHE = new HashMap<>(); + private static final Map<String, WildcardPattern> CACHE = Collections.synchronizedMap(new HashMap<>()); private static final String SPECIAL_CHARS = "()[]^$.{}+|"; private Pattern pattern; @@ -196,11 +199,6 @@ public class WildcardPattern { */ public static WildcardPattern create(String pattern, String directorySeparator) { String key = pattern + directorySeparator; - WildcardPattern wildcardPattern = CACHE.get(key); - if (wildcardPattern == null) { - wildcardPattern = new WildcardPattern(pattern, directorySeparator); - CACHE.put(key, wildcardPattern); - } - return wildcardPattern; + return CACHE.computeIfAbsent(key, k -> new WildcardPattern(pattern, directorySeparator)); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java index 0ae07c5e8d9..82ccf11b3e8 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java @@ -54,7 +54,7 @@ public class DefaultInputFileTest { Path baseDir = temp.newFolder().toPath(); Metadata metadata = new Metadata(42, 42, "", new int[0], 0); - DefaultIndexedFile indexedFile = new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0).setLanguage("php"); + DefaultIndexedFile indexedFile = new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, (f) -> f.setMetadata(metadata)) .setStatus(InputFile.Status.ADDED) .setCharset(StandardCharsets.ISO_8859_1); @@ -82,10 +82,9 @@ public class DefaultInputFileTest { Metadata metadata = new Metadata(42, 30, "", new int[0], 0); - DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0) - .setLanguage("php"), f -> f.setMetadata(metadata)) - .setStatus(InputFile.Status.ADDED) - .setCharset(StandardCharsets.ISO_8859_1); + DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0), f -> f.setMetadata(metadata)) + .setStatus(InputFile.Status.ADDED) + .setCharset(StandardCharsets.ISO_8859_1); assertThat(inputFile.contents()).isEqualTo(content); try (InputStream inputStream = inputFile.inputStream()) { @@ -110,10 +109,9 @@ public class DefaultInputFileTest { Metadata metadata = new Metadata(42, 30, "", new int[0], 0); - DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0) - .setLanguage("php"), f -> f.setMetadata(metadata)) - .setStatus(InputFile.Status.ADDED) - .setCharset(StandardCharsets.UTF_8); + DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0), f -> f.setMetadata(metadata)) + .setStatus(InputFile.Status.ADDED) + .setCharset(StandardCharsets.UTF_8); assertThat(inputFile.contents()).isEqualTo(content); try (InputStream inputStream = inputFile.inputStream()) { @@ -125,9 +123,9 @@ public class DefaultInputFileTest { @Test public void test_equals_and_hashcode() throws Exception { - DefaultInputFile f1 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class)); - DefaultInputFile f1a = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class)); - DefaultInputFile f2 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Bar.php"), (f) -> mock(Metadata.class)); + DefaultInputFile f1 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class)); + DefaultInputFile f1a = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class)); + DefaultInputFile f2 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Bar.php", null), (f) -> mock(Metadata.class)); assertThat(f1).isEqualTo(f1); assertThat(f1).isEqualTo(f1a); @@ -141,14 +139,14 @@ public class DefaultInputFileTest { @Test public void test_toString() throws Exception { - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class)); assertThat(file.toString()).isEqualTo("[moduleKey=ABCDE, relative=src/Foo.php, basedir=module]"); } @Test public void checkValidPointer() { Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); assertThat(file.newPointer(1, 0).line()).isEqualTo(1); assertThat(file.newPointer(1, 0).lineOffset()).isEqualTo(0); // Don't fail @@ -185,7 +183,7 @@ public class DefaultInputFileTest { @Test public void checkValidPointerUsingGlobalOffset() { Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); assertThat(file.newPointer(0).line()).isEqualTo(1); assertThat(file.newPointer(0).lineOffset()).isEqualTo(0); @@ -216,7 +214,7 @@ public class DefaultInputFileTest { @Test public void checkValidRange() { Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde")); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 1)).start().line()).isEqualTo(1); // Don't fail @@ -242,7 +240,7 @@ public class DefaultInputFileTest { @Test public void selectLine() { Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde\n\nabc")); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); assertThat(file.selectLine(1).start().line()).isEqualTo(1); assertThat(file.selectLine(1).start().lineOffset()).isEqualTo(0); @@ -266,7 +264,7 @@ public class DefaultInputFileTest { @Test public void checkValidRangeUsingGlobalOffset() { Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); TextRange newRange = file.newRange(10, 13); assertThat(newRange.start().line()).isEqualTo(2); assertThat(newRange.start().lineOffset()).isEqualTo(0); @@ -277,7 +275,7 @@ public class DefaultInputFileTest { @Test public void testRangeOverlap() { Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15); - DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata)); + DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata)); // Don't fail assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)))).isTrue(); assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 2)))).isTrue(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java index 0337f6044b1..fac72032858 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/PathPatternTest.java @@ -45,26 +45,26 @@ public class PathPatternTest { PathPattern pattern = PathPattern.create("**/*Foo.java"); assertThat(pattern.toString()).isEqualTo("**/*Foo.java"); - IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java"); - assertThat(pattern.match(indexedFile)).isTrue(); + IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isTrue(); // case sensitive by default - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA"); - assertThat(pattern.match(indexedFile)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse(); - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java"); - assertThat(pattern.match(indexedFile)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse(); } @Test public void match_relative_path_and_insensitive_file_extension() throws Exception { PathPattern pattern = PathPattern.create("**/*Foo.java"); - IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA"); - assertThat(pattern.match(indexedFile, false)).isTrue(); + IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isTrue(); - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java"); - assertThat(pattern.match(indexedFile, false)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isFalse(); } @Test @@ -72,15 +72,15 @@ public class PathPatternTest { PathPattern pattern = PathPattern.create("file:**/src/main/**Foo.java"); assertThat(pattern.toString()).isEqualTo("file:**/src/main/**Foo.java"); - IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java"); - assertThat(pattern.match(indexedFile)).isTrue(); + IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isTrue(); // case sensitive by default - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA"); - assertThat(pattern.match(indexedFile)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse(); - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java"); - assertThat(pattern.match(indexedFile)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse(); } @Test @@ -88,11 +88,11 @@ public class PathPatternTest { PathPattern pattern = PathPattern.create("file:**/src/main/**Foo.java"); assertThat(pattern.toString()).isEqualTo("file:**/src/main/**Foo.java"); - IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA"); - assertThat(pattern.match(indexedFile, false)).isTrue(); + IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isTrue(); - indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.JAVA"); - assertThat(pattern.match(indexedFile, false)).isFalse(); + indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.JAVA", null); + assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isFalse(); } @Test diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/IntArrayListTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayListTest.java index c9c7e1912d0..a4e931e1e30 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/IntArrayListTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/charhandler/IntArrayListTest.java @@ -17,9 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.api.batch.fs.internal; +package org.sonar.api.batch.fs.internal.charhandler; import org.junit.Test; +import org.sonar.api.batch.fs.internal.charhandler.IntArrayList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java index 0c0b42bef0a..28d1af3517b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java @@ -19,12 +19,12 @@ */ package org.sonar.scanner; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; +import static java.util.stream.Collectors.toMap; + +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; + import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.SensorContext; @@ -35,10 +35,11 @@ import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; import org.sonar.scanner.scan.measure.MeasureCache; -import static java.util.stream.Collectors.toMap; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; public class DefaultFileLinesContext implements FileLinesContext { - private final SensorContext context; private final InputFile inputFile; private final MetricFinder metricFinder; @@ -47,7 +48,7 @@ public class DefaultFileLinesContext implements FileLinesContext { /** * metric key -> line -> value */ - private final Map<String, Map<Integer, Object>> map = Maps.newHashMap(); + private final Map<String, Map<Integer, Object>> map = new HashMap<>(); public DefaultFileLinesContext(SensorContext context, InputFile inputFile, MetricFinder metricFinder, MeasureCache measureCache) { this.context = context; @@ -73,13 +74,7 @@ public class DefaultFileLinesContext implements FileLinesContext { public Integer getIntValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); checkLineRange(line); - - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - // not in memory, so load - lines = loadData(metricKey, KeyValueFormat.newIntegerConverter()); - map.put(metricKey, lines); - } + Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newIntegerConverter())); return (Integer) lines.get(line); } @@ -96,27 +91,13 @@ public class DefaultFileLinesContext implements FileLinesContext { public String getStringValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); checkLineRange(line); - - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - // not in memory, so load - lines = loadData(metricKey, KeyValueFormat.newStringConverter()); - map.put(metricKey, lines); - } + Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newStringConverter())); return (String) lines.get(line); } - private Map<Integer, Object> getOrCreateLines(String metricKey) { - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - lines = Maps.newHashMap(); - map.put(metricKey, lines); - } - return lines; - } - private void setValue(String metricKey, int line, Object value) { - getOrCreateLines(metricKey).put(line, value); + map.computeIfAbsent(metricKey, k -> new HashMap<>()) + .put(line, value); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java index 40d7cbcd3c7..c0eed526886 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java @@ -21,6 +21,7 @@ package org.sonar.scanner; import java.util.Date; import java.util.Optional; + import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; @@ -31,6 +32,8 @@ import org.sonar.api.utils.System2; /** * @since 6.3 + * + * Immutable after {@link #start()} */ @ScannerSide public class ProjectAnalysisInfo implements Startable { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java index dc4a860533c..1c0ec317ad7 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java @@ -22,10 +22,11 @@ package org.sonar.scanner.analysis; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; + import org.picocontainer.ComponentLifecycle; import org.picocontainer.PicoContainer; import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.internal.DefaultTempFolder; @@ -34,9 +35,9 @@ public class AnalysisTempFolderProvider extends ProviderAdapter implements Compo private DefaultTempFolder projectTempFolder; private boolean started = false; - public TempFolder provide(ProjectReactor projectReactor) { + public TempFolder provide(InputModuleHierarchy moduleHierarchy) { if (projectTempFolder == null) { - Path workingDir = projectReactor.getRoot().getWorkDir().toPath(); + Path workingDir = moduleHierarchy.root().getWorkDir().toPath(); Path tempDir = workingDir.normalize().resolve(TMP_NAME); try { Files.deleteIfExists(tempDir); @@ -64,7 +65,7 @@ public class AnalysisTempFolderProvider extends ProviderAdapter implements Compo @Override public void dispose(PicoContainer container) { - //nothing to do + // nothing to do } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java index 9414be0017f..28fefb1080a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java @@ -21,6 +21,8 @@ package org.sonar.scanner.analysis; import java.util.Map; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -30,6 +32,7 @@ import org.sonar.scanner.bootstrap.GlobalProperties; /** * @since 4.0 */ +@Immutable public class DefaultAnalysisMode extends AbstractAnalysisMode { private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java index 76b37d87b29..743294be0f8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java @@ -19,10 +19,13 @@ */ package org.sonar.scanner.bootstrap; +import javax.annotation.concurrent.Immutable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; +@Immutable public class GlobalMode extends AbstractAnalysisMode { private static final Logger LOG = LoggerFactory.getLogger(GlobalMode.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java index 70749f70fc0..0117f735ba6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java @@ -30,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nullable; + import org.apache.commons.lang.ClassUtils; import org.sonar.api.batch.CheckProject; import org.sonar.api.batch.DependedUpon; @@ -279,7 +280,7 @@ public class ScannerExtensionDictionnary { || (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class))) && (matcher == null || matcher.accept(extension)); if (keep && module != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { - keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module.definition())); + keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module)); } return keep; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java index 87a0794c1e5..e8e1d804b36 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java @@ -19,9 +19,8 @@ */ package org.sonar.scanner.cpd; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Predicate; +import static com.google.common.collect.FluentIterable.from; + import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -30,10 +29,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; + import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputComponent; -import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.duplications.block.Block; @@ -49,7 +48,9 @@ import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.util.ProgressReport; -import static com.google.common.collect.FluentIterable.from; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Predicate; /** * Runs on the root module, at the end of the project analysis. @@ -66,12 +67,12 @@ public class CpdExecutor { private final SonarCpdBlockIndex index; private final ReportPublisher publisher; private final InputComponentStore componentStore; - private final Configuration settings; private final ProgressReport progressReport; + private final CpdSettings settings; private int count; private int total; - public CpdExecutor(Configuration settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) { + public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) { this.settings = settings; this.index = index; this.publisher = publisher; @@ -139,7 +140,8 @@ public class CpdExecutor { List<CloneGroup> filtered; if (!"java".equalsIgnoreCase(inputFile.language())) { - Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(inputFile.language())); + int minTokens = settings.getMinimumTokens(inputFile.language()); + Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(minTokens); filtered = from(duplications).filter(minimumTokensPredicate).toList(); } else { filtered = duplications; @@ -149,17 +151,6 @@ public class CpdExecutor { } @VisibleForTesting - /** - * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block. - * Also, it uses statements instead of tokens. - * @param languageKey - * @return - */ - int getMinimumTokens(String languageKey) { - return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100); - } - - @VisibleForTesting final void saveDuplications(final DefaultInputComponent component, List<CloneGroup> duplications) { if (duplications.size() > MAX_CLONE_GROUP_PER_FILE) { LOG.warn("Too many duplication groups on file " + component + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java new file mode 100644 index 00000000000..7335d85ef88 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.cpd; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.config.Configuration; +import org.sonar.duplications.block.BlockChunker; + +public class CpdSettings { + private final Configuration settings; + private final String branch; + + public CpdSettings(Configuration settings, InputModuleHierarchy hierarchy) { + this.settings = settings; + this.branch = hierarchy.root().getBranch(); + } + + public boolean isCrossProjectDuplicationEnabled() { + return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false) + // No cross project duplication for branches + && StringUtils.isBlank(branch); + } + + /** + * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block. + * Also, it uses statements instead of tokens. + * @param languageKey + * @return + */ + int getMinimumTokens(String languageKey) { + return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java index 2a4cdf9eab0..a5bc77ba3e6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java @@ -24,10 +24,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.stream.Collectors; -import org.sonar.api.CoreProperties; + import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.duplications.block.Block; @@ -36,6 +35,7 @@ import org.sonar.duplications.index.AbstractCloneIndex; import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.protocol.output.FileStructure; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.report.ReportPublisher; @@ -44,17 +44,17 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { private static final Logger LOG = Loggers.get(SonarCpdBlockIndex.class); private final CloneIndex mem = new PackedMemoryCloneIndex(); private final ReportPublisher publisher; - private final Configuration settings; // Files already tokenized private final Set<InputFile> indexedFiles = new HashSet<>(); + private final CpdSettings settings; - public SonarCpdBlockIndex(ReportPublisher publisher, Configuration settings) { + public SonarCpdBlockIndex(ReportPublisher publisher, CpdSettings settings) { this.publisher = publisher; this.settings = settings; } public void insert(InputFile inputFile, Collection<Block> blocks) { - if (isCrossProjectDuplicationEnabled(settings)) { + if (settings.isCrossProjectDuplicationEnabled()) { int id = ((DefaultInputFile) inputFile).batchId(); if (publisher.getWriter().hasComponentData(FileStructure.Domain.CPD_TEXT_BLOCKS, id)) { throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + inputFile.absolutePath()); @@ -87,12 +87,6 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { return indexedFiles.contains(inputFile); } - public static boolean isCrossProjectDuplicationEnabled(Configuration settings) { - return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false) - // No cross project duplication for branches - && !settings.get(CoreProperties.PROJECT_BRANCH_PROPERTY).isPresent(); - } - public Collection<Block> getByInputFile(String resourceKey) { return mem.getByResourceId(resourceKey); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java index 1690a8a44be..14eb7316b40 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java @@ -123,7 +123,7 @@ public class DefaultIndex { if (component == null) { throw new IllegalStateException("Invalid component key: " + key); } - if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) { + if (DefaultSensorStorage.isDeprecatedMetric(measure.getMetricKey())) { // Ignore deprecated metrics return measure; } @@ -187,7 +187,7 @@ public class DefaultIndex { } else if (inputComponent instanceof InputFile) { r = File.create(((InputFile) inputComponent).relativePath()); } else if (inputComponent instanceof InputModule) { - r = new Project(((DefaultInputModule) inputComponent).definition()); + r = new Project(((DefaultInputModule) inputComponent)); } else { throw new IllegalArgumentException("Unknow input path type: " + inputComponent); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java index ccc86732f23..a0331d07aa9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java @@ -20,6 +20,9 @@ package org.sonar.scanner.issue; import java.util.Date; + +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.batch.fs.InputModule; @@ -29,6 +32,7 @@ import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.protocol.output.ScannerReport.Issue; +@ThreadSafe public class DefaultFilterableIssue implements FilterableIssue { private final Issue rawIssue; private final ProjectAnalysisInfo projectAnalysisInfo; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java index d0f01cc432d..fb8105bfd58 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java @@ -22,10 +22,14 @@ package org.sonar.scanner.issue; import java.util.Arrays; import java.util.Collections; import java.util.List; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilter; import org.sonar.api.scan.issue.filter.IssueFilterChain; +@ThreadSafe public class DefaultIssueFilterChain implements IssueFilterChain { private final List<IssueFilter> filters; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java index db9105a3f90..f31865db0cf 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java @@ -30,14 +30,14 @@ import org.sonar.scanner.protocol.output.ScannerReport; @ScannerSide public class IssueFilters { - private final IssueFilter[] filters; + private final IssueFilterChain filterChain; private final org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters; private final DefaultInputModule module; private final ProjectAnalysisInfo projectAnalysisInfo; public IssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters, org.sonar.api.issue.batch.IssueFilter[] filters) { this.module = module; - this.filters = exclusionFilters; + this.filterChain = new DefaultIssueFilterChain(exclusionFilters); this.deprecatedFilters = filters; this.projectAnalysisInfo = projectAnalysisInfo; } @@ -55,7 +55,6 @@ public class IssueFilters { } public boolean accept(String componentKey, ScannerReport.Issue rawIssue) { - IssueFilterChain filterChain = new DefaultIssueFilterChain(filters); FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey); if (filterChain.accept(fIssue)) { return acceptDeprecated(componentKey, rawIssue); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java index 874e4514cf9..279b003d933 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java @@ -19,7 +19,8 @@ */ package org.sonar.scanner.issue; -import com.google.common.base.Strings; +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.rule.ActiveRule; @@ -35,9 +36,12 @@ import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation; import org.sonar.scanner.report.ReportPublisher; +import com.google.common.base.Strings; + /** * Initialize the issues raised during scan. */ +@ThreadSafe public class ModuleIssues { private final ActiveRules activeRules; @@ -62,9 +66,19 @@ public class ModuleIssues { return false; } - String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message(); + ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.batchId(), rule.name(), activeRule.severity()); + + if (filters.accept(inputComponent.key(), rawIssue)) { + write(inputComponent.batchId(), rawIssue); + return true; + } + return false; + } + + private static ScannerReport.Issue createReportIssue(Issue issue, int batchId, String ruleName, String activeRuleSeverity) { + String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? ruleName : issue.primaryLocation().message(); org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity(); - Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity()); + Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity); ScannerReport.Issue.Builder builder = ScannerReport.Issue.newBuilder(); ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder(); @@ -76,7 +90,7 @@ public class ModuleIssues { builder.setMsg(primaryMessage); locationBuilder.setMsg(primaryMessage); - locationBuilder.setComponentRef(inputComponent.batchId()); + locationBuilder.setComponentRef(batchId); TextRange primaryTextRange = issue.primaryLocation().textRange(); if (primaryTextRange != null) { builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange)); @@ -86,13 +100,7 @@ public class ModuleIssues { builder.setGap(gap); } applyFlows(builder, locationBuilder, textRangeBuilder, issue); - ScannerReport.Issue rawIssue = builder.build(); - - if (filters.accept(inputComponent.key(), rawIssue)) { - write(inputComponent.batchId(), rawIssue); - return true; - } - return false; + return builder.build(); } private static void applyFlows(ScannerReport.Issue.Builder builder, ScannerReport.IssueLocation.Builder locationBuilder, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java index 66efeb194f3..dc96c80d2d1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java @@ -19,7 +19,13 @@ */ package org.sonar.scanner.issue.ignore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputComponent; @@ -31,14 +37,15 @@ import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; import org.sonar.scanner.scan.filesystem.InputComponentStore; +@ThreadSafe public class EnforceIssuesFilter implements IssueFilter { private static final Logger LOG = LoggerFactory.getLogger(EnforceIssuesFilter.class); - private IssueInclusionPatternInitializer patternInitializer; - private InputComponentStore componentStore; + private final List<IssuePattern> multicriteriaPatterns; + private final InputComponentStore componentStore; public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, InputComponentStore componentStore) { - this.patternInitializer = patternInitializer; + this.multicriteriaPatterns = Collections.unmodifiableList(new ArrayList<>(patternInitializer.getMulticriteriaPatterns())); this.componentStore = componentStore; } @@ -48,7 +55,7 @@ public class EnforceIssuesFilter implements IssueFilter { boolean atLeastOnePatternFullyMatched = false; IssuePattern matchingPattern = null; - for (IssuePattern pattern : patternInitializer.getMulticriteriaPatterns()) { + for (IssuePattern pattern : multicriteriaPatterns) { if (pattern.getRulePattern().match(issue.ruleKey().toString())) { atLeastOneRuleMatched = true; String relativePath = getRelativePath(issue.componentKey()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java index 4d326f7c198..689d4158d8d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java @@ -24,17 +24,20 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.WildcardPattern; +@Immutable public class IssuePattern { private final WildcardPattern resourcePattern; private final WildcardPattern rulePattern; - private final Set<Integer> lines = new LinkedHashSet<>(); - private final Set<LineRange> lineRanges = new LinkedHashSet<>(); + private final Set<Integer> lines; + private final Set<LineRange> lineRanges; private final boolean checkLines; public IssuePattern(String resourcePattern, String rulePattern) { @@ -45,13 +48,19 @@ public class IssuePattern { this.resourcePattern = WildcardPattern.create(resourcePattern); this.rulePattern = WildcardPattern.create(rulePattern); this.checkLines = !lineRanges.isEmpty(); + Set<Integer> modifiableLines = new LinkedHashSet<>(); + Set<LineRange> modifiableLineRanges = new LinkedHashSet<>(); + for (LineRange range : lineRanges) { if (range.from() == range.to()) { - this.lines.add(range.from()); + modifiableLines.add(range.from()); } else { - this.lineRanges.add(range); + modifiableLineRanges.add(range); } } + + this.lines = Collections.unmodifiableSet(modifiableLines); + this.lineRanges = Collections.unmodifiableSet(modifiableLineRanges); } public WildcardPattern getResourcePattern() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java index 4bdf149ec05..df5ce6c6f1d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler; +import org.sonar.api.batch.fs.internal.charhandler.CharHandler; import org.sonar.scanner.issue.ignore.pattern.BlockIssuePattern; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java index e0230681382..3611b6d7ea5 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java @@ -27,7 +27,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler; +import org.sonar.api.batch.fs.internal.charhandler.CharHandler; import org.sonar.scanner.issue.ignore.pattern.LineRange; import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher; @@ -57,19 +57,19 @@ public class IssueExclusionsRegexpScanner extends CharHandler { } @Override - protected void handleIgnoreEoL(char c) { + public void handleIgnoreEoL(char c) { sb.append(c); } @Override - protected void newLine() { + public void newLine() { processLine(sb.toString()); sb.setLength(0); lineIndex++; } @Override - protected void eof() { + public void eof() { processLine(sb.toString()); if (currentMatcher != null && !currentMatcher.hasSecondPattern()) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java index 1d659f99c9c..8671b672c74 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java @@ -21,16 +21,16 @@ package org.sonar.scanner.issue.tracking; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; import org.sonar.scanner.repository.ServerIssuesLoader; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.storage.Storage; import org.sonar.scanner.storage.Storages; @@ -45,21 +45,20 @@ public class ServerIssueRepository { private final Storages caches; private Storage<ServerIssue> issuesCache; private final ServerIssuesLoader previousIssuesLoader; - private final ImmutableProjectReactor reactor; - private final InputComponentStore resourceCache; + private final InputComponentStore componentStore; - public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, ImmutableProjectReactor reactor, InputComponentStore resourceCache) { + public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, InputComponentStore componentStore) { this.caches = caches; this.previousIssuesLoader = previousIssuesLoader; - this.reactor = reactor; - this.resourceCache = resourceCache; + this.componentStore = componentStore; } public void load() { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); this.issuesCache = caches.createCache("previousIssues"); caches.registerValueCoder(ServerIssue.class, new ServerIssueValueCoder()); - previousIssuesLoader.load(reactor.getRoot().getKeyWithBranch(), this::store); + DefaultInputModule root = (DefaultInputModule) componentStore.root(); + previousIssuesLoader.load(root.getKeyWithBranch(), this::store); profiler.stopInfo(); } @@ -69,10 +68,10 @@ public class ServerIssueRepository { private void store(ServerIssue issue) { String moduleKeyWithBranch = issue.getModuleKey(); - ProjectDefinition projectDefinition = reactor.getProjectDefinition(moduleKeyWithBranch); - if (projectDefinition != null) { - String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(projectDefinition.getKey(), issue.hasPath() ? issue.getPath() : null); - DefaultInputComponent r = (DefaultInputComponent) resourceCache.getByKey(componentKeyWithoutBranch); + InputModule module = componentStore.getModule(moduleKeyWithBranch); + if (module != null) { + String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(module.key(), issue.hasPath() ? issue.getPath() : null); + DefaultInputComponent r = (DefaultInputComponent) componentStore.getByKey(componentKeyWithoutBranch); if (r != null) { issuesCache.put(r.batchId(), issue.getKey(), issue); return; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java index 46acfff0279..e4e5c81356e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java @@ -22,6 +22,7 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; @@ -40,9 +41,10 @@ public abstract class AbstractPhaseExecutor { private final DefaultModuleFileSystem fs; private final QProfileVerifier profileVerifier; private final IssueExclusionsLoader issueExclusionsLoader; + private final InputModuleHierarchy hierarchy; public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, - SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + SensorContext sensorContext, InputModuleHierarchy hierarchy, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader) { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; @@ -53,6 +55,7 @@ public abstract class AbstractPhaseExecutor { this.fs = fs; this.profileVerifier = profileVerifier; this.issueExclusionsLoader = issueExclusionsLoader; + this.hierarchy = hierarchy; } /** @@ -76,7 +79,7 @@ public abstract class AbstractPhaseExecutor { afterSensors(); - if (module.definition().getParent() == null) { + if (hierarchy.isRoot(module)) { executeOnRoot(); postJobsExecutor.execute(sensorContext); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java index b4d5818fc47..556e6298652 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java @@ -19,8 +19,8 @@ */ package org.sonar.scanner.phases; -import com.google.common.collect.Lists; import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.Initializer; import org.sonar.api.batch.fs.internal.DefaultInputModule; @@ -31,6 +31,8 @@ import org.sonar.api.utils.log.Profiler; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; +import com.google.common.collect.Lists; + public class InitializersExecutor { private static final Logger LOG = Loggers.get(SensorsExecutor.class); @@ -52,7 +54,7 @@ public class InitializersExecutor { LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); } - Project project = new Project(module.definition()); + Project project = new Project(module); for (Initializer initializer : initializers) { eventBus.fireEvent(new InitializerExecutionEvent(initializer, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java index 7893e6a0024..e2fd0aea9ff 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java @@ -22,6 +22,7 @@ package org.sonar.scanner.phases; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.issue.IssueCallback; @@ -43,8 +44,8 @@ public final class IssuesPhaseExecutor extends AbstractPhaseExecutor { public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback, InputModuleHierarchy moduleHierarchy) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, moduleHierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.issuesReport = jsonReport; this.localIssueTracking = localIssueTracking; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java index b3b33277f66..d4b42a5f97c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java @@ -21,6 +21,7 @@ package org.sonar.scanner.phases; import java.util.ArrayList; import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.ScannerSide; @@ -58,7 +59,7 @@ public class PostJobsExecutor { private void execute(SensorContext context, Collection<PostJob> postJobs) { logPostJobs(postJobs); - Project project = new Project(module.definition()); + Project project = new Project(module); for (PostJob postJob : postJobs) { LOG.info("Executing post-job {}", ScannerUtils.describe(postJob)); eventBus.fireEvent(new PostJobExecutionEvent(postJob, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java index d8a5d9fcac8..63868c8e2cb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java @@ -34,7 +34,7 @@ class ProjectAnalysisEvent extends AbstractPhaseEvent<ProjectAnalysisHandler> @Override public Project getProject() { - return new Project(module.definition()); + return new Project(module); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java index 434c88a96dd..84ff4b4d4bc 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java @@ -20,6 +20,7 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.cpd.CpdExecutor; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; @@ -38,9 +39,9 @@ public final class PublishPhaseExecutor extends AbstractPhaseExecutor { private final ScmPublisher scm; public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, - EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, - QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, hierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.reportPublisher = reportPublisher; this.cpdExecutor = cpdExecutor; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java index c4b1805d3ba..88158bfb065 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java @@ -27,6 +27,7 @@ import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.resources.Project; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; @@ -40,12 +41,12 @@ public class SensorsExecutor { private final SensorStrategy strategy; private final boolean isRoot; - public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus, SensorStrategy strategy) { + public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, InputModuleHierarchy hierarchy, EventBus eventBus, SensorStrategy strategy) { this.selector = selector; this.module = module; this.eventBus = eventBus; this.strategy = strategy; - this.isRoot = module.definition().getParent() == null; + this.isRoot = hierarchy.isRoot(module); } public void execute(SensorContext context) { @@ -84,7 +85,7 @@ public class SensorsExecutor { private void executeSensor(SensorContext context, Sensor sensor) { eventBus.fireEvent(new SensorExecutionEvent(sensor, true)); - sensor.analyse(new Project(module.definition()), context); + sensor.analyse(new Project(module), context); eventBus.fireEvent(new SensorExecutionEvent(sensor, false)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java index 4da1aeda04e..0ff003378b4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java @@ -29,10 +29,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.TreeSet; + import org.sonar.api.CoreProperties; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -56,16 +58,18 @@ public class AnalysisContextReportPublisher { private final System2 system; private final ProjectRepositories projectRepos; private final GlobalConfiguration globalSettings; + private final InputModuleHierarchy hierarchy; private ScannerReportWriter writer; public AnalysisContextReportPublisher(AnalysisMode mode, ScannerPluginRepository pluginRepo, System2 system, - ProjectRepositories projectRepos, GlobalConfiguration globalSettings) { + ProjectRepositories projectRepos, GlobalConfiguration globalSettings, InputModuleHierarchy hierarchy) { this.mode = mode; this.pluginRepo = pluginRepo; this.system = system; this.projectRepos = projectRepos; this.globalSettings = globalSettings; + this.hierarchy = hierarchy; } public void init(ScannerReportWriter writer) { @@ -120,15 +124,15 @@ public class AnalysisContextReportPublisher { } } - public void dumpModuleSettings(ProjectDefinition moduleDefinition) { + public void dumpModuleSettings(DefaultInputModule module) { if (mode.isIssues()) { return; } File analysisLog = writer.getFileStructure().analysisLog(); try (BufferedWriter fileWriter = Files.newBufferedWriter(analysisLog.toPath(), StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { - Map<String, String> moduleSpecificProps = collectModuleSpecificProps(moduleDefinition); - fileWriter.append(String.format("Settings for module: %s", moduleDefinition.getKey())).append('\n'); + Map<String, String> moduleSpecificProps = collectModuleSpecificProps(module); + fileWriter.append(String.format("Settings for module: %s", module.key())).append('\n'); for (String prop : new TreeSet<>(moduleSpecificProps.keySet())) { if (isSystemProp(prop) || isEnvVariable(prop) || !isSqProp(prop)) { continue; @@ -147,17 +151,17 @@ public class AnalysisContextReportPublisher { /** * Only keep props that are not in parent */ - private Map<String, String> collectModuleSpecificProps(ProjectDefinition moduleDefinition) { + private Map<String, String> collectModuleSpecificProps(DefaultInputModule module) { Map<String, String> moduleSpecificProps = new HashMap<>(); - if (projectRepos.moduleExists(moduleDefinition.getKeyWithBranch())) { - moduleSpecificProps.putAll(projectRepos.settings(moduleDefinition.getKeyWithBranch())); + if (projectRepos.moduleExists(module.getKeyWithBranch())) { + moduleSpecificProps.putAll(projectRepos.settings(module.getKeyWithBranch())); } - ProjectDefinition parent = moduleDefinition.getParent(); + DefaultInputModule parent = hierarchy.parent(module); if (parent == null) { - moduleSpecificProps.putAll(moduleDefinition.properties()); + moduleSpecificProps.putAll(module.properties()); } else { Map<String, String> parentProps = parent.properties(); - for (Map.Entry<String, String> entry : moduleDefinition.properties().entrySet()) { + for (Map.Entry<String, String> entry : module.properties().entrySet()) { if (!parentProps.containsKey(entry.getKey()) || !parentProps.get(entry.getKey()).equals(entry.getValue())) { moduleSpecificProps.put(entry.getKey(), entry.getValue()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index 78fa6bea16c..395b6e4b63b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -22,6 +22,7 @@ package org.sonar.scanner.report; import java.util.Collection; import java.util.stream.Collectors; import javax.annotation.CheckForNull; + import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index 9afa52f2ca8..871f65ca0f0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -25,7 +25,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.config.Configuration; import org.sonar.scanner.ProjectAnalysisInfo; -import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.rule.ModuleQProfiles; @@ -37,12 +37,15 @@ public class MetadataPublisher implements ReportPublisherStep { private final ModuleQProfiles qProfiles; private final ProjectAnalysisInfo projectAnalysisInfo; private final InputModuleHierarchy moduleHierarchy; + private final CpdSettings cpdSettings; - public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings, ModuleQProfiles qProfiles) { + public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings, + ModuleQProfiles qProfiles, CpdSettings cpdSettings) { this.projectAnalysisInfo = projectAnalysisInfo; this.moduleHierarchy = moduleHierarchy; this.settings = settings; this.qProfiles = qProfiles; + this.cpdSettings = cpdSettings; } @Override @@ -53,7 +56,7 @@ public class MetadataPublisher implements ReportPublisherStep { .setAnalysisDate(projectAnalysisInfo.analysisDate().getTime()) // Here we want key without branch .setProjectKey(rootDef.getKey()) - .setCrossProjectDuplicationActivated(SonarCpdBlockIndex.isCrossProjectDuplicationEnabled(settings)) + .setCrossProjectDuplicationActivated(cpdSettings.isCrossProjectDuplicationEnabled()) .setRootComponentRef(rootProject.batchId()); settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(builder::setOrganizationKey); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index 487c48cd4e5..54fd94ca923 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -19,8 +19,8 @@ */ package org.sonar.scanner.report; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; +import static org.sonar.core.util.FileUtils.deleteQuietly; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -31,13 +31,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Map; + import javax.annotation.Nullable; -import okhttp3.HttpUrl; + import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.utils.MessageException; @@ -48,14 +49,16 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.bootstrap.ScannerWsClient; import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.WsCe; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.WsResponse; -import static org.sonar.core.util.FileUtils.deleteQuietly; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; + +import okhttp3.HttpUrl; @ScannerSide public class ReportPublisher implements Startable { @@ -69,7 +72,7 @@ public class ReportPublisher implements Startable { private final Configuration settings; private final ScannerWsClient wsClient; private final AnalysisContextReportPublisher contextPublisher; - private final ImmutableProjectReactor projectReactor; + private final InputModuleHierarchy moduleHierarchy; private final DefaultAnalysisMode analysisMode; private final TempFolder temp; private final ReportPublisherStep[] publishers; @@ -79,12 +82,12 @@ public class ReportPublisher implements Startable { private ScannerReportWriter writer; public ReportPublisher(Configuration settings, ScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher, - ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { + InputModuleHierarchy moduleHierarchy, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { this.settings = settings; this.wsClient = wsClient; this.server = server; this.contextPublisher = contextPublisher; - this.projectReactor = projectReactor; + this.moduleHierarchy = moduleHierarchy; this.analysisMode = analysisMode; this.temp = temp; this.publishers = publishers; @@ -92,7 +95,7 @@ public class ReportPublisher implements Startable { @Override public void start() { - reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report"); + reportDir = new File(moduleHierarchy.root().getWorkDir(), "batch-report"); writer = new ScannerReportWriter(reportDir); contextPublisher.init(writer); @@ -165,14 +168,13 @@ public class ReportPublisher implements Startable { String upload(File report) { LOG.debug("Upload report"); long startTime = System.currentTimeMillis(); - ProjectDefinition projectDefinition = projectReactor.getRoot(); PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report); PostRequest post = new PostRequest("api/ce/submit") .setMediaType(MediaTypes.PROTOBUF) .setParam("organization", settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).orElse(null)) - .setParam("projectKey", projectDefinition.getKey()) - .setParam("projectName", projectDefinition.getOriginalName()) - .setParam("projectBranch", projectDefinition.getBranch()) + .setParam("projectKey", moduleHierarchy.root().key()) + .setParam("projectName", moduleHierarchy.root().getOriginalName()) + .setParam("projectBranch", moduleHierarchy.root().getBranch()) .setPart("report", filePart); WsResponse response; @@ -201,7 +203,7 @@ public class ReportPublisher implements Startable { HttpUrl httpUrl = HttpUrl.parse(publicUrl); Map<String, String> metadata = new LinkedHashMap<>(); - String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); + String effectiveKey = moduleHierarchy.root().getKeyWithBranch(); settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(org -> metadata.put("organization", org)); metadata.put("projectKey", effectiveKey); metadata.put("serverUrl", publicUrl); @@ -230,7 +232,7 @@ public class ReportPublisher implements Startable { } private void dumpMetadata(Map<String, String> metadata) { - Path file = projectReactor.getRoot().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME); + Path file = moduleHierarchy.root().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME); try (Writer output = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { for (Map.Entry<String, String> entry : metadata.entrySet()) { output.write(entry.getKey()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java index c6c0015a88e..4c36c0991ad 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java @@ -19,11 +19,12 @@ */ package org.sonar.scanner.repository; +import static com.google.common.base.Preconditions.checkArgument; + import java.util.HashMap; import java.util.Map; -import org.sonar.api.batch.ScannerSide; -import static com.google.common.base.Preconditions.checkArgument; +import org.sonar.api.batch.ScannerSide; @ScannerSide public class ContextPropertiesCache { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java index 6c9921c53a2..ead8f33b909 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java @@ -19,30 +19,32 @@ */ package org.sonar.scanner.repository; -import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableTable; import com.google.common.collect.Table; import java.util.Date; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +@Immutable public class ProjectRepositories { - private final Table<String, String, String> settingsByModule; - private final Table<String, String, FileData> fileDataByModuleAndPath; + private final ImmutableTable<String, String, String> settingsByModule; + private final ImmutableTable<String, String, FileData> fileDataByModuleAndPath; private final Date lastAnalysisDate; private final boolean exists; public ProjectRepositories() { this.exists = false; - this.settingsByModule = HashBasedTable.create(); - this.fileDataByModuleAndPath = HashBasedTable.create(); + this.settingsByModule = new ImmutableTable.Builder<String, String, String>().build(); + this.fileDataByModuleAndPath = new ImmutableTable.Builder<String, String, FileData>().build(); this.lastAnalysisDate = null; } public ProjectRepositories(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath, @Nullable Date lastAnalysisDate) { - this.settingsByModule = settingsByModule; - this.fileDataByModuleAndPath = fileDataByModuleAndPath; + this.settingsByModule = ImmutableTable.copyOf(settingsByModule); + this.fileDataByModuleAndPath = ImmutableTable.copyOf(fileDataByModuleAndPath); this.lastAnalysisDate = lastAnalysisDate; this.exists = true; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java index 4af9e6a6e74..8866237e54c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java @@ -21,7 +21,10 @@ package org.sonar.scanner.repository.language; import java.util.ArrayList; import java.util.Collection; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.picocontainer.Startable; import org.sonar.api.resources.Languages; @@ -29,6 +32,7 @@ import org.sonar.api.resources.Languages; * Languages repository using {@link Languages} * @since 4.4 */ +@Immutable public class DefaultLanguagesRepository implements LanguagesRepository, Startable { private Languages languages; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java index 8b90c65f57e..775c895bfdb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java @@ -22,6 +22,9 @@ package org.sonar.scanner.repository.language; import java.util.Arrays; import java.util.Collection; +import javax.annotation.concurrent.Immutable; + +@Immutable public final class Language { private final String key; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java index 626a3361f8f..d6f5046ea66 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java @@ -20,7 +20,10 @@ package org.sonar.scanner.repository.language; import java.util.Collection; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.ScannerSide; /** @@ -28,6 +31,7 @@ import org.sonar.api.batch.ScannerSide; * @since 4.4 */ @ScannerSide +@Immutable public interface LanguagesRepository { /** diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java index 013e22e352e..30d87568514 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java @@ -19,19 +19,24 @@ */ package org.sonar.scanner.rule; +import org.sonar.api.utils.DateUtils; + +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; +import org.sonar.api.batch.ScannerSide; + +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.utils.DateUtils; -import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; /** * Lists the Quality profiles enabled on the current module. */ @ScannerSide +@Immutable public class ModuleQProfiles { public static final String SONAR_PROFILE_PROP = "sonar.profile"; @@ -42,11 +47,11 @@ public class ModuleQProfiles { for (QualityProfile qProfile : profiles) { map.put(qProfile.getLanguage(), - new QProfile() + new QProfile.Builder() .setKey(qProfile.getKey()) .setName(qProfile.getName()) .setLanguage(qProfile.getLanguage()) - .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt()))); + .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt())).build()); } byLanguage = Collections.unmodifiableMap(map); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java index a38f942b929..9568368b992 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java @@ -22,49 +22,38 @@ package org.sonar.scanner.rule; import com.google.common.base.MoreObjects; import java.util.Date; +import javax.annotation.concurrent.Immutable; + +@Immutable public class QProfile { + private final String key; + private final String name; + private final String language; + private final Date rulesUpdatedAt; - private String key; - private String name; - private String language; - private Date rulesUpdatedAt; + public QProfile(String key, String name, String language, Date rulesUpdatedAt) { + this.key = key; + this.name = name; + this.language = language; + this.rulesUpdatedAt = rulesUpdatedAt; + } public String getKey() { return key; } - public QProfile setKey(String key) { - this.key = key; - return this; - } - public String getName() { return name; } - public QProfile setName(String name) { - this.name = name; - return this; - } - public String getLanguage() { return language; } - public QProfile setLanguage(String language) { - this.language = language; - return this; - } - public Date getRulesUpdatedAt() { return rulesUpdatedAt; } - public QProfile setRulesUpdatedAt(Date d) { - this.rulesUpdatedAt = d; - return this; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -92,4 +81,51 @@ public class QProfile { .add("rulesUpdatedAt", rulesUpdatedAt) .toString(); } + + public static class Builder { + private String key; + private String name; + private String language; + private Date rulesUpdatedAt; + + public String getKey() { + return key; + } + + public Builder setKey(String key) { + this.key = key; + return this; + } + + public String getName() { + return name; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public String getLanguage() { + return language; + } + + public Builder setLanguage(String language) { + this.language = language; + return this; + } + + public Date getRulesUpdatedAt() { + return rulesUpdatedAt; + } + + public Builder setRulesUpdatedAt(Date d) { + this.rulesUpdatedAt = d; + return this; + } + + public QProfile build() { + return new QProfile(key, name, language, rulesUpdatedAt); + } + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java index bd3d822bdec..2acf1e4e8f4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java @@ -25,6 +25,8 @@ import java.util.Collections; import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.batch.rule.Rules; @@ -33,6 +35,7 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleQuery; +@Immutable public class RuleFinderCompatibility implements RuleFinder { private final Rules rules; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java index ae7f1de06ec..4cf6dcbcab8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java @@ -19,35 +19,68 @@ */ package org.sonar.scanner.scan; -import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import java.nio.file.Path; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; + import javax.annotation.CheckForNull; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.scan.filesystem.PathResolver; +import com.google.common.collect.ImmutableMultimap; + +@Immutable public class DefaultInputModuleHierarchy implements InputModuleHierarchy { private final PathResolver pathResolver = new PathResolver(); - private DefaultInputModule root; - private final Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>(); - private final Multimap<DefaultInputModule, DefaultInputModule> children = HashMultimap.create(); + private final DefaultInputModule root; + private final Map<DefaultInputModule, DefaultInputModule> parents; + private final ImmutableMultimap<DefaultInputModule, DefaultInputModule> children; - public void setRoot(DefaultInputModule root) { + public DefaultInputModuleHierarchy(DefaultInputModule parent, DefaultInputModule child) { + this(Collections.singletonMap(child, parent)); + } + + public DefaultInputModuleHierarchy(DefaultInputModule root) { + this.children = new ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule>().build(); + this.parents = Collections.emptyMap(); this.root = root; } - public void index(DefaultInputModule child, DefaultInputModule parent) { - Preconditions.checkNotNull(child); - Preconditions.checkNotNull(parent); - parents.put(child, parent); - children.put(parent, child); + /** + * Map of child->parent. Neither the Keys or values can be null. + */ + public DefaultInputModuleHierarchy(Map<DefaultInputModule, DefaultInputModule> parents) { + ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule> childrenBuilder = new ImmutableMultimap.Builder<>(); + + for (Map.Entry<DefaultInputModule, DefaultInputModule> e : parents.entrySet()) { + childrenBuilder.put(e.getValue(), e.getKey()); + } + + this.children = childrenBuilder.build(); + this.parents = Collections.unmodifiableMap(new HashMap<>(parents)); + this.root = findRoot(parents); + } + + private static DefaultInputModule findRoot(Map<DefaultInputModule, DefaultInputModule> parents) { + DefaultInputModule r = null; + for (DefaultInputModule parent : parents.values()) { + if (!parents.containsKey(parent)) { + if (r != null && r != parent) { + throw new IllegalStateException(String.format("Found two modules without parent: '%s' and '%s'", r.key(), parent.key())); + } + r = parent; + } + } + if (r == null) { + throw new IllegalStateException("Found no root module"); + } + return r; } @Override @@ -78,11 +111,8 @@ public class DefaultInputModuleHierarchy implements InputModuleHierarchy { return null; } DefaultInputModule inputModule = (DefaultInputModule) module; - - ProjectDefinition parentDefinition = parent.definition(); - Path parentBaseDir = parentDefinition.getBaseDir().toPath(); - ProjectDefinition moduleDefinition = inputModule.definition(); - Path moduleBaseDir = moduleDefinition.getBaseDir().toPath(); + Path parentBaseDir = parent.getBaseDir().toPath(); + Path moduleBaseDir = inputModule.getBaseDir().toPath(); return pathResolver.relativePath(parentBaseDir, moduleBaseDir); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java deleted file mode 100644 index 6d5df79b123..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.scan; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; - -/** - * Immutable copy of project reactor after all modifications have been applied (see {@link ImmutableProjectReactorProvider}). - */ -@ScannerSide -public class ImmutableProjectReactor { - - private ProjectDefinition root; - private Map<String, ProjectDefinition> byKey = new LinkedHashMap<>(); - - public ImmutableProjectReactor(ProjectDefinition root) { - if (root.getParent() != null) { - throw new IllegalArgumentException("Not a root project: " + root); - } - this.root = root; - collectProjects(root); - } - - public Collection<ProjectDefinition> getProjects() { - return byKey.values(); - } - - /** - * Populates list of projects from hierarchy. - */ - private void collectProjects(ProjectDefinition def) { - if (byKey.containsKey(def.getKeyWithBranch())) { - throw new IllegalStateException("Duplicate module key in reactor: " + def.getKeyWithBranch()); - } - byKey.put(def.getKeyWithBranch(), def); - for (ProjectDefinition child : def.getSubProjects()) { - collectProjects(child); - } - } - - public ProjectDefinition getRoot() { - return root; - } - - @CheckForNull - public ProjectDefinition getProjectDefinition(String keyWithBranch) { - return byKey.get(keyWithBranch); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java new file mode 100644 index 00000000000..aaa8cf5f54d --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan; + +import java.util.HashMap; +import java.util.Map; + +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.scan.filesystem.BatchIdGenerator; + +public class InputModuleHierarchyProvider extends ProviderAdapter { + + private DefaultInputModuleHierarchy hierarchy = null; + + public DefaultInputModuleHierarchy provide(ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator, + ProjectReactor projectReactor, BatchIdGenerator batchIdGenerator) { + if (hierarchy == null) { + // 1 Apply project builders + projectBuildersExecutor.execute(projectReactor); + + // 2 Validate final reactor + validator.validate(projectReactor); + + // 3 Create modules and the hierarchy + DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get()); + Map<DefaultInputModule, DefaultInputModule> parents = createChildren(root, batchIdGenerator, new HashMap<>()); + if (parents.isEmpty()) { + hierarchy = new DefaultInputModuleHierarchy(root); + } else { + hierarchy = new DefaultInputModuleHierarchy(parents); + } + } + return hierarchy; + } + + private static Map<DefaultInputModule, DefaultInputModule> createChildren(DefaultInputModule parent, BatchIdGenerator batchIdGenerator, + Map<DefaultInputModule, DefaultInputModule> parents) { + for (ProjectDefinition def : parent.definition().getSubProjects()) { + DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get()); + parents.put(child, parent); + createChildren(child, batchIdGenerator, parents); + } + return parents; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java index 5558d577ade..110baeb2287 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java @@ -20,9 +20,8 @@ package org.sonar.scanner.scan; import org.picocontainer.Startable; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; -import org.sonar.scanner.scan.filesystem.BatchIdGenerator; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.scan.filesystem.InputComponentStore; /** @@ -30,36 +29,27 @@ import org.sonar.scanner.scan.filesystem.InputComponentStore; * project definitions provided by the {@link ImmutableProjectReactor}. */ public class ModuleIndexer implements Startable { - private final ImmutableProjectReactor projectReactor; private final DefaultComponentTree componentTree; - private final DefaultInputModuleHierarchy moduleHierarchy; - private final BatchIdGenerator batchIdGenerator; + private final InputModuleHierarchy moduleHierarchy; private final InputComponentStore componentStore; - public ModuleIndexer(ImmutableProjectReactor projectReactor, DefaultComponentTree componentTree, - InputComponentStore componentStore, BatchIdGenerator batchIdGenerator, DefaultInputModuleHierarchy moduleHierarchy) { - this.projectReactor = projectReactor; + public ModuleIndexer(DefaultComponentTree componentTree, InputComponentStore componentStore, InputModuleHierarchy moduleHierarchy) { this.componentTree = componentTree; this.componentStore = componentStore; this.moduleHierarchy = moduleHierarchy; - this.batchIdGenerator = batchIdGenerator; } @Override public void start() { - DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get()); - moduleHierarchy.setRoot(root); - componentStore.put(root); - createChildren(root); + DefaultInputModule root = moduleHierarchy.root(); + indexChildren(root); } - private void createChildren(DefaultInputModule parent) { - for (ProjectDefinition def : parent.definition().getSubProjects()) { - DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get()); - moduleHierarchy.index(child, parent); - componentTree.index(child, parent); - componentStore.put(child); - createChildren(child); + private void indexChildren(DefaultInputModule parent) { + for (DefaultInputModule module : moduleHierarchy.children(parent)) { + componentTree.index(module, parent); + componentStore.put(module); + indexChildren(module); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index 8305472e8aa..dbb3862d831 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -95,7 +95,7 @@ public class ModuleScanContainer extends ComponentContainer { add( module.definition(), // still injected by some plugins - new Project(module.definition()), + new Project(module), module, MutableModuleSettings.class, new ModuleSettingsProvider()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java index d17a9c0916f..25db68aad37 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java @@ -27,6 +27,7 @@ import java.util.Map; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.scanner.bootstrap.GlobalConfiguration; import org.sonar.scanner.report.AnalysisContextReportPublisher; import org.sonar.scanner.repository.ProjectRepositories; @@ -35,15 +36,15 @@ public class ModuleSettingsProvider extends ProviderAdapter { private ModuleSettings projectSettings; - public ModuleSettings provide(GlobalConfiguration globalSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectRepos, + public ModuleSettings provide(GlobalConfiguration globalSettings, DefaultInputModule module, ProjectRepositories projectRepos, AnalysisMode analysisMode, AnalysisContextReportPublisher contextReportPublisher) { if (projectSettings == null) { Map<String, String> settings = new LinkedHashMap<>(); settings.putAll(globalSettings.getProperties()); - settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, moduleDefinition)); - addScannerSideProperties(settings, moduleDefinition); - contextReportPublisher.dumpModuleSettings(moduleDefinition); + settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, module.definition())); + addScannerSideProperties(settings, module.definition()); + contextReportPublisher.dumpModuleSettings(module); projectSettings = new ModuleSettings(globalSettings.getDefinitions(), globalSettings.getEncryption(), analysisMode, settings); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java index bddf206cc59..d8af158590d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java @@ -23,16 +23,17 @@ import java.io.IOException; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.nio.file.Path; + import org.picocontainer.Startable; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.home.cache.DirectoryLock; import org.sonar.scanner.bootstrap.Slf4jLogger; public class ProjectLock implements Startable { private final DirectoryLock lock; - public ProjectLock(ProjectReactor projectReactor) { - Path directory = projectReactor.getRoot().getWorkDir().toPath(); + public ProjectLock(InputModuleHierarchy moduleHierarchy) { + Path directory = moduleHierarchy.root().getWorkDir().toPath(); try { if (!directory.toFile().exists()) { Files.createDirectories(directory); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java index f5f9d0886e7..e69eb31916e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java @@ -19,30 +19,28 @@ */ package org.sonar.scanner.scan; -import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.List; + import javax.annotation.Nullable; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.analysis.DefaultAnalysisMode; +import com.google.common.base.Joiner; + /** * This class aims at validating project reactor * @since 3.6 */ public class ProjectReactorValidator { - - private static final String SONAR_PHASE = "sonar.phase"; - private final Settings settings; private final DefaultAnalysisMode mode; - public ProjectReactorValidator(Settings settings, DefaultAnalysisMode mode) { - this.settings = settings; + public ProjectReactorValidator(DefaultAnalysisMode mode) { this.mode = mode; } @@ -50,7 +48,6 @@ public class ProjectReactorValidator { String branch = reactor.getRoot().getBranch(); List<String> validationMessages = new ArrayList<>(); - checkDeprecatedProperties(validationMessages); for (ProjectDefinition moduleDef : reactor.getProjects()) { if (mode.isIssues()) { @@ -81,12 +78,6 @@ public class ProjectReactorValidator { } } - private void checkDeprecatedProperties(List<String> validationMessages) { - if (settings.getString(SONAR_PHASE) != null) { - validationMessages.add(String.format("Property \"%s\" is deprecated. Please remove it from your configuration.", SONAR_PHASE)); - } - } - private static void validateBranch(List<String> validationMessages, @Nullable String branch) { if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) { validationMessages.add(String.format("\"%s\" is not a valid branch name. " diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index 9720666b113..458239cc526 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -19,7 +19,6 @@ */ package org.sonar.scanner.scan; -import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; @@ -42,6 +41,7 @@ import org.sonar.scanner.bootstrap.ExtensionMatcher; import org.sonar.scanner.bootstrap.ExtensionUtils; import org.sonar.scanner.bootstrap.MetricProvider; import org.sonar.scanner.cpd.CpdExecutor; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; import org.sonar.scanner.deprecated.test.TestPlanBuilder; import org.sonar.scanner.deprecated.test.TestableBuilder; @@ -86,12 +86,14 @@ import org.sonar.scanner.rule.DefaultRulesLoader; import org.sonar.scanner.rule.RulesLoader; import org.sonar.scanner.rule.RulesProvider; import org.sonar.scanner.scan.filesystem.BatchIdGenerator; -import org.sonar.scanner.scan.filesystem.InputComponentStore; +import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider; import org.sonar.scanner.scan.measure.DefaultMetricFinder; import org.sonar.scanner.scan.measure.DeprecatedMetricFinder; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.storage.Storages; +import com.google.common.annotations.VisibleForTesting; + public class ProjectScanContainer extends ComponentContainer { private static final Logger LOG = Loggers.get(ProjectScanContainer.class); @@ -106,10 +108,10 @@ public class ProjectScanContainer extends ComponentContainer { @Override protected void doBeforeStart() { addBatchComponents(); + addBatchExtensions(); ProjectLock lock = getComponentByType(ProjectLock.class); lock.tryLock(); getComponentByType(WorkDirectoryCleaner.class).execute(); - addBatchExtensions(); Settings settings = getComponentByType(Settings.class); if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) { add(PhasesSumUpTimeProfiler.class); @@ -126,7 +128,6 @@ public class ProjectScanContainer extends ComponentContainer { ProjectReactorBuilder.class, WorkDirectoryCleaner.class, new MutableProjectReactorProvider(), - new ImmutableProjectReactorProvider(), ProjectBuildersExecutor.class, ProjectLock.class, EventBus.class, @@ -145,9 +146,9 @@ public class ProjectScanContainer extends ComponentContainer { // file system ModuleIndexer.class, - InputComponentStore.class, + new InputComponentStoreProvider(), PathResolver.class, - DefaultInputModuleHierarchy.class, + new InputModuleHierarchyProvider(), DefaultComponentTree.class, BatchIdGenerator.class, @@ -197,6 +198,7 @@ public class ProjectScanContainer extends ComponentContainer { // Cpd CpdExecutor.class, + CpdSettings.class, SonarCpdBlockIndex.class, ScanTaskObservers.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java index 607b7bb0397..277b230fd88 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java @@ -24,15 +24,16 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; -import org.sonar.api.batch.bootstrap.ProjectReactor; + +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.core.util.FileUtils; import org.sonar.home.cache.DirectoryLock; public class WorkDirectoryCleaner { - private Path workDir; + private final Path workDir; - public WorkDirectoryCleaner(ProjectReactor projectReactor) { - workDir = projectReactor.getRoot().getWorkDir().toPath(); + public WorkDirectoryCleaner(InputModuleHierarchy moduleHierarchy) { + workDir = moduleHierarchy.root().getWorkDir().toPath(); } public void execute() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java index 087f8445ad3..119d8d9bc9f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java @@ -21,6 +21,9 @@ package org.sonar.scanner.scan.filesystem; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.fs.InputComponent; /** @@ -28,6 +31,7 @@ import org.sonar.api.batch.fs.InputComponent; * The IDs must be unique among all types of components and for all modules in the project. * The ID should never be 0, as it is sometimes used to indicate invalid components. */ +@ThreadSafe public class BatchIdGenerator implements Supplier<Integer> { private AtomicInteger nextBatchId = new AtomicInteger(1); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java index 0bebac55636..f84bec040cf 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java @@ -84,7 +84,7 @@ public class ExclusionFilters { if (inclusionPatterns.length > 0) { boolean matchInclusion = false; for (PathPattern pattern : inclusionPatterns) { - matchInclusion |= pattern.match(indexedFile); + matchInclusion |= pattern.match(indexedFile.absolutePath(), indexedFile.relativePath()); } if (!matchInclusion) { return false; @@ -92,7 +92,7 @@ public class ExclusionFilters { } if (exclusionPatterns.length > 0) { for (PathPattern pattern : exclusionPatterns) { - if (pattern.match(indexedFile)) { + if (pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())) { return false; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java index dd8a32e91e5..3af3741e987 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java @@ -157,9 +157,10 @@ public class FileIndexer { DefaultInputFile inputFile = inputFileBuilder.create(realFile, type, fileSystem.encoding()); if (inputFile != null) { if (exclusionFilters.accept(inputFile, type) && accept(inputFile)) { + String parentRelativePath = getParentRelativePath(fileSystem, inputFile); synchronized (this) { fileSystem.add(inputFile); - indexParentDir(fileSystem, inputFile); + indexParentDir(fileSystem, inputFile, parentRelativePath); progress.markAsIndexed(inputFile); } LOG.debug("'{}' indexed {}with language '{}'", inputFile.relativePath(), type == Type.TEST ? "as test " : "", inputFile.language()); @@ -171,16 +172,19 @@ public class FileIndexer { return null; } - private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile) { + private static String getParentRelativePath(DefaultModuleFileSystem fileSystem, InputFile inputFile) { Path parentDir = inputFile.path().getParent(); String relativePath = new PathResolver().relativePath(fileSystem.baseDirPath(), parentDir); if (relativePath == null) { throw new IllegalStateException("Failed to compute relative path of file: " + inputFile); } + return relativePath; + } - DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), relativePath); + private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile, String parentRelativePath) { + DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), parentRelativePath); if (inputDir == null) { - inputDir = new DefaultInputDir(fileSystem.moduleKey(), relativePath, batchIdGenerator.get()); + inputDir = new DefaultInputDir(fileSystem.moduleKey(), parentRelativePath, batchIdGenerator.get()); inputDir.setModuleBaseDir(fileSystem.baseDirPath()); fileSystem.add(inputDir); componentTree.index(inputDir, module); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java index ff92762a405..6726cba970b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java @@ -58,14 +58,17 @@ public class InputComponentStore { private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create(); private final Map<String, InputDir> globalInputDirCache = new HashMap<>(); private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create(); + // indexed by key with branch private final Map<String, InputModule> inputModuleCache = new HashMap<>(); private final Map<String, InputComponent> inputComponents = new HashMap<>(); private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create(); private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create(); - private InputModule root; + private final InputModule root; - public InputComponentStore(PathResolver pathResolver) { + public InputComponentStore(PathResolver pathResolver, DefaultInputModule root) { this.pathResolver = pathResolver; + this.root = root; + this.put(root); } public Collection<InputComponent> all() { @@ -90,7 +93,6 @@ public class InputComponentStore { return inputComponents.get(key); } - @CheckForNull public InputModule root() { return root; } @@ -157,7 +159,7 @@ public class InputComponentStore { } private Path getProjectBaseDir() { - return ((DefaultInputModule) root).definition().getBaseDir().toPath(); + return ((DefaultInputModule) root).getBaseDir().toPath(); } @CheckForNull @@ -181,22 +183,18 @@ public class InputComponentStore { } @CheckForNull - public InputModule getModule(String moduleKey) { - return inputModuleCache.get(moduleKey); + public InputModule getModule(String moduleKeyWithBranch) { + return inputModuleCache.get(moduleKeyWithBranch); } public void put(DefaultInputModule inputModule) { String key = inputModule.key(); + String keyWithBranch = inputModule.getKeyWithBranch(); + Preconditions.checkNotNull(inputModule); Preconditions.checkState(!inputComponents.containsKey(key), "Module '%s' already indexed", key); - Preconditions.checkState(!inputModuleCache.containsKey(key), "Module '%s' already indexed", key); + Preconditions.checkState(!inputModuleCache.containsKey(keyWithBranch), "Module '%s' already indexed", keyWithBranch); inputComponents.put(key, inputModule); - inputModuleCache.put(key, inputModule); - if (inputModule.definition().getParent() == null) { - if (root != null) { - throw new IllegalStateException("Root module already indexed: '" + root.key() + "', '" + key + "'"); - } - root = inputModule; - } + inputModuleCache.put(keyWithBranch, inputModule); } public Iterable<InputFile> getFilesByName(String filename) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactorProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStoreProvider.java index d3111db3a46..65e78e623e6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactorProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStoreProvider.java @@ -17,25 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.scan; +package org.sonar.scanner.scan.filesystem; import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.scan.filesystem.PathResolver; -public class ImmutableProjectReactorProvider extends ProviderAdapter { +public class InputComponentStoreProvider extends ProviderAdapter { + private InputComponentStore store; - private ImmutableProjectReactor singleton; - - public ImmutableProjectReactor provide(ProjectReactor reactor, ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator) { - if (singleton == null) { - // 1 Apply project builders - projectBuildersExecutor.execute(reactor); - - // 2 Validate final reactor - validator.validate(reactor); - - singleton = new ImmutableProjectReactor(reactor.getRoot()); + public InputComponentStore provide(PathResolver pathResolver, InputModuleHierarchy hierarchy) { + if (store == null) { + store = new InputComponentStore(pathResolver, hierarchy.root()); } - return singleton; + return store; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java index c44e24f4942..fe8d125a5e9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java @@ -60,14 +60,13 @@ public class InputFileBuilder { LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", file.toAbsolutePath(), moduleBaseDir); return null; } - DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, idGenerator.get()); - String language = langDetection.language(indexedFile); + String language = langDetection.language(file.toAbsolutePath().normalize().toString(), relativePath); if (language == null && langDetection.forcedLanguage() != null) { LOG.warn("File '{}' is ignored because it doesn't belong to the forced language '{}'", file.toAbsolutePath(), langDetection.forcedLanguage()); return null; } - indexedFile.setLanguage(language); + DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, idGenerator.get()); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(f, defaultEncoding)); if (language != null) { inputFile.setPublish(true); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java index 828a8403655..8267e4509c9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java @@ -19,29 +19,34 @@ */ package org.sonar.scanner.scan.filesystem; -import com.google.common.base.Joiner; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.batch.fs.internal.PathPattern; import org.sonar.api.config.Configuration; import org.sonar.api.utils.MessageException; import org.sonar.scanner.repository.language.Language; import org.sonar.scanner.repository.language.LanguagesRepository; +import com.google.common.base.Joiner; + /** * Detect language of a source file based on its suffix and configured patterns. */ @ScannerSide +@ThreadSafe public class LanguageDetection { private static final Logger LOG = LoggerFactory.getLogger(LanguageDetection.class); @@ -49,16 +54,17 @@ public class LanguageDetection { /** * Lower-case extension -> languages */ - private final Map<String, PathPattern[]> patternsByLanguage = new LinkedHashMap<>(); - private final List<String> languagesToConsider = new ArrayList<>(); + private final Map<String, PathPattern[]> patternsByLanguage; + private final List<String> languagesToConsider; private final String forcedLanguage; public LanguageDetection(Configuration settings, LanguagesRepository languages) { + Map<String, PathPattern[]> patternsByLanguageBuilder = new LinkedHashMap<>(); for (Language language : languages.all()) { String[] filePatterns = settings.getStringArray(getFileLangPatternPropKey(language.key())); PathPattern[] pathPatterns = PathPattern.create(filePatterns); if (pathPatterns.length > 0) { - patternsByLanguage.put(language.key(), pathPatterns); + patternsByLanguageBuilder.put(language.key(), pathPatterns); } else { // If no custom language pattern is defined then fallback to suffixes declared by language String[] patterns = language.fileSuffixes().toArray(new String[language.fileSuffixes().size()]); @@ -68,22 +74,24 @@ public class LanguageDetection { patterns[i] = new StringBuilder().append("**/*.").append(extension).toString(); } PathPattern[] defaultLanguagePatterns = PathPattern.create(patterns); - patternsByLanguage.put(language.key(), defaultLanguagePatterns); - LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key())); + patternsByLanguageBuilder.put(language.key(), defaultLanguagePatterns); + LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key(), defaultLanguagePatterns)); } } forcedLanguage = StringUtils.defaultIfBlank(settings.get(CoreProperties.PROJECT_LANGUAGE_PROPERTY).orElse(null), null); // First try with lang patterns if (forcedLanguage != null) { - if (!patternsByLanguage.containsKey(forcedLanguage)) { + if (!patternsByLanguageBuilder.containsKey(forcedLanguage)) { throw MessageException.of("You must install a plugin that supports the language '" + forcedLanguage + "'"); } LOG.info("Language is forced to {}", forcedLanguage); - languagesToConsider.add(forcedLanguage); + languagesToConsider = Collections.singletonList(forcedLanguage); } else { - languagesToConsider.addAll(patternsByLanguage.keySet()); + languagesToConsider = Collections.unmodifiableList(new ArrayList<>(patternsByLanguageBuilder.keySet())); } + + patternsByLanguage = Collections.unmodifiableMap(patternsByLanguageBuilder); } public String forcedLanguage() { @@ -95,16 +103,16 @@ public class LanguageDetection { } @CheckForNull - String language(DefaultIndexedFile inputFile) { + String language(String absolutePath, String relativePath) { String detectedLanguage = null; for (String languageKey : languagesToConsider) { - if (isCandidateForLanguage(inputFile, languageKey)) { + if (isCandidateForLanguage(absolutePath, relativePath, languageKey)) { if (detectedLanguage == null) { detectedLanguage = languageKey; } else { // Language was already forced by another pattern throw MessageException.of(MessageFormat.format("Language of file ''{0}'' can not be decided as the file matches patterns of both {1} and {2}", - inputFile.relativePath(), getDetails(detectedLanguage), getDetails(languageKey))); + relativePath, getDetails(detectedLanguage), getDetails(languageKey))); } } } @@ -120,11 +128,11 @@ public class LanguageDetection { return null; } - private boolean isCandidateForLanguage(DefaultIndexedFile inputFile, String languageKey) { + private boolean isCandidateForLanguage(String absolutePath, String relativePath, String languageKey) { PathPattern[] patterns = patternsByLanguage.get(languageKey); if (patterns != null) { for (PathPattern pathPattern : patterns) { - if (pathPattern.match(inputFile, false)) { + if (pathPattern.match(absolutePath, relativePath, false)) { return true; } } @@ -137,7 +145,11 @@ public class LanguageDetection { } private String getDetails(String detectedLanguage) { - return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patternsByLanguage.get(detectedLanguage)); + return getDetails(detectedLanguage, patternsByLanguage.get(detectedLanguage)); + } + + private static String getDetails(String detectedLanguage, PathPattern[] patterns) { + return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patterns); } static String sanitizeExtension(String suffix) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java index 2c5c9e54e8d..f13b39b5325 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java @@ -22,6 +22,7 @@ package org.sonar.scanner.scan.filesystem; import java.io.File; import java.util.ArrayList; import java.util.List; + import org.apache.commons.io.FileUtils; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.bootstrap.ProjectDefinition; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java index 851332f663d..ac11c11d3b8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java @@ -19,11 +19,14 @@ */ package org.sonar.scanner.scan.filesystem; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.ProjectRepositories; +@Immutable class StatusDetection { private final ProjectRepositories projectSettings; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java index 00d484ef598..c6655b7d570 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java @@ -22,21 +22,28 @@ package org.sonar.scanner.scan.measure; import com.google.common.collect.Lists; import java.io.Serializable; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.scanner.repository.MetricsRepository; +@ThreadSafe public class DefaultMetricFinder implements MetricFinder { - private Map<String, Metric<Serializable>> metricsByKey = new LinkedHashMap<>(); + private Map<String, Metric<Serializable>> metricsByKey; public DefaultMetricFinder(MetricsRepository metricsRepository) { + Map<String, Metric<Serializable>> metrics = new LinkedHashMap<>(); for (org.sonar.api.measures.Metric metric : metricsRepository.metrics()) { - metricsByKey.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create()); + metrics.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create()); } + metricsByKey = Collections.unmodifiableMap(metrics); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java index eaaf9b00b0e..46531e99c40 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java @@ -20,6 +20,8 @@ package org.sonar.scanner.scan.report; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.StringEscapeUtils; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.rule.Rule; @@ -27,6 +29,7 @@ import org.sonar.api.batch.rule.Rules; import org.sonar.api.rule.RuleKey; @ScannerSide +@Immutable public class RuleNameProvider { private Rules rules; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java index 68c75b647a6..73f29557bc0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java @@ -19,9 +19,9 @@ */ package org.sonar.scanner.scm; -import com.google.common.base.Joiner; import java.util.LinkedHashMap; import java.util.Map; + import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; @@ -31,11 +31,13 @@ import org.sonar.api.PropertyType; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.scan.ImmutableProjectReactor; + +import com.google.common.base.Joiner; @Properties({ @Property( @@ -56,15 +58,15 @@ public final class ScmConfiguration implements Startable { public static final String FORCE_RELOAD_KEY = "sonar.scm.forceReloadAll"; - private final ImmutableProjectReactor projectReactor; private final Configuration settings; private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<>(); private final AnalysisMode analysisMode; + private final InputModuleHierarchy moduleHierarchy; private ScmProvider provider; - public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) { - this.projectReactor = projectReactor; + public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) { + this.moduleHierarchy = moduleHierarchy; this.analysisMode = analysisMode; this.settings = settings; for (ScmProvider scmProvider : providers) { @@ -72,8 +74,8 @@ public final class ScmConfiguration implements Startable { } } - public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings) { - this(projectReactor, analysisMode, settings, new ScmProvider[0]); + public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings) { + this(moduleHierarchy, analysisMode, settings, new ScmProvider[0]); } @Override @@ -121,7 +123,7 @@ public final class ScmConfiguration implements Startable { private void autodetection() { for (ScmProvider installedProvider : providerPerKey.values()) { - if (installedProvider.supports(projectReactor.getRoot().getBaseDir())) { + if (installedProvider.supports(moduleHierarchy.root().getBaseDir())) { if (this.provider == null) { this.provider = installedProvider; } else { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java index 9467d96829a..1889bbaab94 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java @@ -20,6 +20,9 @@ package org.sonar.scanner.sensor; import java.io.Serializable; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.SonarRuntime; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.FileSystem; @@ -50,6 +53,7 @@ import org.sonar.scanner.sensor.noop.NoOpNewCpdTokens; import org.sonar.scanner.sensor.noop.NoOpNewHighlighting; import org.sonar.scanner.sensor.noop.NoOpNewSymbolTable; +@ThreadSafe public class DefaultSensorContext implements SensorContext { private static final NoOpNewHighlighting NO_OP_NEW_HIGHLIGHTING = new NoOpNewHighlighting(); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index ac966c2a855..c6fc08c8ce4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -19,52 +19,6 @@ */ package org.sonar.scanner.sensor; -import com.google.common.annotations.VisibleForTesting; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.TextRange; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.measure.Metric; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; -import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; -import org.sonar.api.batch.sensor.error.AnalysisError; -import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; -import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.batch.sensor.issue.Issue; -import org.sonar.api.batch.sensor.measure.Measure; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; -import org.sonar.api.config.Configuration; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.metric.ScannerMetrics; -import org.sonar.duplications.block.Block; -import org.sonar.duplications.internal.pmd.PmdBlockChunker; -import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; -import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.issue.ModuleIssues; -import org.sonar.scanner.protocol.output.FileStructure; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.report.ReportPublisher; -import org.sonar.scanner.report.ScannerReportUtils; -import org.sonar.scanner.repository.ContextPropertiesCache; -import org.sonar.scanner.scan.measure.MeasureCache; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; - import static java.util.stream.Collectors.toList; import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY; @@ -112,6 +66,54 @@ import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.measure.Metric; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; +import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; +import org.sonar.api.batch.sensor.error.AnalysisError; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.measure.Measure; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; +import org.sonar.api.config.Configuration; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.metric.ScannerMetrics; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; +import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; +import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; +import org.sonar.scanner.issue.ModuleIssues; +import org.sonar.scanner.protocol.output.FileStructure; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.report.ReportPublisher; +import org.sonar.scanner.report.ScannerReportUtils; +import org.sonar.scanner.repository.ContextPropertiesCache; +import org.sonar.scanner.scan.measure.MeasureCache; +import org.sonar.scanner.sensor.coverage.CoverageExclusions; + +import com.google.common.annotations.VisibleForTesting; + public class DefaultSensorStorage implements SensorStorage { private static final Logger LOG = Loggers.get(DefaultSensorStorage.class); @@ -151,12 +153,10 @@ public class DefaultSensorStorage implements SensorStorage { private final Map<Metric<?>, Metric<?>> deprecatedCoverageMetricMapping = new HashMap<>(); private final Set<Metric<?>> coverageMetrics = new HashSet<>(); private final Set<Metric<?>> byLineMetrics = new HashSet<>(); - private Set<String> alreadyLogged = new HashSet<>(); + private final Set<String> alreadyLogged = new HashSet<>(); - public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, - Configuration settings, - CoverageExclusions coverageExclusions, ReportPublisher reportPublisher, - MeasureCache measureCache, SonarCpdBlockIndex index, + public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Configuration settings, CoverageExclusions coverageExclusions, + ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; @@ -210,10 +210,12 @@ public class DefaultSensorStorage implements SensorStorage { saveMeasure(newMeasure.inputComponent(), (DefaultMeasure<?>) newMeasure); } + /** + * Thread safe + */ private void logOnce(String metricKey, String msg, Object... params) { - if (!alreadyLogged.contains(metricKey)) { + if (alreadyLogged.add(metricKey)) { LOG.warn(msg, params); - alreadyLogged.add(metricKey); } } @@ -313,11 +315,11 @@ public class DefaultSensorStorage implements SensorStorage { } } - public boolean isDeprecatedMetric(String metricKey) { + public static boolean isDeprecatedMetric(String metricKey) { return DEPRECATED_METRICS_KEYS.contains(metricKey); } - public boolean isPlatformMetric(String metricKey) { + public static boolean isPlatformMetric(String metricKey) { return PLATFORM_METRICS_KEYS.contains(metricKey); } @@ -325,7 +327,7 @@ public class DefaultSensorStorage implements SensorStorage { return this.byLineMetrics.contains(metric); } - public void validateCoverageMeasure(String value, InputFile inputFile) { + public static void validateCoverageMeasure(String value, InputFile inputFile) { Map<Integer, Integer> m = KeyValueFormat.parseIntInt(value); validatePositiveLine(m, inputFile.absolutePath()); validateMaxLine(m, inputFile); @@ -349,6 +351,9 @@ public class DefaultSensorStorage implements SensorStorage { } } + /** + * Thread safe assuming that each issues for each file are only written once. + */ @Override public void store(Issue issue) { if (issue.primaryLocation().inputComponent() instanceof DefaultInputFile) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java index 490f067d764..5af99184414 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java @@ -19,11 +19,11 @@ */ package org.sonar.scanner.sensor.coverage; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; import java.util.Collection; import java.util.Iterator; + +import javax.annotation.concurrent.Immutable; + import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,20 +32,26 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; import org.sonar.api.utils.WildcardPattern; -public class CoverageExclusions implements Startable { +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +@Immutable +public class CoverageExclusions implements Startable { private static final Logger LOG = LoggerFactory.getLogger(CoverageExclusions.class); - private final Configuration settings; private Collection<WildcardPattern> exclusionPatterns; public CoverageExclusions(Configuration settings) { - this.settings = settings; + Builder<WildcardPattern> builder = ImmutableList.builder(); + for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) { + builder.add(WildcardPattern.create(pattern)); + } + exclusionPatterns = builder.build(); } @Override public void start() { - initPatterns(); + log("Excluded sources for coverage: ", exclusionPatterns); } @Override @@ -62,16 +68,6 @@ public class CoverageExclusions implements Startable { return found; } - @VisibleForTesting - final void initPatterns() { - Builder<WildcardPattern> builder = ImmutableList.builder(); - for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) { - builder.add(WildcardPattern.create(pattern)); - } - exclusionPatterns = builder.build(); - log("Excluded sources for coverage: ", exclusionPatterns); - } - private static void log(String title, Collection<WildcardPattern> patterns) { if (!patterns.isEmpty()) { LOG.info(title); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java index 8e2cb47094e..a5128b5baf3 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java @@ -19,12 +19,12 @@ */ package org.sonar.scanner.storage; -import com.google.common.collect.Sets; import com.persistit.Exchange; import com.persistit.Key; import com.persistit.KeyFilter; import com.persistit.exception.PersistitException; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.CheckForNull; @@ -242,7 +242,7 @@ public class Storage<V> { @SuppressWarnings("rawtypes") public Set keySet(Object key) { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(key); @@ -259,7 +259,7 @@ public class Storage<V> { @SuppressWarnings("rawtypes") public Set keySet(Object firstKey, Object secondKey) { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(firstKey); @@ -281,7 +281,7 @@ public class Storage<V> { */ public Set<Object> keySet() { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(Key.BEFORE); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisTempFolderProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisTempFolderProviderTest.java index 60a8f7140bb..ce458c8bb3a 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisTempFolderProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisTempFolderProviderTest.java @@ -19,42 +19,43 @@ */ package org.sonar.scanner.analysis; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.TempFolder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class AnalysisTempFolderProviderTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); private AnalysisTempFolderProvider tempFolderProvider; - private ProjectReactor projectReactor; + private InputModuleHierarchy moduleHierarchy; @Before public void setUp() { tempFolderProvider = new AnalysisTempFolderProvider(); - projectReactor = mock(ProjectReactor.class); - ProjectDefinition projectDefinition = mock(ProjectDefinition.class); - when(projectReactor.getRoot()).thenReturn(projectDefinition); - when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot()); + moduleHierarchy = mock(InputModuleHierarchy.class); + DefaultInputModule module = mock(DefaultInputModule.class); + when(moduleHierarchy.root()).thenReturn(module); + when(module.getWorkDir()).thenReturn(temp.getRoot()); } @Test public void createTempFolder() throws IOException { File defaultDir = new File(temp.getRoot(), AnalysisTempFolderProvider.TMP_NAME); - TempFolder tempFolder = tempFolderProvider.provide(projectReactor); + TempFolder tempFolder = tempFolderProvider.provide(moduleHierarchy); tempFolder.newDir(); tempFolder.newFile(); assertThat(defaultDir).exists(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java index e46702cb90e..33717366d07 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java @@ -19,12 +19,17 @@ */ package org.sonar.scanner.cpd; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,8 +37,8 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; -import org.sonar.api.config.internal.MapSettings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; @@ -50,10 +55,6 @@ import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.scan.filesystem.InputComponentStore; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class CpdExecutorTest { @Rule public LogTester logTester = new LogTester(); @@ -65,7 +66,7 @@ public class CpdExecutorTest { public ExpectedException thrown = ExpectedException.none(); private CpdExecutor executor; - private MapSettings settings; + private CpdSettings settings; private SonarCpdBlockIndex index; private ReportPublisher publisher; private ScannerReportReader reader; @@ -80,15 +81,15 @@ public class CpdExecutorTest { File outputDir = temp.newFolder(); baseDir = temp.newFolder(); - settings = new MapSettings(); + settings = mock(CpdSettings.class); publisher = mock(ReportPublisher.class); when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir)); - index = new SonarCpdBlockIndex(publisher, settings.asConfig()); - componentStore = new InputComponentStore(new PathResolver()); - executor = new CpdExecutor(settings.asConfig(), index, publisher, componentStore); - reader = new ScannerReportReader(outputDir); - componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", baseDir)); + index = new SonarCpdBlockIndex(publisher, settings); + DefaultInputModule inputModule = TestInputFileBuilder.newDefaultInputModule("foo", baseDir); + componentStore = new InputComponentStore(new PathResolver(), inputModule); + executor = new CpdExecutor(settings, index, publisher, componentStore); + reader = new ScannerReportReader(outputDir); batchComponent1 = createComponent("src/Foo.php", 5); batchComponent2 = createComponent("src/Foo2.php", 5); @@ -105,22 +106,6 @@ public class CpdExecutorTest { } @Test - public void defaultMinimumTokens() { - assertThat(executor.getMinimumTokens("java")).isEqualTo(100); - } - - @Test - public void minimumTokensByLanguage() { - settings.setProperty("sonar.cpd.java.minimumTokens", "42"); - settings.setProperty("sonar.cpd.php.minimumTokens", "33"); - assertThat(executor.getMinimumTokens("java")).isEqualTo(42); - - settings.setProperty("sonar.cpd.java.minimumTokens", "42"); - settings.setProperty("sonar.cpd.php.minimumTokens", "33"); - assertThat(executor.getMinimumTokens("php")).isEqualTo(33); - } - - @Test public void testNothingToSave() { executor.saveDuplications(batchComponent1, Collections.<CloneGroup>emptyList()); assertThat(reader.readComponentDuplications(batchComponent1.batchId())).hasSize(0); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdSettingsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdSettingsTest.java new file mode 100644 index 00000000000..c7774c750e9 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdSettingsTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.cpd; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.config.Configuration; + +public class CpdSettingsTest { + private CpdSettings cpdSettings; + private Configuration configuration; + private DefaultInputModule module; + + @Before + public void setUp() { + module = mock(DefaultInputModule.class); + InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class); + when(hierarchy.root()).thenReturn(module); + configuration = mock(Configuration.class); + cpdSettings = new CpdSettings(configuration, hierarchy); + } + + @Test + public void defaultMinimumTokens() { + when(configuration.getInt(anyString())).thenReturn(Optional.empty()); + assertThat(cpdSettings.getMinimumTokens("java")).isEqualTo(100); + } + + @Test + public void minimumTokensByLanguage() { + when(configuration.getInt("sonar.cpd.java.minimumTokens")).thenReturn(Optional.of(42)); + when(configuration.getInt("sonar.cpd.php.minimumTokens")).thenReturn(Optional.of(33)); + + assertThat(cpdSettings.getMinimumTokens("java")).isEqualTo(42); + assertThat(cpdSettings.getMinimumTokens("php")).isEqualTo(33); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java index d82bb5d20b1..d7d16341d8f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java @@ -19,6 +19,10 @@ */ package org.sonar.scanner.index; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.util.Collections; import org.junit.Before; @@ -44,10 +48,6 @@ import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.sensor.DefaultSensorStorage; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class DefaultIndexTest { @org.junit.Rule @@ -83,10 +83,10 @@ public class DefaultIndexTest { rootDef.addSubProject(moduleBDef); moduleBDef.addSubProject(moduleB1Def); - project = new Project(rootDef); - moduleA = new Project(moduleADef); - moduleB = new Project(moduleBDef); - moduleB1 = new Project(moduleB1Def); + project = new Project(new DefaultInputModule(rootDef)); + moduleA = new Project(new DefaultInputModule(moduleADef)); + moduleB = new Project(new DefaultInputModule(moduleBDef)); + moduleB1 = new Project(new DefaultInputModule(moduleB1Def)); RulesProfile rulesProfile = RulesProfile.create(); rule = Rule.create("repoKey", "ruleKey", "Rule"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java index a0aae458ed9..718f16ceb62 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java @@ -53,12 +53,11 @@ public class EnforceIssuesFilterTest { issue = mock(FilterableIssue.class); chain = mock(IssueFilterChain.class); when(chain.accept(issue)).thenReturn(true); - - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); } @Test public void shouldPassToChainIfNoConfiguredPatterns() { + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verify(chain).accept(issue); } @@ -76,6 +75,7 @@ public class EnforceIssuesFilterTest { when(rulePattern.match(rule)).thenReturn(false); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verify(chain).accept(issue); } @@ -100,6 +100,7 @@ public class EnforceIssuesFilterTest { when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path)); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verifyZeroInteractions(chain); } @@ -128,6 +129,7 @@ public class EnforceIssuesFilterTest { when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path)); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); assertThat(ignoreFilter.accept(issue, chain)).isFalse(); verifyZeroInteractions(chain); } @@ -152,6 +154,7 @@ public class EnforceIssuesFilterTest { when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); when(inputComponentStore.getByKey(componentKey)).thenReturn(null); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); assertThat(ignoreFilter.accept(issue, chain)).isFalse(); verifyZeroInteractions(chain); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/tracking/SourceHashHolderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/tracking/SourceHashHolderTest.java index 0cf540f0b7a..dde76bf6722 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/tracking/SourceHashHolderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/tracking/SourceHashHolderTest.java @@ -19,9 +19,17 @@ */ package org.sonar.scanner.issue.tracking; +import static org.apache.commons.codec.digest.DigestUtils.md5Hex; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Collections; + import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Rule; @@ -34,12 +42,6 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; -import static org.apache.commons.codec.digest.DigestUtils.md5Hex; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class SourceHashHolderTest { @Rule @@ -51,10 +53,11 @@ public class SourceHashHolderTest { DefaultInputFile file; private File ioFile; - private ProjectDefinition def = ProjectDefinition.create(); + private ProjectDefinition def; @Before public void setUp() throws Exception { + def = mock(ProjectDefinition.class); lastSnapshots = mock(ServerLineHashesLoader.class); file = mock(DefaultInputFile.class); ioFile = temp.newFile(); @@ -83,7 +86,7 @@ public class SourceHashHolderTest { public void should_lazy_load_reference_hashes_when_status_changed() throws Exception { final String source = "source"; FileUtils.write(ioFile, source, StandardCharsets.UTF_8); - def.setKey("foo"); + when(def.getKeyWithBranch()).thenReturn("foo"); when(file.relativePath()).thenReturn("src/Foo.java"); String key = "foo:src/Foo.java"; when(file.status()).thenReturn(InputFile.Status.CHANGED); @@ -100,8 +103,8 @@ public class SourceHashHolderTest { public void should_lazy_load_reference_hashes_when_status_changed_on_branch() throws Exception { final String source = "source"; FileUtils.write(ioFile, source, StandardCharsets.UTF_8); - def.setKey("foo"); - def.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); + when(def.getKeyWithBranch()).thenReturn("foo:myBranch"); + when(def.properties()).thenReturn(Collections.singletonMap(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch")); when(file.relativePath()).thenReturn("src/Foo.java"); String key = "foo:myBranch:src/Foo.java"; when(file.status()).thenReturn(InputFile.Status.CHANGED); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java index b2d4722cf1c..55a3234a522 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java @@ -670,6 +670,7 @@ public class FileSystemMediumTest { .newScanTask(new File(projectDir, "sonar-project.properties")) .start(); + System.out.println(logs.getAsString()); assertThat(result.inputFiles()).hasSize(4); assertThat(result.inputDirs()).hasSize(4); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/SensorsExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/SensorsExecutorTest.java index 5afcb9ee1f5..dcea19af285 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/SensorsExecutorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/SensorsExecutorTest.java @@ -19,27 +19,30 @@ */ package org.sonar.scanner.phases; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.util.Collections; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.resources.Project; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.sensor.SensorStrategy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class SensorsExecutorTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -83,12 +86,17 @@ public class SensorsExecutorTest { when(selector.selectSensors(any(DefaultInputModule.class), eq(false))).thenReturn(Collections.singleton(perModuleSensor)); when(selector.selectSensors(any(DefaultInputModule.class), eq(true))).thenReturn(Collections.singleton(globalSensor)); - DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder()); - rootModuleExecutor = new SensorsExecutor(selector, rootModule, mock(EventBus.class), strategy); + ProjectDefinition childDef = ProjectDefinition.create().setKey("sub").setBaseDir(temp.newFolder()); + ProjectDefinition rootDef = ProjectDefinition.create().setKey("root").setBaseDir(temp.newFolder()); + + DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootDef); + DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(childDef); + + InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class); + when(hierarchy.isRoot(rootModule)).thenReturn(true); - DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule("sub", temp.newFolder()); - rootModule.definition().addSubProject(subModule.definition()); - subModuleExecutor = new SensorsExecutor(selector, subModule, mock(EventBus.class), strategy); + rootModuleExecutor = new SensorsExecutor(selector, rootModule, hierarchy, mock(EventBus.class), strategy); + subModuleExecutor = new SensorsExecutor(selector, subModule, hierarchy, mock(EventBus.class), strategy); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/postjob/DefaultPostJobContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/postjob/DefaultPostJobContextTest.java index 47ceca94888..9f19852e6fd 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/postjob/DefaultPostJobContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/postjob/DefaultPostJobContextTest.java @@ -26,6 +26,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.postjob.issue.PostJobIssue; import org.sonar.api.batch.rule.Severity; @@ -51,9 +52,10 @@ public class DefaultPostJobContextTest { private AnalysisMode analysisMode; @Before - public void prepare() { + public void setUp() throws IOException { issueCache = mock(IssueCache.class); - componentStore = new InputComponentStore(new PathResolver()); + DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule("foo", temp.newFolder()); + componentStore = new InputComponentStore(new PathResolver(), rootModule); settings = new MapSettings(); analysisMode = mock(AnalysisMode.class); context = new DefaultPostJobContext(settings.asConfig(), settings, issueCache, componentStore, analysisMode); @@ -85,8 +87,6 @@ public class DefaultPostJobContextTest { assertThat(issue.inputComponent()).isNull(); String moduleKey = "foo"; - componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", temp.newFolder())); - componentStore.put(new TestInputFileBuilder(moduleKey, "src/Foo.php").build()); assertThat(issue.inputComponent()).isNotNull(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfilerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfilerTest.java index a3d55dfa855..b2853116007 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfilerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfilerTest.java @@ -42,6 +42,7 @@ import org.sonar.api.batch.events.SensorExecutionHandler; import org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent; import org.sonar.api.batch.events.SensorsPhaseHandler; import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.resources.Project; import org.sonar.api.utils.System2; import org.sonar.scanner.bootstrap.GlobalProperties; @@ -132,7 +133,7 @@ public class PhasesSumUpTimeProfilerTest { } private Project mockProject(String name, boolean isRoot) { - return new Project(ProjectDefinition.create().setName(name).setKey(name)); + return new Project(new DefaultInputModule(ProjectDefinition.create().setName(name).setKey(name))); } private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/AnalysisContextReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/AnalysisContextReportPublisherTest.java index 3c65a91b598..77fc6206b24 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/AnalysisContextReportPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/AnalysisContextReportPublisherTest.java @@ -31,6 +31,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; @@ -65,6 +67,7 @@ public class AnalysisContextReportPublisherTest { private System2 system2; private ProjectRepositories projectRepos; private GlobalConfiguration globalSettings; + private InputModuleHierarchy hierarchy; @Before public void prepare() throws Exception { @@ -73,7 +76,8 @@ public class AnalysisContextReportPublisherTest { when(system2.properties()).thenReturn(new Properties()); projectRepos = mock(ProjectRepositories.class); globalSettings = mock(GlobalConfiguration.class); - publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos, globalSettings); + hierarchy = mock(InputModuleHierarchy.class); + publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos, globalSettings, hierarchy); } @Test @@ -95,7 +99,7 @@ public class AnalysisContextReportPublisherTest { ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); publisher.init(writer); - publisher.dumpModuleSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo")); + publisher.dumpModuleSettings(new DefaultInputModule("foo")); assertThat(writer.getFileStructure().analysisLog()).doesNotExist(); } @@ -122,8 +126,7 @@ public class AnalysisContextReportPublisherTest { when(projectRepos.moduleExists("foo")).thenReturn(true); when(projectRepos.settings("foo")).thenReturn(ImmutableMap.of(COM_FOO, "bar", SONAR_SKIP, "true")); - publisher.dumpModuleSettings(ProjectDefinition.create() - .setProperty("sonar.projectKey", "foo")); + publisher.dumpModuleSettings(new DefaultInputModule("foo")); String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); assertThat(content).doesNotContain(COM_FOO); @@ -144,10 +147,10 @@ public class AnalysisContextReportPublisherTest { assertThat(content).containsOnlyOnce(COM_FOO); assertThat(content).doesNotContain(SONAR_SKIP); - publisher.dumpModuleSettings(ProjectDefinition.create() + publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create() .setProperty("sonar.projectKey", "foo") .setProperty(COM_FOO, "bar") - .setProperty(SONAR_SKIP, "true")); + .setProperty(SONAR_SKIP, "true"))); content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); assertThat(content).containsOnlyOnce(COM_FOO); @@ -170,9 +173,9 @@ public class AnalysisContextReportPublisherTest { assertThat(content).containsOnlyOnce(BIZ); assertThat(content).containsSequence(BIZ, FOO); - publisher.dumpModuleSettings(ProjectDefinition.create() + publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create() .setProperty("sonar.projectKey", "foo") - .setProperty("env." + FOO, "BAR")); + .setProperty("env." + FOO, "BAR"))); content = FileUtils.readFileToString(writer.getFileStructure().analysisLog()); assertThat(content).containsOnlyOnce(FOO); @@ -187,12 +190,12 @@ public class AnalysisContextReportPublisherTest { assertThat(writer.getFileStructure().analysisLog()).exists(); - publisher.dumpModuleSettings(ProjectDefinition.create() + publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create() .setProperty("sonar.projectKey", "foo") .setProperty("sonar.projectKey", "foo") .setProperty("sonar.login", "my_token") .setProperty("sonar.password", "azerty") - .setProperty("sonar.cpp.license.secured", "AZERTY")); + .setProperty("sonar.cpp.license.secured", "AZERTY"))); assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).containsSequence( "sonar.cpp.license.secured=******", @@ -222,14 +225,15 @@ public class AnalysisContextReportPublisherTest { ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder()); publisher.init(writer); - ProjectDefinition module = ProjectDefinition.create() + DefaultInputModule module = new DefaultInputModule(ProjectDefinition.create() .setProperty("sonar.projectKey", "foo") - .setProperty(SONAR_SKIP, "true"); + .setProperty(SONAR_SKIP, "true")); - ProjectDefinition.create() + DefaultInputModule parent = new DefaultInputModule(ProjectDefinition.create() .setProperty("sonar.projectKey", "parent") - .setProperty(SONAR_SKIP, "true") - .addSubProject(module); + .setProperty(SONAR_SKIP, "true")); + + when(hierarchy.parent(module)).thenReturn(parent); publisher.dumpModuleSettings(module); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java index 5551ca8f679..395d227be62 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java @@ -26,6 +26,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; @@ -56,8 +57,8 @@ public class CoveragePublisherTest { public void prepare() throws IOException { String moduleKey = "foo"; inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setLines(5).build(); - InputComponentStore componentCache = new InputComponentStore(new PathResolver()); - componentCache.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder())); + DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()); + InputComponentStore componentCache = new InputComponentStore(new PathResolver(), rootModule); componentCache.put(inputFile); measureCache = mock(MeasureCache.class); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java index d3dfe4cc3e9..6f513738f45 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java @@ -69,8 +69,7 @@ public class MeasuresPublisherTest { String moduleKey = "foo"; inputModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()); inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setPublish(true).build(); - InputComponentStore componentCache = new InputComponentStore(new PathResolver()); - componentCache.put(inputModule); + InputComponentStore componentCache = new InputComponentStore(new PathResolver(), inputModule); componentCache.put(inputFile); measureCache = mock(MeasureCache.class); when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList()); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java index e59cbd47ee4..3d3dddfe821 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java @@ -19,8 +19,15 @@ */ package org.sonar.scanner.report; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.util.Date; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,53 +39,48 @@ import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.config.internal.MapSettings; import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.rule.ModuleQProfiles; import org.sonar.scanner.rule.QProfile; -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class MetadataPublisherTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private ProjectDefinition projectDef; private DefaultInputModule rootModule; private MetadataPublisher underTest; private MapSettings settings; private ModuleQProfiles qProfiles; private ProjectAnalysisInfo projectAnalysisInfo; + private CpdSettings cpdSettings; private InputModuleHierarchy inputModuleHierarchy; @Before public void prepare() { - projectDef = ProjectDefinition.create().setKey("foo"); - rootModule = new DefaultInputModule(projectDef, TestInputFileBuilder.nextBatchId()); projectAnalysisInfo = mock(ProjectAnalysisInfo.class); + cpdSettings = mock(CpdSettings.class); when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(1234567L)); - inputModuleHierarchy = mock(InputModuleHierarchy.class); - when(inputModuleHierarchy.root()).thenReturn(rootModule); settings = new MapSettings(); qProfiles = mock(ModuleQProfiles.class); - underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles); + createPublisher(ProjectDefinition.create().setKey("foo")); + } + + private void createPublisher(ProjectDefinition def) { + rootModule = new DefaultInputModule(def, TestInputFileBuilder.nextBatchId()); + inputModuleHierarchy = mock(InputModuleHierarchy.class); + when(inputModuleHierarchy.root()).thenReturn(rootModule); + underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles, cpdSettings); } @Test public void write_metadata() throws Exception { settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); Date date = new Date(); - when(qProfiles.findAll()).thenReturn(asList(new QProfile() - .setKey("q1") - .setName("Q1") - .setLanguage("java") - .setRulesUpdatedAt(date))); + when(qProfiles.findAll()).thenReturn(asList(new QProfile("q1", "Q1", "java", date))); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); @@ -89,7 +91,6 @@ public class MetadataPublisherTest { assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); assertThat(metadata.getProjectKey()).isEqualTo("foo"); assertThat(metadata.getProjectKey()).isEqualTo("foo"); - assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue(); assertThat(metadata.getQprofilesPerLanguage()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() .setKey("q1") .setName("Q1") @@ -100,10 +101,14 @@ public class MetadataPublisherTest { @Test public void write_project_branch() throws Exception { + when(cpdSettings.isCrossProjectDuplicationEnabled()).thenReturn(false); settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); - projectDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); - projectDef.setKey("foo"); + + ProjectDefinition projectDef = ProjectDefinition.create() + .setKey("foo") + .setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); + createPublisher(projectDef); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); @@ -115,7 +120,6 @@ public class MetadataPublisherTest { assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); assertThat(metadata.getProjectKey()).isEqualTo("foo"); assertThat(metadata.getBranch()).isEqualTo("myBranch"); - // Cross project duplication disabled on branches assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java index 7b93925349e..635f15e74f5 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java @@ -19,12 +19,21 @@ */ package org.sonar.scanner.report; +import static org.apache.commons.io.FileUtils.readFileToString; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.nio.file.Files; import java.nio.file.Path; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -34,6 +43,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.platform.Server; @@ -44,20 +55,11 @@ import org.sonar.api.utils.log.LoggerLevel; import org.sonar.core.config.CorePropertyDefinitions; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.bootstrap.ScannerWsClient; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonarqube.ws.WsCe; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.WsRequest; import org.sonarqube.ws.client.WsResponse; -import static org.apache.commons.io.FileUtils.readFileToString; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class ReportPublisherTest { @Rule @@ -73,22 +75,23 @@ public class ReportPublisherTest { MapSettings settings = new MapSettings(new PropertyDefinitions(CorePropertyDefinitions.all())); ScannerWsClient wsClient; Server server = mock(Server.class); - ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class); - ProjectDefinition root; + InputModuleHierarchy moduleHierarchy = mock(InputModuleHierarchy.class); + DefaultInputModule root; AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class); @Before public void setUp() { wsClient = mock(ScannerWsClient.class, Mockito.RETURNS_DEEP_STUBS); - root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot()); - when(reactor.getRoot()).thenReturn(root); + root = new DefaultInputModule(ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot())); + when(moduleHierarchy.root()).thenReturn(root); when(server.getPublicRootUrl()).thenReturn("https://localhost"); when(server.getVersion()).thenReturn("6.4"); } @Test public void log_and_dump_information_about_report_uploading() throws IOException { - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg"); underTest.logSuccess("TASK-123"); @@ -111,7 +114,8 @@ public class ReportPublisherTest { @Test public void parse_upload_error_message() throws IOException { - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); HttpException ex = new HttpException("url", 404, "{\"errors\":[{\"msg\":\"Organization with key 'MyOrg' does not exist\"}]}"); WsResponse response = mock(WsResponse.class); when(response.failIfNotSuccessful()).thenThrow(ex); @@ -125,7 +129,8 @@ public class ReportPublisherTest { @Test public void log_public_url_if_defined() throws IOException { when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube"); - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); underTest.logSuccess("TASK-123"); @@ -146,7 +151,8 @@ public class ReportPublisherTest { @Test public void fail_if_public_url_malformed() throws IOException { when(server.getPublicRootUrl()).thenReturn("invalid"); - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); exception.expect(MessageException.class); exception.expectMessage("Failed to parse public URL set in SonarQube server: invalid"); @@ -155,7 +161,8 @@ public class ReportPublisherTest { @Test public void log_but_not_dump_information_when_report_is_not_uploaded() { - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); underTest.logSuccess(/* report not uploaded, no server task */null); @@ -172,7 +179,8 @@ public class ReportPublisherTest { settings.setProperty("sonar.batch.keepReport", true); Path reportDir = temp.getRoot().toPath().resolve("batch-report"); Files.createDirectory(reportDir); - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); underTest.start(); underTest.stop(); @@ -183,7 +191,7 @@ public class ReportPublisherTest { public void should_delete_report_by_default() throws IOException { Path reportDir = temp.getRoot().toPath().resolve("batch-report"); Files.createDirectory(reportDir); - ReportPublisher job = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher job = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), new ReportPublisherStep[0]); job.start(); job.stop(); @@ -192,7 +200,8 @@ public class ReportPublisherTest { @Test public void test_ws_parameters() throws Exception { - ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); + ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), + new ReportPublisherStep[0]); settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/SourcePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/SourcePublisherTest.java index 3624dcabf65..76f0dc7aee6 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/SourcePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/SourcePublisherTest.java @@ -28,6 +28,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.scanner.protocol.output.ScannerReportWriter; @@ -55,8 +56,8 @@ public class SourcePublisherTest { .setCharset(StandardCharsets.ISO_8859_1) .build(); - InputComponentStore componentStore = new InputComponentStore(new PathResolver()); - componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, baseDir)); + DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, baseDir); + InputComponentStore componentStore = new InputComponentStore(new PathResolver(), rootModule); componentStore.put(inputFile); publisher = new SourcePublisher(componentStore); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileTest.java index a21efc17c55..e5fda6b88c5 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileTest.java @@ -26,18 +26,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class QProfileTest { @Test public void testEquals() { - QProfile q1 = new QProfile(); - QProfile q2 = new QProfile(); - QProfile q3 = new QProfile(); - - q1.setKey("k1"); - q1.setName("name1"); - - q2.setKey("k1"); - q2.setName("name2"); - - q3.setKey("k3"); - q3.setName("name3"); + QProfile q1 = new QProfile("k1", "name1", null, null); + QProfile q2 = new QProfile("k1", "name2", null, null); + QProfile q3 = new QProfile("k3", "name3", null, null); assertThat(q1).isEqualTo(q2); assertThat(q1).isNotEqualTo(q3); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileVerifierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileVerifierTest.java index f68303521a5..cd5b36636f4 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileVerifierTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileVerifierTest.java @@ -50,9 +50,9 @@ public class QProfileVerifierTest { public void before() throws Exception { fs = new DefaultFileSystem(temp.newFolder().toPath()); profiles = mock(ModuleQProfiles.class); - QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java"); + QProfile javaProfile = new QProfile("p1", "My Java profile", "java", null); when(profiles.findByLanguage("java")).thenReturn(javaProfile); - QProfile cobolProfile = new QProfile().setKey("p2").setName("My Cobol profile").setLanguage("cobol"); + QProfile cobolProfile = new QProfile("p2", "My Cobol profile", "cobol", null); when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java index 8dff3fadef3..2df62b25272 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java @@ -39,7 +39,7 @@ public class RulesProfileProviderTest { @Test public void merge_profiles() { - QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null); when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings.asConfig()); @@ -61,7 +61,7 @@ public class RulesProfileProviderTest { public void keep_compatibility_with_single_language_projects() { settings.setProperty("sonar.language", "java"); - QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null); when(qProfiles.findByLanguage("java")).thenReturn(qProfile); RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings.asConfig()); @@ -74,7 +74,7 @@ public class RulesProfileProviderTest { @Test public void support_rule_templates() { - QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); + QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null); when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); activeRulesBuilder.create(RuleKey.of("java", "S001")).setTemplateRuleKey("T001").setLanguage("java").activate(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/DefaultInputModuleHierarchyTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/DefaultInputModuleHierarchyTest.java index be0f7773038..977fdc6a67c 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/DefaultInputModuleHierarchyTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/DefaultInputModuleHierarchyTest.java @@ -19,20 +19,17 @@ */ package org.sonar.scanner.scan; -import org.junit.Before; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + import org.junit.Test; import org.sonar.api.batch.fs.internal.DefaultInputModule; -import static org.assertj.core.api.Assertions.assertThat; - public class DefaultInputModuleHierarchyTest { private DefaultInputModuleHierarchy moduleHierarchy; - @Before - public void setUp() { - moduleHierarchy = new DefaultInputModuleHierarchy(); - } - @Test public void test() { DefaultInputModule root = new DefaultInputModule("root"); @@ -41,11 +38,14 @@ public class DefaultInputModuleHierarchyTest { DefaultInputModule mod3 = new DefaultInputModule("mod3"); DefaultInputModule mod4 = new DefaultInputModule("mod4"); - moduleHierarchy.setRoot(root); - moduleHierarchy.index(mod1, root); - moduleHierarchy.index(mod2, mod1); - moduleHierarchy.index(mod3, root); - moduleHierarchy.index(mod4, root); + Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>(); + + parents.put(mod1, root); + parents.put(mod2, mod1); + parents.put(mod3, root); + parents.put(mod4, root); + + moduleHierarchy = new DefaultInputModuleHierarchy(parents); assertThat(moduleHierarchy.children(root)).containsOnly(mod1, mod3, mod4); assertThat(moduleHierarchy.children(mod4)).isEmpty(); @@ -58,4 +58,28 @@ public class DefaultInputModuleHierarchyTest { assertThat(moduleHierarchy.root()).isEqualTo(root); } + + @Test + public void testOnlyRoot() { + DefaultInputModule root = new DefaultInputModule("root"); + moduleHierarchy = new DefaultInputModuleHierarchy(root); + + assertThat(moduleHierarchy.children(root)).isEmpty(); + assertThat(moduleHierarchy.parent(root)).isNull(); + assertThat(moduleHierarchy.root()).isEqualTo(root); + } + + @Test + public void testParentChild() { + DefaultInputModule root = new DefaultInputModule("root"); + DefaultInputModule mod1 = new DefaultInputModule("mod1"); + moduleHierarchy = new DefaultInputModuleHierarchy(root, mod1); + + assertThat(moduleHierarchy.children(root)).containsOnly(mod1); + assertThat(moduleHierarchy.children(mod1)).isEmpty(); + + assertThat(moduleHierarchy.parent(mod1)).isEqualTo(root); + assertThat(moduleHierarchy.parent(root)).isNull(); + assertThat(moduleHierarchy.root()).isEqualTo(root); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ModuleIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ModuleIndexerTest.java index 74faa55f153..63bd38a6ada 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ModuleIndexerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ModuleIndexerTest.java @@ -19,48 +19,63 @@ */ package org.sonar.scanner.scan; -import org.junit.Before; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + import org.junit.Test; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.scanner.scan.filesystem.BatchIdGenerator; import org.sonar.scanner.scan.filesystem.InputComponentStore; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class ModuleIndexerTest { private ModuleIndexer indexer; private DefaultComponentTree tree; private DefaultInputModuleHierarchy moduleHierarchy; - private ImmutableProjectReactor reactor; private InputComponentStore componentStore; - @Before - public void setUp() { - reactor = mock(ImmutableProjectReactor.class); - componentStore = new InputComponentStore(new PathResolver()); + public void createIndexer(DefaultInputModule rootModule) { + componentStore = new InputComponentStore(new PathResolver(), rootModule); tree = new DefaultComponentTree(); - moduleHierarchy = new DefaultInputModuleHierarchy(); - indexer = new ModuleIndexer(reactor, tree, componentStore, new BatchIdGenerator(), moduleHierarchy); + moduleHierarchy = mock(DefaultInputModuleHierarchy.class); + indexer = new ModuleIndexer(tree, componentStore, moduleHierarchy); } - + @Test public void testIndex() { - ProjectDefinition root = ProjectDefinition.create().setKey("root"); - ProjectDefinition mod1 = ProjectDefinition.create().setKey("mod1"); - ProjectDefinition mod2 = ProjectDefinition.create().setKey("mod2"); - ProjectDefinition mod3 = ProjectDefinition.create().setKey("mod3"); - ProjectDefinition mod4 = ProjectDefinition.create().setKey("mod4"); + ProjectDefinition rootDef = mock(ProjectDefinition.class); + ProjectDefinition def = mock(ProjectDefinition.class); + when(rootDef.getParent()).thenReturn(null); + when(def.getParent()).thenReturn(rootDef); + + DefaultInputModule root = mock(DefaultInputModule.class); + DefaultInputModule mod1 = mock(DefaultInputModule.class); + DefaultInputModule mod2 = mock(DefaultInputModule.class); + DefaultInputModule mod3 = mock(DefaultInputModule.class); + + when(root.key()).thenReturn("root"); + when(mod1.key()).thenReturn("mod1"); + when(mod2.key()).thenReturn("mod2"); + when(mod3.key()).thenReturn("mod3"); + + when(root.getKeyWithBranch()).thenReturn("root"); + when(mod1.getKeyWithBranch()).thenReturn("mod1"); + when(mod2.getKeyWithBranch()).thenReturn("mod2"); + when(mod3.getKeyWithBranch()).thenReturn("mod3"); + + when(root.definition()).thenReturn(rootDef); + when(mod1.definition()).thenReturn(def); + when(mod2.definition()).thenReturn(def); + when(mod3.definition()).thenReturn(def); - root.addSubProject(mod1); - mod1.addSubProject(mod2); - root.addSubProject(mod3); - root.addSubProject(mod4); + createIndexer(root); + when(moduleHierarchy.root()).thenReturn(root); + when(moduleHierarchy.children(root)).thenReturn(Arrays.asList(mod1, mod2, mod3)); - when(reactor.getRoot()).thenReturn(root); indexer.start(); InputModule rootModule = moduleHierarchy.root(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectLockTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectLockTest.java index cd628bbe8f4..af46b5edd7b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectLockTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectLockTest.java @@ -19,22 +19,23 @@ */ package org.sonar.scanner.scan; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.nio.file.Files; import java.nio.file.Path; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.home.cache.DirectoryLock; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class ProjectLockTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @@ -49,12 +50,12 @@ public class ProjectLockTest { } private ProjectLock setUpTest(File file) { - ProjectReactor projectReactor = mock(ProjectReactor.class); - ProjectDefinition projectDefinition = mock(ProjectDefinition.class); - when(projectReactor.getRoot()).thenReturn(projectDefinition); - when(projectDefinition.getWorkDir()).thenReturn(file); + InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class); + DefaultInputModule root = mock(DefaultInputModule.class); + when(hierarchy.root()).thenReturn(root); + when(root.getWorkDir()).thenReturn(file); - return new ProjectLock(projectReactor); + return new ProjectLock(hierarchy); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java index 9f7833fbc75..24fa9205c37 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java @@ -19,6 +19,9 @@ */ package org.sonar.scanner.scan; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -26,28 +29,21 @@ import org.junit.rules.ExpectedException; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; -import org.sonar.api.config.internal.MapSettings; import org.sonar.api.utils.MessageException; import org.sonar.scanner.analysis.DefaultAnalysisMode; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class ProjectReactorValidatorTest { @Rule public ExpectedException thrown = ExpectedException.none(); private ProjectReactorValidator validator; - private Settings settings; private DefaultAnalysisMode mode; @Before public void prepare() { mode = mock(DefaultAnalysisMode.class); - settings = new MapSettings(); - validator = new ProjectReactorValidator(settings, mode); + validator = new ProjectReactorValidator(mode); } @Test @@ -62,12 +58,12 @@ public class ProjectReactorValidatorTest { validator.validate(createProjectReactor("3-3")); validator.validate(createProjectReactor("-:")); } - + @Test public void allow_slash_issues_mode() { when(mode.isIssues()).thenReturn(true); validator.validate(createProjectReactor("project/key")); - + when(mode.isIssues()).thenReturn(false); thrown.expect(MessageException.class); thrown.expectMessage("is not a valid project or module key"); @@ -157,16 +153,6 @@ public class ProjectReactorValidatorTest { validator.validate(reactor); } - @Test - public void fail_with_deprecated_sonar_phase() { - ProjectReactor reactor = createProjectReactor("foo"); - settings.setProperty("sonar.phase", "phase"); - - thrown.expect(MessageException.class); - thrown.expectMessage("\"sonar.phase\" is deprecated"); - validator.validate(reactor); - } - private ProjectReactor createProjectReactor(String projectKey) { ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); ProjectReactor reactor = new ProjectReactor(def); @@ -177,9 +163,7 @@ public class ProjectReactorValidatorTest { ProjectDefinition def = ProjectDefinition.create() .setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey) .setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch); - ProjectReactor reactor = new ProjectReactor(def); - settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch); - return reactor; + return new ProjectReactor(def); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java index 58b4acc2823..aa824ab9abc 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java @@ -19,20 +19,21 @@ */ package org.sonar.scanner.scan; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.home.cache.DirectoryLock; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class WorkDirectoryCleanerTest { private WorkDirectoryCleaner cleaner; @Rule @@ -50,21 +51,21 @@ public class WorkDirectoryCleanerTest { lock.createNewFile(); // mock project - ProjectReactor projectReactor = mock(ProjectReactor.class); - ProjectDefinition projectDefinition = mock(ProjectDefinition.class); - when(projectReactor.getRoot()).thenReturn(projectDefinition); - when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot()); + InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class); + DefaultInputModule root = mock(DefaultInputModule.class); + when(hierarchy.root()).thenReturn(root); + when(root.getWorkDir()).thenReturn(temp.getRoot()); assertThat(temp.getRoot().list().length).isGreaterThan(1); - cleaner = new WorkDirectoryCleaner(projectReactor); + cleaner = new WorkDirectoryCleaner(hierarchy); } - + @Test public void testNonExisting() { temp.delete(); cleaner.execute(); } - + @Test public void testClean() { File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ExclusionFiltersTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ExclusionFiltersTest.java index 9815f2de7f2..f64a6729750 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ExclusionFiltersTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ExclusionFiltersTest.java @@ -19,9 +19,12 @@ */ package org.sonar.scanner.scan.filesystem; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.File; import java.io.IOException; import java.nio.file.Path; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -33,8 +36,6 @@ import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.scan.filesystem.FileExclusions; -import static org.assertj.core.api.Assertions.assertThat; - public class ExclusionFiltersTest { @Rule @@ -53,7 +54,8 @@ public class ExclusionFiltersTest { @Test public void no_inclusions_nor_exclusions() throws IOException { filter.prepare(); - IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java"); + + IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue(); assertThat(filter.accept(indexedFile, InputFile.Type.TEST)).isTrue(); } @@ -63,10 +65,10 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java"); filter.prepare(); - IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java"); + IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue(); - indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java"); + indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse(); } @@ -75,10 +77,10 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java"); filter.prepare(); - IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java"); + IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse(); - indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDto.java"); + indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDto.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue(); } @@ -89,14 +91,14 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java"); filter.prepare(); - IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java"); + IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse(); - indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java"); + indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue(); // source exclusions do not apply to tests - indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/test/java/com/mycompany/FooDao.java"); + indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/test/java/com/mycompany/FooDao.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.TEST)).isTrue(); } @@ -108,10 +110,10 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getAbsolutePath()); filter.prepare(); - IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java"); + IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue(); - indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Bar.java"); + indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Bar.java", null); assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java index 6bff097acf3..9a82a623f55 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java @@ -27,6 +27,7 @@ import java.util.List; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Status; import org.sonar.api.batch.fs.InputFile.Type; @@ -44,16 +45,20 @@ public class InputComponentStoreTest { @Test public void should_add_input_file() throws Exception { - InputComponentStore cache = new InputComponentStore(new PathResolver()); - String rootModuleKey = "struts"; + String subModuleKey = "struts-core"; + File rootBaseDir = temp.newFolder(); - DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootModuleKey, rootBaseDir); - cache.put(rootModule); - String subModuleKey = "struts-core"; - DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(subModuleKey, temp.newFolder()); - rootModule.definition().addSubProject(subModule.definition()); + ProjectDefinition moduleDef = ProjectDefinition.create() + .setKey(subModuleKey).setBaseDir(rootBaseDir); + ProjectDefinition rootDef = ProjectDefinition.create() + .setKey(rootModuleKey).setBaseDir(rootBaseDir).addSubProject(moduleDef); + + DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootDef); + DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(moduleDef); + + InputComponentStore cache = new InputComponentStore(new PathResolver(), rootModule); cache.put(subModule); DefaultInputFile fooFile = new TestInputFileBuilder(rootModuleKey, "src/main/java/Foo.java") @@ -97,9 +102,7 @@ public class InputComponentStoreTest { static class InputComponentStoreTester extends InputComponentStore { InputComponentStoreTester() throws IOException { - super(new PathResolver()); - DefaultInputModule root = TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder()); - put(root); + super(new PathResolver(), TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder())); } InputFile addFile(String moduleKey, String relpath, String language) { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/LanguageDetectionTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/LanguageDetectionTest.java index 86d33bb6660..1d8f553d1f5 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/LanguageDetectionTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/LanguageDetectionTest.java @@ -19,15 +19,19 @@ */ package org.sonar.scanner.scan.filesystem; +import static junit.framework.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; + import java.io.File; import java.io.IOException; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; @@ -35,10 +39,6 @@ import org.sonar.api.utils.MessageException; import org.sonar.scanner.repository.language.DefaultLanguagesRepository; import org.sonar.scanner.repository.language.LanguagesRepository; -import static junit.framework.Assert.fail; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.spy; - public class LanguageDetectionTest { @Rule @@ -67,23 +67,23 @@ public class LanguageDetectionTest { LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob"))); LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("Foo.java"))).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("src/Foo.java"))).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("Foo.JAVA"))).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("Foo.jav"))).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("Foo.Jav"))).isEqualTo("java"); + assertThat(detectLanguage(detection, "Foo.java")).isEqualTo("java"); + assertThat(detectLanguage(detection, "src/Foo.java")).isEqualTo("java"); + assertThat(detectLanguage(detection, "Foo.JAVA")).isEqualTo("java"); + assertThat(detectLanguage(detection, "Foo.jav")).isEqualTo("java"); + assertThat(detectLanguage(detection, "Foo.Jav")).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("abc.cbl"))).isEqualTo("cobol"); - assertThat(detection.language(newIndexedFile("abc.CBL"))).isEqualTo("cobol"); + assertThat(detectLanguage(detection, "abc.cbl")).isEqualTo("cobol"); + assertThat(detectLanguage(detection, "abc.CBL")).isEqualTo("cobol"); - assertThat(detection.language(newIndexedFile("abc.php"))).isNull(); - assertThat(detection.language(newIndexedFile("abc"))).isNull(); + assertThat(detectLanguage(detection, "abc.php")).isNull(); + assertThat(detectLanguage(detection, "abc")).isNull(); } @Test public void should_not_fail_if_no_language() throws Exception { LanguageDetection detection = spy(new LanguageDetection(settings.asConfig(), new DefaultLanguagesRepository(new Languages()))); - assertThat(detection.language(newIndexedFile("Foo.java"))).isNull(); + assertThat(detectLanguage(detection, "Foo.java")).isNull(); } @Test @@ -91,7 +91,7 @@ public class LanguageDetectionTest { LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap", "ABAP"))); LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("abc.abap"))).isEqualTo("abap"); + assertThat(detectLanguage(detection, "abc.abap")).isEqualTo("abap"); } @Test @@ -102,15 +102,15 @@ public class LanguageDetectionTest { // No side-effect on non-ABAP projects LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("abc"))).isNull(); - assertThat(detection.language(newIndexedFile("abc.abap"))).isNull(); - assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("java"); + assertThat(detectLanguage(detection, "abc")).isNull(); + assertThat(detectLanguage(detection, "abc.abap")).isNull(); + assertThat(detectLanguage(detection, "abc.java")).isEqualTo("java"); settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "abap"); detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("abc"))).isEqualTo("abap"); - assertThat(detection.language(newIndexedFile("abc.txt"))).isEqualTo("abap"); - assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("abap"); + assertThat(detectLanguage(detection, "abc")).isEqualTo("abap"); + assertThat(detectLanguage(detection, "abc.txt")).isEqualTo("abap"); + assertThat(detectLanguage(detection, "abc.java")).isEqualTo("abap"); } @Test @@ -119,10 +119,10 @@ public class LanguageDetectionTest { settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "java"); LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("abc"))).isNull(); - assertThat(detection.language(newIndexedFile("abc.php"))).isNull(); - assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("java"); - assertThat(detection.language(newIndexedFile("src/abc.java"))).isEqualTo("java"); + assertThat(detectLanguage(detection, "abc")).isNull(); + assertThat(detectLanguage(detection, "abc.php")).isNull(); + assertThat(detectLanguage(detection, "abc.java")).isEqualTo("java"); + assertThat(detectLanguage(detection, "src/abc.java")).isEqualTo("java"); } @Test @@ -140,7 +140,7 @@ public class LanguageDetectionTest { LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml"))); LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); try { - detection.language(newIndexedFile("abc.xhtml")); + detectLanguage(detection, "abc.xhtml"); fail(); } catch (MessageException e) { assertThat(e.getMessage()) @@ -157,8 +157,8 @@ public class LanguageDetectionTest { settings.setProperty("sonar.lang.patterns.xml", "xml/**"); settings.setProperty("sonar.lang.patterns.web", "web/**"); LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("xml/abc.xhtml"))).isEqualTo("xml"); - assertThat(detection.language(newIndexedFile("web/abc.xhtml"))).isEqualTo("web"); + assertThat(detectLanguage(detection, "xml/abc.xhtml")).isEqualTo("xml"); + assertThat(detectLanguage(detection, "web/abc.xhtml")).isEqualTo("web"); } @Test @@ -169,10 +169,10 @@ public class LanguageDetectionTest { LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages); - assertThat(detection.language(newIndexedFile("abc.abap"))).isEqualTo("abap"); - assertThat(detection.language(newIndexedFile("abc.cobol"))).isEqualTo("cobol"); + assertThat(detectLanguage(detection, "abc.abap")).isEqualTo("abap"); + assertThat(detectLanguage(detection, "abc.cobol")).isEqualTo("cobol"); try { - detection.language(newIndexedFile("abc.txt")); + detectLanguage(detection, "abc.txt"); fail(); } catch (MessageException e) { assertThat(e.getMessage()) @@ -182,9 +182,8 @@ public class LanguageDetectionTest { } } - private DefaultIndexedFile newIndexedFile(String path) throws IOException { - File basedir = temp.newFolder(); - return new DefaultIndexedFile("foo", basedir.toPath(), path); + private String detectLanguage(LanguageDetection detection, String path) { + return detection.language(new File(temp.getRoot(), path).getAbsolutePath(), path); } static class MockLanguage implements Language { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializerTest.java index 4790f48ae6e..454223ad846 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializerTest.java @@ -19,8 +19,12 @@ */ package org.sonar.scanner.scan.filesystem; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import java.io.File; import java.io.IOException; + import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.junit.Rule; @@ -30,9 +34,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.TempFolder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - public class ModuleFileSystemInitializerTest { @Rule diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java index cb018de8258..8451685ee3c 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.scanner.sensor.SensorStrategy; @@ -46,8 +47,8 @@ public class ModuleInputComponentStoreTest { @Before public void setUp() throws IOException { - componentStore = new InputComponentStore(new PathResolver()); - componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder())); + DefaultInputModule root = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()); + componentStore = new InputComponentStore(new PathResolver(), root); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java index 926b149ef3e..42d4c443c72 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java @@ -19,6 +19,12 @@ */ package org.sonar.scanner.scan.report; +import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; import java.io.StringWriter; @@ -26,6 +32,7 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.TimeZone; + import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; @@ -51,12 +58,6 @@ import org.sonar.scanner.issue.tracking.TrackedIssue; import org.sonar.scanner.scan.DefaultComponentTree; import org.sonar.scanner.scan.filesystem.InputComponentStore; -import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - public class JSONReportTest { private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); @@ -80,10 +81,11 @@ public class JSONReportTest { SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00")); when(server.getVersion()).thenReturn("3.6"); - InputComponentStore inputComponentStore = new InputComponentStore(new PathResolver()); DefaultComponentTree inputComponentTree = new DefaultComponentTree(); - DefaultInputModule rootModule = new DefaultInputModule(ProjectDefinition.create().setBaseDir(projectBaseDir).setKey("struts"), 1); - inputComponentStore.put(rootModule); + ProjectDefinition def = ProjectDefinition.create().setBaseDir(projectBaseDir).setKey("struts"); + DefaultInputModule rootModule = new DefaultInputModule(def, 1); + InputComponentStore inputComponentStore = new InputComponentStore(new PathResolver(), rootModule); + DefaultInputModule moduleA = new DefaultInputModule("struts-core"); inputComponentTree.index(moduleA, rootModule); DefaultInputModule moduleB = new DefaultInputModule("struts-ui"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java index 7eafee529ef..e5d5d91a7a2 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java @@ -37,14 +37,13 @@ public class CoverageExclusionsTest { @Before public void prepare() { settings = new MapSettings(new PropertyDefinitions(ExclusionProperties.all())); - coverageExclusions = new CoverageExclusions(settings.asConfig()); } @Test public void shouldExcludeFileBasedOnPattern() { InputFile file = new TestInputFileBuilder("foo", "src/org/polop/File.php").build(); settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*"); - coverageExclusions.initPatterns(); + coverageExclusions = new CoverageExclusions(settings.asConfig()); assertThat(coverageExclusions.isExcluded(file)).isTrue(); } @@ -52,7 +51,7 @@ public class CoverageExclusionsTest { public void shouldNotExcludeFileBasedOnPattern() { InputFile file = new TestInputFileBuilder("foo", "src/org/polop/File.php").build(); settings.setProperty("sonar.coverage.exclusions", "src/org/other/*"); - coverageExclusions.initPatterns(); + coverageExclusions = new CoverageExclusions(settings.asConfig()); assertThat(coverageExclusions.isExcluded(file)).isFalse(); } } diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java index f61d225128d..e8468cc79d3 100644 --- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java +++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/FileStructure.java @@ -21,9 +21,12 @@ package org.sonar.scanner.protocol.output; import java.io.File; +import javax.annotation.concurrent.Immutable; + /** * Structure of files in the zipped report */ +@Immutable public class FileStructure { public enum Domain { diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java index 9cca4f6d5ca..9ce6547ff52 100644 --- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java +++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportWriter.java @@ -23,9 +23,13 @@ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; + +import javax.annotation.concurrent.Immutable; + import org.sonar.core.util.ContextException; import org.sonar.core.util.Protobuf; +@Immutable public class ScannerReportWriter { private final FileStructure fileStructure; |