@@ -20,12 +20,13 @@ | |||
package org.sonar.api.batch.fs; | |||
import org.sonar.api.ExtensionPoint; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.scanner.ScannerSide; | |||
import org.sonarsource.api.sonarlint.SonarLintSide; | |||
/** | |||
* Extension point to exclude some files from inspection | |||
* @since 4.2 | |||
* @since 7.6 evaluated at project level | |||
*/ | |||
@ScannerSide | |||
@SonarLintSide |
@@ -26,11 +26,8 @@ import java.io.InputStreamReader; | |||
import java.io.Reader; | |||
import java.nio.charset.Charset; | |||
import java.nio.charset.StandardCharsets; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.charhandler.CharHandler; | |||
import org.sonar.api.batch.fs.internal.charhandler.FileHashComputer; | |||
@@ -42,7 +39,6 @@ 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 char LINE_FEED = '\n'; |
@@ -37,10 +37,11 @@ import org.sonar.api.config.Configuration; | |||
@Deprecated | |||
@ScannerSide | |||
public class FileExclusions { | |||
private final Configuration settings; | |||
public FileExclusions(Configuration settings) { | |||
this.settings = settings; | |||
private final Configuration config; | |||
public FileExclusions(Configuration config) { | |||
this.config = config; | |||
} | |||
public String[] sourceInclusions() { | |||
@@ -52,7 +53,7 @@ public class FileExclusions { | |||
} | |||
private String[] inclusions(String propertyKey) { | |||
return Arrays.stream(settings.getStringArray(propertyKey)) | |||
return Arrays.stream(config.getStringArray(propertyKey)) | |||
.map(StringUtils::trim) | |||
.filter(s -> !"**/*".equals(s)) | |||
.filter(s -> !"file:**/*".equals(s)) | |||
@@ -68,8 +69,8 @@ public class FileExclusions { | |||
} | |||
private String[] exclusions(String globalExclusionsProperty, String exclusionsProperty) { | |||
String[] globalExclusions = settings.getStringArray(globalExclusionsProperty); | |||
String[] exclusions = settings.getStringArray(exclusionsProperty); | |||
String[] globalExclusions = config.getStringArray(globalExclusionsProperty); | |||
String[] exclusions = config.getStringArray(exclusionsProperty); | |||
return Stream.concat(Arrays.stream(globalExclusions), Arrays.stream(exclusions)) | |||
.map(StringUtils::trim) | |||
.toArray(String[]::new); |
@@ -25,15 +25,15 @@ import java.util.List; | |||
import java.util.Set; | |||
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.charhandler.CharHandler; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
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; | |||
public class IssueExclusionsRegexpScanner extends CharHandler { | |||
private static final Logger LOG = LoggerFactory.getLogger(IssueExclusionsLoader.class); | |||
private static final Logger LOG = Loggers.get(IssueExclusionsLoader.class); | |||
private final StringBuilder sb = new StringBuilder(); | |||
private final List<Pattern> allFilePatterns; |
@@ -22,17 +22,15 @@ package org.sonar.scanner.phases; | |||
import java.util.Arrays; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.InputModuleHierarchy; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.rule.QProfileVerifier; | |||
import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; | |||
import org.sonar.scanner.scan.filesystem.FileIndexer; | |||
public abstract class AbstractModulePhaseExecutor { | |||
@@ -41,26 +39,22 @@ public abstract class AbstractModulePhaseExecutor { | |||
private final PostJobsExecutor postJobsExecutor; | |||
private final SensorsExecutor sensorsExecutor; | |||
private final DefaultModuleFileSystem fs; | |||
private final QProfileVerifier profileVerifier; | |||
private final IssueExclusionsLoader issueExclusionsLoader; | |||
private final InputModuleHierarchy hierarchy; | |||
private final FileIndexer fileIndexer; | |||
private final ModuleCoverageExclusions moduleCoverageExclusions; | |||
private final ProjectCoverageExclusions projectCoverageExclusions; | |||
private final AnalysisWarnings analysisWarnings; | |||
private boolean warnCoverageAlreadyLogged; | |||
public AbstractModulePhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, InputModuleHierarchy hierarchy, DefaultModuleFileSystem fs, | |||
QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, FileIndexer fileIndexer, | |||
IssueExclusionsLoader issueExclusionsLoader, | |||
ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions, | |||
AnalysisWarnings analysisWarnings) { | |||
this.postJobsExecutor = postJobsExecutor; | |||
this.sensorsExecutor = sensorsExecutor; | |||
this.fs = fs; | |||
this.profileVerifier = profileVerifier; | |||
this.issueExclusionsLoader = issueExclusionsLoader; | |||
this.hierarchy = hierarchy; | |||
this.fileIndexer = fileIndexer; | |||
this.moduleCoverageExclusions = moduleCoverageExclusions; | |||
this.projectCoverageExclusions = projectCoverageExclusions; | |||
this.analysisWarnings = analysisWarnings; | |||
@@ -70,15 +64,6 @@ public abstract class AbstractModulePhaseExecutor { | |||
* Executed on each module | |||
*/ | |||
public final void execute(DefaultInputModule module) { | |||
// Index the filesystem | |||
fileIndexer.index(); | |||
// Log detected languages and their profiles after FS is indexed and languages detected | |||
profileVerifier.execute(); | |||
// Initialize issue exclusions | |||
initIssueExclusions(); | |||
// Initialize coverage exclusions | |||
evaluateCoverageExclusions(module); | |||
@@ -133,11 +118,4 @@ public abstract class AbstractModulePhaseExecutor { | |||
protected abstract void executeOnRoot(); | |||
private void initIssueExclusions() { | |||
if (issueExclusionsLoader.shouldExecute()) { | |||
for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) { | |||
issueExclusionsLoader.addMulticriteriaPatterns(((DefaultInputFile) inputFile).getModuleRelativePath(), inputFile.key()); | |||
} | |||
} | |||
} | |||
} |
@@ -25,9 +25,7 @@ import org.sonar.api.batch.fs.internal.InputModuleHierarchy; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.issue.tracking.IssueTransition; | |||
import org.sonar.scanner.rule.QProfileVerifier; | |||
import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; | |||
import org.sonar.scanner.scan.filesystem.FileIndexer; | |||
import org.sonar.scanner.scan.report.IssuesReports; | |||
public final class IssuesPhaseExecutor extends AbstractModulePhaseExecutor { | |||
@@ -38,11 +36,11 @@ public final class IssuesPhaseExecutor extends AbstractModulePhaseExecutor { | |||
private final IssueTransition localIssueTracking; | |||
public IssuesPhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, | |||
IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, | |||
IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, InputModuleHierarchy moduleHierarchy, FileIndexer fileIndexer, | |||
ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions, | |||
AnalysisWarnings analysisWarnings) { | |||
super(postJobsExecutor, sensorsExecutor, moduleHierarchy, fs, profileVerifier, issueExclusionsLoader, fileIndexer, | |||
IssuesReports jsonReport, DefaultModuleFileSystem fs, | |||
IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, InputModuleHierarchy moduleHierarchy, | |||
ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions, | |||
AnalysisWarnings analysisWarnings) { | |||
super(postJobsExecutor, sensorsExecutor, moduleHierarchy, fs, issueExclusionsLoader, | |||
moduleCoverageExclusions, projectCoverageExclusions, analysisWarnings); | |||
this.issuesReport = jsonReport; | |||
this.localIssueTracking = localIssueTracking; |
@@ -24,9 +24,7 @@ import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.scanner.cpd.CpdExecutor; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.report.ReportPublisher; | |||
import org.sonar.scanner.rule.QProfileVerifier; | |||
import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; | |||
import org.sonar.scanner.scan.filesystem.FileIndexer; | |||
import org.sonar.scanner.scm.ScmPublisher; | |||
public final class PublishPhaseExecutor extends AbstractModulePhaseExecutor { | |||
@@ -36,11 +34,11 @@ public final class PublishPhaseExecutor extends AbstractModulePhaseExecutor { | |||
private final ScmPublisher scm; | |||
public PublishPhaseExecutor(PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, | |||
ReportPublisher reportPublisher, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, | |||
CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy, FileIndexer fileIndexer, | |||
ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions, | |||
AnalysisWarnings analysisWarnings) { | |||
super(postJobsExecutor, sensorsExecutor, hierarchy, fs, profileVerifier, issueExclusionsLoader, fileIndexer, moduleCoverageExclusions, | |||
ReportPublisher reportPublisher, DefaultModuleFileSystem fs, IssueExclusionsLoader issueExclusionsLoader, | |||
CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy, | |||
ModuleCoverageExclusions moduleCoverageExclusions, ProjectCoverageExclusions projectCoverageExclusions, | |||
AnalysisWarnings analysisWarnings) { | |||
super(postJobsExecutor, sensorsExecutor, hierarchy, fs, issueExclusionsLoader, moduleCoverageExclusions, | |||
projectCoverageExclusions, analysisWarnings); | |||
this.reportPublisher = reportPublisher; | |||
this.cpdExecutor = cpdExecutor; |
@@ -23,25 +23,23 @@ import com.google.common.annotations.VisibleForTesting; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.batch.fs.FileSystem; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||
import static org.apache.commons.lang.StringUtils.isNotEmpty; | |||
@ScannerSide | |||
public class QProfileVerifier { | |||
private static final Logger LOG = LoggerFactory.getLogger(QProfileVerifier.class); | |||
private final Configuration settings; | |||
private final FileSystem fs; | |||
private final Configuration config; | |||
private final InputComponentStore store; | |||
private final QualityProfiles profiles; | |||
public QProfileVerifier(Configuration settings, FileSystem fs, QualityProfiles profiles) { | |||
this.settings = settings; | |||
this.fs = fs; | |||
public QProfileVerifier(Configuration config, InputComponentStore store, QualityProfiles profiles) { | |||
this.config = config; | |||
this.store = store; | |||
this.profiles = profiles; | |||
} | |||
@@ -51,9 +49,9 @@ public class QProfileVerifier { | |||
@VisibleForTesting | |||
void execute(Logger logger) { | |||
String defaultName = settings.get(QualityProfiles.SONAR_PROFILE_PROP).orElse(null); | |||
String defaultName = config.get(QualityProfiles.SONAR_PROFILE_PROP).orElse(null); | |||
boolean defaultNameUsed = StringUtils.isBlank(defaultName); | |||
for (String lang : fs.languages()) { | |||
for (String lang : store.getLanguages()) { | |||
QProfile profile = profiles.findByLanguage(lang); | |||
if (profile == null) { | |||
logger.warn("No Quality profile found for language {}", lang); | |||
@@ -64,7 +62,7 @@ public class QProfileVerifier { | |||
} | |||
} | |||
} | |||
if (!defaultNameUsed && !fs.languages().isEmpty()) { | |||
if (!defaultNameUsed && !store.getLanguages().isEmpty()) { | |||
throw MessageException.of("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration."); | |||
} | |||
} |
@@ -22,7 +22,6 @@ package org.sonar.scanner.scan; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.scan.filesystem.FileExclusions; | |||
import org.sonar.core.extension.CoreExtensionsInstaller; | |||
import org.sonar.core.platform.ComponentContainer; | |||
@@ -33,12 +32,6 @@ import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; | |||
import org.sonar.scanner.deprecated.perspectives.ScannerPerspectives; | |||
import org.sonar.scanner.issue.ModuleIssueFilters; | |||
import org.sonar.scanner.issue.ModuleIssues; | |||
import org.sonar.scanner.issue.ignore.EnforceIssuesFilter; | |||
import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.phases.AbstractModulePhaseExecutor; | |||
import org.sonar.scanner.phases.IssuesPhaseExecutor; | |||
import org.sonar.scanner.phases.ModuleCoverageExclusions; | |||
@@ -49,10 +42,6 @@ import org.sonar.scanner.postjob.DefaultPostJobContext; | |||
import org.sonar.scanner.postjob.PostJobOptimizer; | |||
import org.sonar.scanner.rule.QProfileVerifier; | |||
import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; | |||
import org.sonar.scanner.scan.filesystem.ExclusionFilters; | |||
import org.sonar.scanner.scan.filesystem.FileIndexer; | |||
import org.sonar.scanner.scan.filesystem.InputFileBuilder; | |||
import org.sonar.scanner.scan.filesystem.MetadataGenerator; | |||
import org.sonar.scanner.scan.filesystem.ModuleInputComponentStore; | |||
import org.sonar.scanner.scan.report.IssuesReports; | |||
import org.sonar.scanner.sensor.DefaultSensorContext; | |||
@@ -105,13 +94,7 @@ public class ModuleScanContainer extends ComponentContainer { | |||
// file system | |||
ModuleInputComponentStore.class, | |||
FileExclusions.class, | |||
ExclusionFilters.class, | |||
MetadataGenerator.class, | |||
FileMetadata.class, | |||
FileIndexer.class, | |||
InputFileBuilder.class, | |||
DefaultModuleFileSystem.class, | |||
QProfileVerifier.class, | |||
SensorOptimizer.class, | |||
PostJobOptimizer.class, | |||
@@ -126,14 +109,6 @@ public class ModuleScanContainer extends ComponentContainer { | |||
// issues | |||
ModuleIssues.class, | |||
// issue exclusions | |||
IssueInclusionPatternInitializer.class, | |||
IssueExclusionPatternInitializer.class, | |||
PatternMatcher.class, | |||
IssueExclusionsLoader.class, | |||
EnforceIssuesFilter.class, | |||
IgnoreIssuesFilter.class, | |||
// Perspectives | |||
ScannerPerspectives.class, | |||
@@ -22,6 +22,7 @@ package org.sonar.scanner.scan; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.batch.fs.internal.InputModuleHierarchy; | |||
import org.sonar.api.batch.fs.internal.SensorStrategy; | |||
import org.sonar.api.batch.rule.CheckFactory; | |||
@@ -50,6 +51,12 @@ import org.sonar.scanner.deprecated.test.TestPlanBuilder; | |||
import org.sonar.scanner.deprecated.test.TestableBuilder; | |||
import org.sonar.scanner.issue.DefaultProjectIssues; | |||
import org.sonar.scanner.issue.IssueCache; | |||
import org.sonar.scanner.issue.ignore.EnforceIssuesFilter; | |||
import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.issue.tracking.DefaultServerLineHashesLoader; | |||
import org.sonar.scanner.issue.tracking.IssueTransition; | |||
import org.sonar.scanner.issue.tracking.LocalIssueTracking; | |||
@@ -85,6 +92,7 @@ import org.sonar.scanner.rule.ActiveRulesLoader; | |||
import org.sonar.scanner.rule.ActiveRulesProvider; | |||
import org.sonar.scanner.rule.DefaultActiveRulesLoader; | |||
import org.sonar.scanner.rule.DefaultRulesLoader; | |||
import org.sonar.scanner.rule.QProfileVerifier; | |||
import org.sonar.scanner.rule.RulesLoader; | |||
import org.sonar.scanner.rule.RulesProvider; | |||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||
@@ -92,8 +100,12 @@ import org.sonar.scanner.scan.branch.BranchConfigurationProvider; | |||
import org.sonar.scanner.scan.branch.BranchType; | |||
import org.sonar.scanner.scan.branch.ProjectBranchesProvider; | |||
import org.sonar.scanner.scan.branch.ProjectPullRequestsProvider; | |||
import org.sonar.scanner.scan.filesystem.FileIndexer; | |||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||
import org.sonar.scanner.scan.filesystem.LanguageDetection; | |||
import org.sonar.scanner.scan.filesystem.MetadataGenerator; | |||
import org.sonar.scanner.scan.filesystem.ProjectExclusionFilters; | |||
import org.sonar.scanner.scan.filesystem.ProjectFileIndexer; | |||
import org.sonar.scanner.scan.filesystem.ScannerComponentIdGenerator; | |||
import org.sonar.scanner.scan.filesystem.StatusDetection; | |||
import org.sonar.scanner.scan.measure.DefaultMetricFinder; | |||
@@ -165,11 +177,18 @@ public class ProjectScanContainer extends ComponentContainer { | |||
new ScmChangedFilesProvider(), | |||
StatusDetection.class, | |||
LanguageDetection.class, | |||
MetadataGenerator.class, | |||
FileMetadata.class, | |||
FileIndexer.class, | |||
ProjectFileIndexer.class, | |||
ProjectExclusionFilters.class, | |||
// rules | |||
new ActiveRulesProvider(), | |||
new QualityProfilesProvider(), | |||
CheckFactory.class, | |||
QProfileVerifier.class, | |||
// issues | |||
IssueCache.class, | |||
@@ -191,6 +210,14 @@ public class ProjectScanContainer extends ComponentContainer { | |||
// Measures | |||
MeasureCache.class, | |||
// issue exclusions | |||
IssueInclusionPatternInitializer.class, | |||
IssueExclusionPatternInitializer.class, | |||
PatternMatcher.class, | |||
IssueExclusionsLoader.class, | |||
EnforceIssuesFilter.class, | |||
IgnoreIssuesFilter.class, | |||
// context | |||
ContextPropertiesCache.class, | |||
ContextPropertiesPublisher.class, | |||
@@ -290,6 +317,11 @@ public class ProjectScanContainer extends ComponentContainer { | |||
LOG.info("Branch name: {}, type: {}", branchConfig.branchName(), branchTypeToDisplayName(branchConfig.branchType())); | |||
} | |||
getComponentByType(ProjectFileIndexer.class).index(); | |||
// Log detected languages and their profiles after FS is indexed and languages detected | |||
getComponentByType(QProfileVerifier.class).execute(); | |||
LOG.debug("Start recursive analysis of project modules"); | |||
scanRecursively(tree, tree.root(), analysisMode); | |||
@@ -0,0 +1,171 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.filesystem; | |||
import java.nio.file.Path; | |||
import java.util.Arrays; | |||
import java.util.function.Function; | |||
import java.util.stream.Stream; | |||
import org.apache.commons.lang.ArrayUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.PathPattern; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
public abstract class AbstractExclusionFilters { | |||
private static final Logger LOG = Loggers.get(AbstractExclusionFilters.class); | |||
private final String[] sourceInclusions; | |||
private final String[] testInclusions; | |||
private final String[] sourceExclusions; | |||
private final String[] testExclusions; | |||
private PathPattern[] mainInclusionsPattern; | |||
private PathPattern[] mainExclusionsPattern; | |||
private PathPattern[] testInclusionsPattern; | |||
private PathPattern[] testExclusionsPattern; | |||
public AbstractExclusionFilters(Function<String, String[]> configProvider) { | |||
this.sourceInclusions = inclusions(configProvider, CoreProperties.PROJECT_INCLUSIONS_PROPERTY); | |||
this.testInclusions = inclusions(configProvider, CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY); | |||
this.sourceExclusions = exclusions(configProvider, CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY, CoreProperties.PROJECT_EXCLUSIONS_PROPERTY); | |||
this.testExclusions = exclusions(configProvider, CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY, CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY); | |||
this.mainInclusionsPattern = prepareMainInclusions(sourceInclusions); | |||
this.mainExclusionsPattern = prepareMainExclusions(sourceExclusions, testInclusions); | |||
this.testInclusionsPattern = prepareTestInclusions(testInclusions); | |||
this.testExclusionsPattern = prepareTestExclusions(testExclusions); | |||
} | |||
protected void log() { | |||
log("Included sources: ", mainInclusionsPattern); | |||
log("Excluded sources: ", mainExclusionsPattern); | |||
log("Included tests: ", testInclusionsPattern); | |||
log("Excluded tests: ", testExclusionsPattern); | |||
} | |||
private String[] inclusions(Function<String, String[]> configProvider, String propertyKey) { | |||
return Arrays.stream(configProvider.apply(propertyKey)) | |||
.map(StringUtils::trim) | |||
.filter(s -> !"**/*".equals(s)) | |||
.filter(s -> !"file:**/*".equals(s)) | |||
.toArray(String[]::new); | |||
} | |||
private String[] exclusions(Function<String, String[]> configProvider, String globalExclusionsProperty, String exclusionsProperty) { | |||
String[] globalExclusions = configProvider.apply(globalExclusionsProperty); | |||
String[] exclusions = configProvider.apply(exclusionsProperty); | |||
return Stream.concat(Arrays.stream(globalExclusions), Arrays.stream(exclusions)) | |||
.map(StringUtils::trim) | |||
.toArray(String[]::new); | |||
} | |||
public boolean hasPattern() { | |||
return mainInclusionsPattern.length > 0 || mainExclusionsPattern.length > 0 || testInclusionsPattern.length > 0 || testExclusionsPattern.length > 0; | |||
} | |||
private static void log(String title, PathPattern[] patterns) { | |||
if (patterns.length > 0) { | |||
LOG.info(title); | |||
for (PathPattern pattern : patterns) { | |||
LOG.info(" {}", pattern); | |||
} | |||
} | |||
} | |||
public boolean accept(Path absolutePath, Path relativePath, InputFile.Type type) { | |||
PathPattern[] inclusionPatterns; | |||
PathPattern[] exclusionPatterns; | |||
if (InputFile.Type.MAIN == type) { | |||
inclusionPatterns = mainInclusionsPattern; | |||
exclusionPatterns = mainExclusionsPattern; | |||
} else if (InputFile.Type.TEST == type) { | |||
inclusionPatterns = testInclusionsPattern; | |||
exclusionPatterns = testExclusionsPattern; | |||
} else { | |||
throw new IllegalArgumentException("Unknown file type: " + type); | |||
} | |||
if (inclusionPatterns.length > 0) { | |||
boolean matchInclusion = false; | |||
for (PathPattern pattern : inclusionPatterns) { | |||
matchInclusion |= pattern.match(absolutePath, relativePath); | |||
} | |||
if (!matchInclusion) { | |||
return false; | |||
} | |||
} | |||
if (exclusionPatterns.length > 0) { | |||
for (PathPattern pattern : exclusionPatterns) { | |||
if (pattern.match(absolutePath, relativePath)) { | |||
return false; | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
private static PathPattern[] prepareMainInclusions(String[] sourceInclusions) { | |||
if (sourceInclusions.length > 0) { | |||
// User defined params | |||
return PathPattern.create(sourceInclusions); | |||
} | |||
return new PathPattern[0]; | |||
} | |||
private static PathPattern[] prepareTestInclusions(String[] testInclusions) { | |||
return PathPattern.create(testInclusions); | |||
} | |||
static PathPattern[] prepareMainExclusions(String[] sourceExclusions, String[] testInclusions) { | |||
String[] patterns = (String[]) ArrayUtils.addAll( | |||
sourceExclusions, testInclusions); | |||
return PathPattern.create(patterns); | |||
} | |||
private static PathPattern[] prepareTestExclusions(String[] testExclusions) { | |||
return PathPattern.create(testExclusions); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (!(o instanceof AbstractExclusionFilters)) { | |||
return false; | |||
} | |||
AbstractExclusionFilters that = (AbstractExclusionFilters) o; | |||
return Arrays.equals(sourceInclusions, that.sourceInclusions) && | |||
Arrays.equals(testInclusions, that.testInclusions) && | |||
Arrays.equals(sourceExclusions, that.sourceExclusions) && | |||
Arrays.equals(testExclusions, that.testExclusions); | |||
} | |||
@Override | |||
public int hashCode() { | |||
int result = Arrays.hashCode(sourceInclusions); | |||
result = 31 * result + Arrays.hashCode(testInclusions); | |||
result = 31 * result + Arrays.hashCode(sourceExclusions); | |||
result = 31 * result + Arrays.hashCode(testExclusions); | |||
return result; | |||
} | |||
} |
@@ -1,132 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.filesystem; | |||
import java.nio.file.Path; | |||
import org.apache.commons.lang.ArrayUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.PathPattern; | |||
import org.sonar.api.scan.filesystem.FileExclusions; | |||
@ScannerSide | |||
public class ExclusionFilters { | |||
private static final Logger LOG = LoggerFactory.getLogger(ExclusionFilters.class); | |||
private final FileExclusions exclusionSettings; | |||
private PathPattern[] mainInclusions; | |||
private PathPattern[] mainExclusions; | |||
private PathPattern[] testInclusions; | |||
private PathPattern[] testExclusions; | |||
public ExclusionFilters(FileExclusions exclusions) { | |||
this.exclusionSettings = exclusions; | |||
} | |||
public void prepare() { | |||
mainInclusions = prepareMainInclusions(); | |||
mainExclusions = prepareMainExclusions(); | |||
testInclusions = prepareTestInclusions(); | |||
testExclusions = prepareTestExclusions(); | |||
log("Included sources: ", mainInclusions); | |||
log("Excluded sources: ", mainExclusions); | |||
log("Included tests: ", testInclusions); | |||
log("Excluded tests: ", testExclusions); | |||
} | |||
public boolean hasPattern() { | |||
return mainInclusions.length > 0 || mainExclusions.length > 0 || testInclusions.length > 0 || testExclusions.length > 0; | |||
} | |||
private static void log(String title, PathPattern[] patterns) { | |||
if (patterns.length > 0) { | |||
LOG.info(title); | |||
for (PathPattern pattern : patterns) { | |||
LOG.info(" {}", pattern); | |||
} | |||
} | |||
} | |||
public boolean accept(Path absolutePath, Path relativePath, InputFile.Type type) { | |||
PathPattern[] inclusionPatterns; | |||
PathPattern[] exclusionPatterns; | |||
if (InputFile.Type.MAIN == type) { | |||
inclusionPatterns = mainInclusions; | |||
exclusionPatterns = mainExclusions; | |||
} else if (InputFile.Type.TEST == type) { | |||
inclusionPatterns = testInclusions; | |||
exclusionPatterns = testExclusions; | |||
} else { | |||
throw new IllegalArgumentException("Unknown file type: " + type); | |||
} | |||
if (inclusionPatterns.length > 0) { | |||
boolean matchInclusion = false; | |||
for (PathPattern pattern : inclusionPatterns) { | |||
matchInclusion |= pattern.match(absolutePath, relativePath); | |||
} | |||
if (!matchInclusion) { | |||
return false; | |||
} | |||
} | |||
if (exclusionPatterns.length > 0) { | |||
for (PathPattern pattern : exclusionPatterns) { | |||
if (pattern.match(absolutePath, relativePath)) { | |||
return false; | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
PathPattern[] prepareMainInclusions() { | |||
if (exclusionSettings.sourceInclusions().length > 0) { | |||
// User defined params | |||
return PathPattern.create(exclusionSettings.sourceInclusions()); | |||
} | |||
return new PathPattern[0]; | |||
} | |||
PathPattern[] prepareTestInclusions() { | |||
return PathPattern.create(computeTestInclusions()); | |||
} | |||
private String[] computeTestInclusions() { | |||
if (exclusionSettings.testInclusions().length > 0) { | |||
// User defined params | |||
return exclusionSettings.testInclusions(); | |||
} | |||
return ArrayUtils.EMPTY_STRING_ARRAY; | |||
} | |||
PathPattern[] prepareMainExclusions() { | |||
String[] patterns = (String[]) ArrayUtils.addAll( | |||
exclusionSettings.sourceExclusions(), computeTestInclusions()); | |||
return PathPattern.create(patterns); | |||
} | |||
PathPattern[] prepareTestExclusions() { | |||
return PathPattern.create(exclusionSettings.testExclusions()); | |||
} | |||
} |
@@ -19,181 +19,136 @@ | |||
*/ | |||
package org.sonar.scanner.scan.filesystem; | |||
import com.google.common.util.concurrent.ThreadFactoryBuilder; | |||
import java.io.IOException; | |||
import java.nio.file.FileSystemLoopException; | |||
import java.nio.file.FileVisitOption; | |||
import java.nio.file.FileVisitResult; | |||
import java.nio.file.FileVisitor; | |||
import java.nio.file.Files; | |||
import java.nio.file.LinkOption; | |||
import java.nio.file.Path; | |||
import java.nio.file.attribute.BasicFileAttributes; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.concurrent.ExecutionException; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.Future; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.ScannerSide; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.fs.InputFileFilter; | |||
import org.sonar.api.batch.fs.internal.DefaultIndexedFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.SensorStrategy; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
import org.sonar.scanner.scan.ScanProperties; | |||
import org.sonar.scanner.util.ProgressReport; | |||
/** | |||
* Index input files into {@link InputComponentStore}. | |||
*/ | |||
@ScannerSide | |||
public class FileIndexer { | |||
private static final Logger LOG = LoggerFactory.getLogger(FileIndexer.class); | |||
private static final Logger LOG = Loggers.get(FileIndexer.class); | |||
private final AnalysisWarnings analysisWarnings; | |||
private final ScanProperties properties; | |||
private final InputFileFilter[] filters; | |||
private final ExclusionFilters exclusionFilters; | |||
private final InputFileBuilder inputFileBuilder; | |||
private final DefaultInputModule module; | |||
private final ProjectExclusionFilters projectExclusionFilters; | |||
private final IssueExclusionsLoader issueExclusionsLoader; | |||
private final MetadataGenerator metadataGenerator; | |||
private final DefaultInputProject project; | |||
private final ScannerComponentIdGenerator scannerComponentIdGenerator; | |||
private final InputComponentStore componentStore; | |||
private ExecutorService executorService; | |||
private final List<Future<Void>> tasks; | |||
private final DefaultModuleFileSystem defaultModuleFileSystem; | |||
private final SensorStrategy sensorStrategy; | |||
private final LanguageDetection langDetection; | |||
private ProgressReport progressReport; | |||
private boolean warnExclusionsAlreadyLogged; | |||
public FileIndexer(ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, DefaultInputModule module, ExclusionFilters exclusionFilters, | |||
InputFileBuilder inputFileBuilder, DefaultModuleFileSystem defaultModuleFileSystem, | |||
LanguageDetection languageDetection, | |||
InputFileFilter[] filters) { | |||
public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, ProjectExclusionFilters projectExclusionFilters, | |||
IssueExclusionsLoader issueExclusionsLoader, | |||
MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, | |||
LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties, | |||
InputFileFilter[] filters) { | |||
this.project = project; | |||
this.scannerComponentIdGenerator = scannerComponentIdGenerator; | |||
this.componentStore = componentStore; | |||
this.module = module; | |||
this.inputFileBuilder = inputFileBuilder; | |||
this.defaultModuleFileSystem = defaultModuleFileSystem; | |||
this.issueExclusionsLoader = issueExclusionsLoader; | |||
this.metadataGenerator = metadataGenerator; | |||
this.sensorStrategy = sensorStrategy; | |||
this.langDetection = languageDetection; | |||
this.analysisWarnings = analysisWarnings; | |||
this.properties = properties; | |||
this.filters = filters; | |||
this.exclusionFilters = exclusionFilters; | |||
this.tasks = new ArrayList<>(); | |||
this.projectExclusionFilters = projectExclusionFilters; | |||
} | |||
public FileIndexer(ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, DefaultInputModule module, ExclusionFilters exclusionFilters, | |||
InputFileBuilder inputFileBuilder, DefaultModuleFileSystem defaultModuleFileSystem, | |||
LanguageDetection languageDetection) { | |||
this(scannerComponentIdGenerator, componentStore, module, exclusionFilters, inputFileBuilder, defaultModuleFileSystem, languageDetection, | |||
new InputFileFilter[0]); | |||
public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, ProjectExclusionFilters projectExclusionFilters, | |||
IssueExclusionsLoader issueExclusionsLoader, | |||
MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties) { | |||
this(project, scannerComponentIdGenerator, componentStore, projectExclusionFilters, issueExclusionsLoader, metadataGenerator, sensorStrategy, languageDetection, analysisWarnings, | |||
properties, new InputFileFilter[0]); | |||
} | |||
public void index() { | |||
int threads = Math.max(1, Runtime.getRuntime().availableProcessors() - 1); | |||
this.executorService = Executors.newFixedThreadPool(threads, new ThreadFactoryBuilder() | |||
.setNameFormat("FileIndexer-%d") | |||
.setDaemon(true) | |||
.build()); | |||
progressReport = new ProgressReport("Report about progress of file indexation", TimeUnit.SECONDS.toMillis(10)); | |||
progressReport.start("Index files"); | |||
exclusionFilters.prepare(); | |||
Progress progress = new Progress(); | |||
indexFiles(module.getSourceDirsOrFiles(), InputFile.Type.MAIN, progress); | |||
indexFiles(module.getTestDirsOrFiles(), InputFile.Type.TEST, progress); | |||
waitForTasksToComplete(progressReport); | |||
progressReport.stop(progress.count() + " " + pluralizeFiles(progress.count()) + " indexed"); | |||
if (exclusionFilters.hasPattern()) { | |||
LOG.info("{} {} ignored because of inclusion/exclusion patterns", progress.excludedByPatternsCount(), pluralizeFiles(progress.excludedByPatternsCount())); | |||
} | |||
} | |||
private void waitForTasksToComplete(ProgressReport report) { | |||
executorService.shutdown(); | |||
for (Future<Void> task : tasks) { | |||
try { | |||
task.get(); | |||
} catch (ExecutionException e) { | |||
// Unwrap ExecutionException | |||
stopAsap(report); | |||
throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause() : new IllegalStateException(e.getCause()); | |||
} catch (InterruptedException e) { | |||
stopAsap(report); | |||
throw new IllegalStateException(e); | |||
} | |||
} | |||
} | |||
private void stopAsap(ProgressReport report) { | |||
report.stop(null); | |||
executorService.shutdownNow(); | |||
try { | |||
executorService.awaitTermination(5, TimeUnit.SECONDS); | |||
} catch (InterruptedException e1) { | |||
// ignore, what's important is the original exception | |||
} | |||
} | |||
private static String pluralizeFiles(int count) { | |||
return count == 1 ? "file" : "files"; | |||
} | |||
private void indexFiles(List<Path> sources, InputFile.Type type, Progress progress) { | |||
try { | |||
for (Path dirOrFile : sources) { | |||
if (dirOrFile.toFile().isDirectory()) { | |||
indexDirectory(dirOrFile, type, progress); | |||
} else { | |||
tasks.add(executorService.submit(() -> indexFile(dirOrFile, type, progress))); | |||
} | |||
} | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to index files", e); | |||
} | |||
} | |||
private void indexDirectory(Path dirToIndex, InputFile.Type type, Progress progress) throws IOException { | |||
Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, | |||
new IndexFileVisitor(type, progress)); | |||
} | |||
private Void indexFile(Path sourceFile, InputFile.Type type, Progress progress) throws IOException { | |||
public void indexFile(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Path sourceFile, InputFile.Type type, ProgressReport progressReport, | |||
AtomicInteger excludedByPatternsCount) | |||
throws IOException { | |||
// get case of real file without resolving link | |||
Path realAbsoluteFile = sourceFile.toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize(); | |||
if (!realAbsoluteFile.startsWith(module.getBaseDir())) { | |||
LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", realAbsoluteFile.toAbsolutePath(), module.getBaseDir()); | |||
return null; | |||
if (!realAbsoluteFile.startsWith(project.getBaseDir())) { | |||
LOG.warn("File '{}' is ignored. It is not located in project basedir '{}'.", realAbsoluteFile.toAbsolutePath(), project.getBaseDir()); | |||
return; | |||
} | |||
Path relativePath = module.getBaseDir().relativize(realAbsoluteFile); | |||
if (!exclusionFilters.accept(realAbsoluteFile, relativePath, type)) { | |||
progress.increaseExcludedByPatternsCount(); | |||
return null; | |||
Path projectRelativePath = project.getBaseDir().relativize(realAbsoluteFile); | |||
Path moduleRelativePath = module.getBaseDir().relativize(realAbsoluteFile); | |||
if (!projectExclusionFilters.accept(realAbsoluteFile, projectRelativePath, type)) { | |||
excludedByPatternsCount.incrementAndGet(); | |||
return; | |||
} | |||
if (!moduleExclusionFilters.accept(realAbsoluteFile, moduleRelativePath, type)) { | |||
if (projectExclusionFilters.equals(moduleExclusionFilters)) { | |||
warnOnce("File '" + projectRelativePath + "' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " + | |||
"Please update file inclusion/exclusion configuration so that patterns refer to project relative paths."); | |||
} else { | |||
warnOnce("Defining inclusion/exclusions at module level is deprecated. " + | |||
"Move file inclusion/exclusion configuration from module '" + module.getName() + "' " + | |||
"to the root project and update patterns to refer to project relative paths."); | |||
} | |||
excludedByPatternsCount.incrementAndGet(); | |||
return; | |||
} | |||
String language = langDetection.language(realAbsoluteFile, relativePath); | |||
String language = langDetection.language(realAbsoluteFile, projectRelativePath); | |||
if (language == null && langDetection.getForcedLanguage() != null) { | |||
LOG.warn("File '{}' is ignored because it doesn't belong to the forced language '{}'", realAbsoluteFile.toAbsolutePath(), langDetection.getForcedLanguage()); | |||
return null; | |||
return; | |||
} | |||
DefaultIndexedFile indexedFile = new DefaultIndexedFile(realAbsoluteFile, project.key(), | |||
projectRelativePath.toString(), | |||
moduleRelativePath.toString(), | |||
type, language, scannerComponentIdGenerator.getAsInt(), sensorStrategy); | |||
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(module.getKeyWithBranch(), f, module.getEncoding())); | |||
if (language != null) { | |||
inputFile.setPublished(true); | |||
} | |||
DefaultInputFile inputFile = inputFileBuilder.create(type, realAbsoluteFile, language); | |||
if (!accept(inputFile)) { | |||
progress.increaseExcludedByPatternsCount(); | |||
return null; | |||
return; | |||
} | |||
if (componentStore.getFile(inputFile.getProjectRelativePath()) != null) { | |||
throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce " | |||
+ "disjoint sets for main and test files"); | |||
} | |||
synchronized (this) { | |||
progress.markAsIndexed(inputFile); | |||
defaultModuleFileSystem.add(inputFile); | |||
componentStore.put(module.key(), inputFile); | |||
if (issueExclusionsLoader.shouldExecute()) { | |||
issueExclusionsLoader.addMulticriteriaPatterns(inputFile.getProjectRelativePath(), inputFile.key()); | |||
} | |||
LOG.debug("'{}' indexed {}with language '{}'", projectRelativePath, type == Type.TEST ? "as test " : "", inputFile.language()); | |||
if (properties.preloadFileMetadata()) { | |||
inputFile.checkMetadata(); | |||
} | |||
int count = componentStore.allFiles().size(); | |||
progressReport.message(count + " " + pluralizeFiles(count) + " indexed... (last one was " + inputFile.getProjectRelativePath() + ")"); | |||
} | |||
private void warnOnce(String msg) { | |||
if (!warnExclusionsAlreadyLogged) { | |||
LOG.warn(msg); | |||
analysisWarnings.addUnique(msg); | |||
warnExclusionsAlreadyLogged = true; | |||
} | |||
LOG.debug("'{}' indexed {}with language '{}'", relativePath, type == Type.TEST ? "as test " : "", inputFile.language()); | |||
inputFileBuilder.checkMetadata(inputFile); | |||
return null; | |||
} | |||
private boolean accept(InputFile indexedFile) { | |||
@@ -207,76 +162,8 @@ public class FileIndexer { | |||
return true; | |||
} | |||
private class IndexFileVisitor implements FileVisitor<Path> { | |||
private final Progress status; | |||
private final Type type; | |||
IndexFileVisitor(InputFile.Type type, Progress status) { | |||
this.status = status; | |||
this.type = type; | |||
} | |||
@Override | |||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { | |||
Path fileName = dir.getFileName(); | |||
if (fileName != null && fileName.toString().length() > 1 && fileName.toString().charAt(0) == '.') { | |||
return FileVisitResult.SKIP_SUBTREE; | |||
} | |||
if (Files.isHidden(dir)) { | |||
return FileVisitResult.SKIP_SUBTREE; | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | |||
if (!Files.isHidden(file)) { | |||
tasks.add(executorService.submit(() -> indexFile(file, type, status))); | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { | |||
if (exc instanceof FileSystemLoopException) { | |||
LOG.warn("Not indexing due to symlink loop: {}", file.toFile()); | |||
return FileVisitResult.CONTINUE; | |||
} | |||
throw exc; | |||
} | |||
@Override | |||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { | |||
return FileVisitResult.CONTINUE; | |||
} | |||
} | |||
private class Progress { | |||
private AtomicInteger indexedCount = new AtomicInteger(0); | |||
private AtomicInteger excludedByPatternsCount = new AtomicInteger(0); | |||
void markAsIndexed(DefaultInputFile inputFile) { | |||
if (componentStore.getFile(inputFile.getProjectRelativePath()) != null) { | |||
throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce " | |||
+ "disjoint sets for main and test files"); | |||
} | |||
int count = indexedCount.incrementAndGet(); | |||
progressReport.message(count + " " + pluralizeFiles(count) + " indexed... (last one was " + inputFile.getProjectRelativePath() + ")"); | |||
} | |||
void increaseExcludedByPatternsCount() { | |||
excludedByPatternsCount.incrementAndGet(); | |||
} | |||
public int excludedByPatternsCount() { | |||
return excludedByPatternsCount.get(); | |||
} | |||
int count() { | |||
return indexedCount.get(); | |||
} | |||
private static String pluralizeFiles(int count) { | |||
return count == 1 ? "file" : "files"; | |||
} | |||
} |
@@ -37,7 +37,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 org.sonar.api.batch.fs.internal.FileExtensionPredicate; | |||
import org.sonar.api.scanner.fs.InputProject; | |||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||
/** | |||
@@ -55,11 +54,9 @@ public class InputComponentStore { | |||
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 final InputProject project; | |||
private final BranchConfiguration branchConfiguration; | |||
public InputComponentStore(InputProject project, BranchConfiguration branchConfiguration) { | |||
this.project = project; | |||
public InputComponentStore(BranchConfiguration branchConfiguration) { | |||
this.branchConfiguration = branchConfiguration; | |||
} | |||
@@ -83,7 +80,7 @@ public class InputComponentStore { | |||
::iterator; | |||
} | |||
public Iterable<InputFile> allFiles() { | |||
public Collection<InputFile> allFiles() { | |||
return globalInputFileCache.values(); | |||
} | |||
@@ -1,71 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.filesystem; | |||
import java.nio.file.Path; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultIndexedFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.SensorStrategy; | |||
import org.sonar.scanner.scan.ScanProperties; | |||
public class InputFileBuilder { | |||
private final DefaultInputProject project; | |||
private final DefaultInputModule module; | |||
private final ScannerComponentIdGenerator idGenerator; | |||
private final MetadataGenerator metadataGenerator; | |||
private final boolean preloadMetadata; | |||
private final Path projectBaseDir; | |||
private final SensorStrategy sensorStrategy; | |||
public InputFileBuilder(DefaultInputProject project, DefaultInputModule module, MetadataGenerator metadataGenerator, | |||
ScannerComponentIdGenerator idGenerator, ScanProperties properties, | |||
SensorStrategy sensorStrategy) { | |||
this.sensorStrategy = sensorStrategy; | |||
this.projectBaseDir = project.getBaseDir(); | |||
this.project = project; | |||
this.module = module; | |||
this.metadataGenerator = metadataGenerator; | |||
this.idGenerator = idGenerator; | |||
this.preloadMetadata = properties.preloadFileMetadata(); | |||
} | |||
DefaultInputFile create(InputFile.Type type, Path absolutePath, @Nullable String language) { | |||
DefaultIndexedFile indexedFile = new DefaultIndexedFile(absolutePath, project.key(), | |||
projectBaseDir.relativize(absolutePath).toString(), | |||
module.getBaseDir().relativize(absolutePath).toString(), | |||
type, language, idGenerator.getAsInt(), sensorStrategy); | |||
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(f, module.getEncoding())); | |||
if (language != null) { | |||
inputFile.setPublished(true); | |||
} | |||
return inputFile; | |||
} | |||
void checkMetadata(DefaultInputFile inputFile) { | |||
if (preloadMetadata) { | |||
inputFile.checkMetadata(); | |||
} | |||
} | |||
} |
@@ -22,17 +22,16 @@ package org.sonar.scanner.scan.filesystem; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import java.io.InputStream; | |||
import java.nio.charset.Charset; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.batch.fs.internal.Metadata; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; | |||
public class MetadataGenerator { | |||
private static final Logger LOG = LoggerFactory.getLogger(MetadataGenerator.class); | |||
private static final Logger LOG = Loggers.get(MetadataGenerator.class); | |||
@VisibleForTesting | |||
static final Charset UTF_32BE = Charset.forName("UTF-32BE"); | |||
@@ -41,11 +40,9 @@ public class MetadataGenerator { | |||
private final StatusDetection statusDetection; | |||
private final FileMetadata fileMetadata; | |||
private final DefaultInputModule inputModule; | |||
private final IssueExclusionsLoader exclusionsScanner; | |||
public MetadataGenerator(DefaultInputModule inputModule, StatusDetection statusDetection, FileMetadata fileMetadata, IssueExclusionsLoader exclusionsScanner) { | |||
this.inputModule = inputModule; | |||
public MetadataGenerator(StatusDetection statusDetection, FileMetadata fileMetadata, IssueExclusionsLoader exclusionsScanner) { | |||
this.statusDetection = statusDetection; | |||
this.fileMetadata = fileMetadata; | |||
this.exclusionsScanner = exclusionsScanner; | |||
@@ -55,7 +52,7 @@ public class MetadataGenerator { | |||
* Sets all metadata in the file, including charset and status. | |||
* It is an expensive computation, reading the entire file. | |||
*/ | |||
public void setMetadata(final DefaultInputFile inputFile, Charset defaultEncoding) { | |||
public void setMetadata(String moduleKeyWithBranch, final DefaultInputFile inputFile, Charset defaultEncoding) { | |||
CharsetDetector charsetDetector = new CharsetDetector(inputFile.path(), defaultEncoding); | |||
try { | |||
Charset charset; | |||
@@ -69,8 +66,8 @@ public class MetadataGenerator { | |||
inputFile.setCharset(charset); | |||
Metadata metadata = fileMetadata.readMetadata(is, charset, inputFile.absolutePath(), exclusionsScanner.createCharHandlerFor(inputFile.key())); | |||
inputFile.setMetadata(metadata); | |||
inputFile.setStatus(statusDetection.status(inputModule.definition().getKeyWithBranch(), inputFile, metadata.hash())); | |||
LOG.debug("'{}' generated metadata {} with charset '{}'", inputFile, inputFile.type() == Type.TEST ? "as test " : "", charset); | |||
inputFile.setStatus(statusDetection.status(moduleKeyWithBranch, inputFile, metadata.hash())); | |||
LOG.debug("'{}' generated metadata{} with charset '{}'", inputFile, inputFile.type() == Type.TEST ? " as test " : "", charset); | |||
} catch (Exception e) { | |||
throw new IllegalStateException(e); | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.filesystem; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import static org.sonar.api.config.internal.MultivalueProperty.parseAsCsv; | |||
public class ModuleExclusionFilters extends AbstractExclusionFilters { | |||
public ModuleExclusionFilters(DefaultInputModule module) { | |||
super(k -> { | |||
String value = module.properties().get(k); | |||
return value != null ? parseAsCsv(k, value) : new String[0]; | |||
}); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.filesystem; | |||
import org.sonar.api.config.Configuration; | |||
public class ProjectExclusionFilters extends AbstractExclusionFilters { | |||
public ProjectExclusionFilters(Configuration projectConfig) { | |||
super(projectConfig::getStringArray); | |||
log(); | |||
} | |||
} |
@@ -0,0 +1,168 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.filesystem; | |||
import java.io.IOException; | |||
import java.nio.file.FileSystemLoopException; | |||
import java.nio.file.FileVisitOption; | |||
import java.nio.file.FileVisitResult; | |||
import java.nio.file.FileVisitor; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.attribute.BasicFileAttributes; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.InputModuleHierarchy; | |||
import org.sonar.scanner.util.ProgressReport; | |||
/** | |||
* Index project input files into {@link InputComponentStore}. | |||
*/ | |||
public class ProjectFileIndexer { | |||
private static final Logger LOG = LoggerFactory.getLogger(ProjectFileIndexer.class); | |||
private final AbstractExclusionFilters projectExclusionFilters; | |||
private final DefaultInputProject project; | |||
private final InputComponentStore componentStore; | |||
private final InputModuleHierarchy inputModuleHierarchy; | |||
private final FileIndexer fileIndexer; | |||
private ProgressReport progressReport; | |||
public ProjectFileIndexer(DefaultInputProject project, InputComponentStore componentStore, AbstractExclusionFilters exclusionFilters, | |||
InputModuleHierarchy inputModuleHierarchy, | |||
FileIndexer fileIndexer) { | |||
this.project = project; | |||
this.componentStore = componentStore; | |||
this.inputModuleHierarchy = inputModuleHierarchy; | |||
this.fileIndexer = fileIndexer; | |||
this.projectExclusionFilters = exclusionFilters; | |||
} | |||
public void index() { | |||
progressReport = new ProgressReport("Report about progress of file indexation", TimeUnit.SECONDS.toMillis(10)); | |||
progressReport.start("Index files"); | |||
AtomicInteger excludedByPatternsCount = new AtomicInteger(0); | |||
indexModulesRecursively(inputModuleHierarchy.root(), excludedByPatternsCount); | |||
int totalIndexed = componentStore.allFiles().size(); | |||
progressReport.stop(totalIndexed + " " + pluralizeFiles(totalIndexed) + " indexed"); | |||
if (projectExclusionFilters.hasPattern()) { | |||
int excludedFileCount = excludedByPatternsCount.get(); | |||
LOG.info("{} {} ignored because of inclusion/exclusion patterns", excludedFileCount, pluralizeFiles(excludedFileCount)); | |||
} | |||
} | |||
private void indexModulesRecursively(DefaultInputModule module, AtomicInteger excludedByPatternsCount) { | |||
inputModuleHierarchy.children(module).forEach(m -> indexModulesRecursively(m, excludedByPatternsCount)); | |||
index(module, excludedByPatternsCount); | |||
} | |||
private void index(DefaultInputModule module, AtomicInteger excludedByPatternsCount) { | |||
ModuleExclusionFilters moduleExclusionFilters = new ModuleExclusionFilters(module); | |||
indexFiles(module, moduleExclusionFilters, module.getSourceDirsOrFiles(), Type.MAIN, excludedByPatternsCount); | |||
indexFiles(module, moduleExclusionFilters, module.getTestDirsOrFiles(), Type.TEST, excludedByPatternsCount); | |||
} | |||
private static String pluralizeFiles(int count) { | |||
return count == 1 ? "file" : "files"; | |||
} | |||
private void indexFiles(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, List<Path> sources, Type type, AtomicInteger excludedByPatternsCount) { | |||
try { | |||
for (Path dirOrFile : sources) { | |||
if (dirOrFile.toFile().isDirectory()) { | |||
indexDirectory(module, moduleExclusionFilters, dirOrFile, type, excludedByPatternsCount); | |||
} else { | |||
fileIndexer.indexFile(module, moduleExclusionFilters, dirOrFile, type, progressReport, excludedByPatternsCount); | |||
} | |||
} | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to index files", e); | |||
} | |||
} | |||
private void indexDirectory(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Path dirToIndex, Type type, AtomicInteger excludedByPatternsCount) | |||
throws IOException { | |||
Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, | |||
new IndexFileVisitor(module, moduleExclusionFilters, type, excludedByPatternsCount)); | |||
} | |||
private class IndexFileVisitor implements FileVisitor<Path> { | |||
private final DefaultInputModule module; | |||
private final AbstractExclusionFilters moduleExclusionFilters; | |||
private final Type type; | |||
private final AtomicInteger excludedByPatternsCount; | |||
IndexFileVisitor(DefaultInputModule module, AbstractExclusionFilters moduleExclusionFilters, Type type, AtomicInteger excludedByPatternsCount) { | |||
this.module = module; | |||
this.moduleExclusionFilters = moduleExclusionFilters; | |||
this.type = type; | |||
this.excludedByPatternsCount = excludedByPatternsCount; | |||
} | |||
@Override | |||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { | |||
Path fileName = dir.getFileName(); | |||
if (fileName != null && fileName.toString().length() > 1 && fileName.toString().charAt(0) == '.') { | |||
return FileVisitResult.SKIP_SUBTREE; | |||
} | |||
if (Files.isHidden(dir)) { | |||
return FileVisitResult.SKIP_SUBTREE; | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | |||
if (!Files.isHidden(file)) { | |||
fileIndexer.indexFile(module, moduleExclusionFilters, file, type, progressReport, excludedByPatternsCount); | |||
} | |||
return FileVisitResult.CONTINUE; | |||
} | |||
@Override | |||
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { | |||
if (exc instanceof FileSystemLoopException) { | |||
LOG.warn("Not indexing due to symlink loop: {}", file.toFile()); | |||
return FileVisitResult.CONTINUE; | |||
} | |||
throw exc; | |||
} | |||
@Override | |||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { | |||
return FileVisitResult.CONTINUE; | |||
} | |||
} | |||
} |
@@ -90,7 +90,7 @@ public class CpdExecutorTest { | |||
when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir)); | |||
DefaultInputProject project = TestInputFileBuilder.newDefaultInputProject("foo", baseDir); | |||
componentStore = new InputComponentStore(project, mock(BranchConfiguration.class)); | |||
componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
executor = new CpdExecutor(settings, index, publisher, componentStore, executorService); | |||
reader = new ScannerReportReader(outputDir); | |||
@@ -37,13 +37,15 @@ import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||
import org.sonar.scanner.mediumtest.LogOutputRecorder; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.xoo.XooPlugin; | |||
import org.sonar.xoo.global.GlobalSensor; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
import static java.util.stream.Collectors.joining; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assume.assumeTrue; | |||
@@ -55,14 +57,14 @@ public class FileSystemMediumTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private LogOutputRecorder logs = new LogOutputRecorder(); | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
@Rule | |||
public ScannerMediumTester tester = new ScannerMediumTester() | |||
.registerPlugin("xoo", new XooPlugin()) | |||
.addDefaultQProfile("xoo", "Sonar Way") | |||
.addDefaultQProfile("xoo2", "Sonar Way") | |||
.setLogOutput(logs); | |||
.addDefaultQProfile("xoo2", "Sonar Way"); | |||
private File baseDir; | |||
private ImmutableMap.Builder<String, String> builder; | |||
@@ -137,9 +139,9 @@ public class FileSystemMediumTest { | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("Project key: com.foo.project"); | |||
assertThat(logs.getAllAsString()).contains("Organization key: my org"); | |||
assertThat(logs.getAllAsString()).doesNotContain("Branch key"); | |||
assertThat(logTester.logs()).contains("Project key: com.foo.project"); | |||
assertThat(logTester.logs()).contains("Organization key: my org"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("Branch key"); | |||
} | |||
@Test | |||
@@ -158,9 +160,9 @@ public class FileSystemMediumTest { | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("Project key: com.foo.project"); | |||
assertThat(logs.getAllAsString()).contains("Branch key: my-branch"); | |||
assertThat(logs.getAllAsString()).contains("The use of \"sonar.branch\" is deprecated and replaced by \"sonar.branch.name\"."); | |||
assertThat(logTester.logs()).contains("Project key: com.foo.project"); | |||
assertThat(logTester.logs()).contains("Branch key: my-branch"); | |||
assertThat(logTester.logs()).contains("The use of \"sonar.branch\" is deprecated and replaced by \"sonar.branch.name\". See https://redirect.sonarsource.com/doc/branches.html."); | |||
} | |||
@Test | |||
@@ -197,9 +199,9 @@ public class FileSystemMediumTest { | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("Project key: com.foo.project"); | |||
assertThat(logs.getAllAsString()).doesNotContain("Organization key"); | |||
assertThat(logs.getAllAsString()).doesNotContain("Branch key"); | |||
assertThat(logTester.logs()).contains("Project key: com.foo.project"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("Organization key"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("Branch key"); | |||
} | |||
@Test | |||
@@ -215,15 +217,17 @@ public class FileSystemMediumTest { | |||
File javaFile = new File(srcDir, "sample.java"); | |||
FileUtils.write(javaFile, "Sample xoo\ncontent"); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("2 files indexed"); | |||
assertThat(logs.getAllAsString()).contains("'src/sample.xoo' generated metadata"); | |||
assertThat(logs.getAllAsString()).doesNotContain("'src/sample.java' generated metadata"); | |||
assertThat(logTester.logs()).contains("2 files indexed"); | |||
assertThat(logTester.logs()).contains("'src/sample.xoo' generated metadata with charset 'UTF-8'"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("'src/sample.java' generated metadata"); | |||
} | |||
@@ -241,15 +245,17 @@ public class FileSystemMediumTest { | |||
File javaFile = new File(srcDir, "sample.java"); | |||
FileUtils.write(javaFile, "Sample xoo\ncontent"); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("2 files indexed"); | |||
assertThat(logs.getAllAsString()).contains("'src/sample.xoo' generated metadata"); | |||
assertThat(logs.getAllAsString()).contains("'src/sample.java' generated metadata"); | |||
assertThat(logTester.logs()).contains("2 files indexed"); | |||
assertThat(logTester.logs()).contains("'src/sample.xoo' generated metadata with charset 'UTF-8'"); | |||
assertThat(logTester.logs()).contains("'src/sample.java' generated metadata with charset 'UTF-8'"); | |||
} | |||
@Test | |||
@@ -271,6 +277,8 @@ public class FileSystemMediumTest { | |||
Path javaFile = mainDir.resolve("sample.java"); | |||
Files.write(javaFile, "Sample xoo\ncontent".getBytes(StandardCharsets.UTF_8)); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
AnalysisResult result = tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src/main") | |||
@@ -278,10 +286,10 @@ public class FileSystemMediumTest { | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("3 files indexed"); | |||
assertThat(logs.getAllAsString()).contains("'src/main/sample.xoo' generated metadata"); | |||
assertThat(logs.getAllAsString()).doesNotContain("'src/main/sample.java' generated metadata"); | |||
assertThat(logs.getAllAsString()).doesNotContain("'src/test/sample.java' generated metadata"); | |||
assertThat(logTester.logs()).contains("3 files indexed"); | |||
assertThat(logTester.logs()).contains("'src/main/sample.xoo' generated metadata with charset 'UTF-8'"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("'src/main/sample.java' generated metadata"); | |||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("'src/test/sample.java' generated metadata"); | |||
DefaultInputFile javaInputFile = (DefaultInputFile) result.inputFile("src/main/sample.java"); | |||
thrown.expect(IllegalStateException.class); | |||
@@ -304,15 +312,17 @@ public class FileSystemMediumTest { | |||
File xooFile = new File(srcDir, "sample.unknown"); | |||
FileUtils.write(xooFile, "Sample xoo\ncontent"); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
AnalysisResult result = tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).contains("1 file indexed"); | |||
assertThat(logs.getAllAsString()).contains("'src" + File.separator + "sample.unknown' indexed with language 'null'"); | |||
assertThat(logs.getAllAsString()).contains("'src/sample.unknown' generated metadata"); | |||
assertThat(logTester.logs()).contains("1 file indexed"); | |||
assertThat(logTester.logs()).contains("'src" + File.separator + "sample.unknown' indexed with language 'null'"); | |||
assertThat(logTester.logs()).contains("'src/sample.unknown' generated metadata with charset 'UTF-8'"); | |||
DefaultInputFile inputFile = (DefaultInputFile) result.inputFile("src/sample.unknown"); | |||
assertThat(result.getReportComponent(inputFile)).isNotNull(); | |||
} | |||
@@ -337,15 +347,17 @@ public class FileSystemMediumTest { | |||
new Random().nextBytes(b); | |||
FileUtils.writeByteArrayToFile(unknownFile, b); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).containsOnlyOnce("'src" + File.separator + "myfile.binary' indexed with language 'null'"); | |||
assertThat(logs.getAllAsString()).doesNotContain("'src/myfile.binary' generating issue exclusions"); | |||
assertThat(logs.getAllAsString()).containsOnlyOnce("'src/sample.xoo' generating issue exclusions"); | |||
assertThat(logTester.logs()).containsOnlyOnce("'src" + File.separator + "myfile.binary' indexed with language 'null'"); | |||
assertThat(logTester.logs()).doesNotContain("'src/myfile.binary' generating issue exclusions"); | |||
assertThat(logTester.logs()).containsOnlyOnce("'src/sample.xoo' generating issue exclusions"); | |||
} | |||
@Test | |||
@@ -363,14 +375,16 @@ public class FileSystemMediumTest { | |||
File unknownFile = new File(srcDir, "myfile.binary"); | |||
FileUtils.write(unknownFile, "some text"); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
tester.newAnalysis() | |||
.properties(builder | |||
.put("sonar.sources", "src") | |||
.build()) | |||
.execute(); | |||
assertThat(logs.getAllAsString()).containsOnlyOnce("- Exclusion pattern 'pattern'"); | |||
assertThat(logs.getAllAsString()).containsOnlyOnce("'src/myfile.binary' generating issue exclusions"); | |||
assertThat(logTester.logs()).containsOnlyOnce("- Exclusion pattern 'pattern': every issue in this file will be ignored."); | |||
assertThat(logTester.logs()).containsOnlyOnce("'src/myfile.binary' generating issue exclusions"); | |||
} | |||
@Test | |||
@@ -525,6 +539,84 @@ public class FileSystemMediumTest { | |||
assertThat(result.inputFiles()).hasSize(2); | |||
} | |||
@Test | |||
public void warn_user_for_outdated_inherited_exclusions_for_multi_module_project() throws IOException { | |||
File baseDir = temp.getRoot(); | |||
File baseDirModuleA = new File(baseDir, "moduleA"); | |||
File baseDirModuleB = new File(baseDir, "moduleB"); | |||
File srcDirA = new File(baseDirModuleA, "src"); | |||
srcDirA.mkdirs(); | |||
File srcDirB = new File(baseDirModuleB, "src"); | |||
srcDirB.mkdirs(); | |||
File xooFileA = new File(srcDirA, "sample.xoo"); | |||
FileUtils.write(xooFileA, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||
File xooFileB = new File(srcDirB, "sample.xoo"); | |||
FileUtils.write(xooFileB, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||
AnalysisResult result = tester.newAnalysis() | |||
.properties(ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||
.put("sonar.projectKey", "com.foo.project") | |||
.put("sonar.sources", "src") | |||
.put("sonar.modules", "moduleA,moduleB") | |||
.put("sonar.exclusions", "src/sample.xoo") | |||
.build()) | |||
.execute(); | |||
InputFile fileA = result.inputFile("moduleA/src/sample.xoo"); | |||
assertThat(fileA).isNull(); | |||
InputFile fileB = result.inputFile("moduleB/src/sample.xoo"); | |||
assertThat(fileB).isNull(); | |||
assertThat(logTester.logs(LoggerLevel.WARN)) | |||
.contains("File 'moduleB/src/sample.xoo' was excluded because patterns are still evaluated using module relative paths but this is deprecated. " + | |||
"Please update file inclusion/exclusion configuration so that patterns refer to project relative paths."); | |||
} | |||
@Test | |||
public void warn_user_for_outdated_module_exclusions_for_multi_module_project() throws IOException { | |||
File baseDir = temp.getRoot(); | |||
File baseDirModuleA = new File(baseDir, "moduleA"); | |||
File baseDirModuleB = new File(baseDir, "moduleB"); | |||
File srcDirA = new File(baseDirModuleA, "src"); | |||
srcDirA.mkdirs(); | |||
File srcDirB = new File(baseDirModuleB, "src"); | |||
srcDirB.mkdirs(); | |||
File xooFileA = new File(srcDirA, "sample.xoo"); | |||
FileUtils.write(xooFileA, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||
File xooFileB = new File(srcDirB, "sample.xoo"); | |||
FileUtils.write(xooFileB, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||
AnalysisResult result = tester.newAnalysis() | |||
.properties(ImmutableMap.<String, String>builder() | |||
.put("sonar.task", "scan") | |||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||
.put("sonar.projectKey", "com.foo.project") | |||
.put("sonar.sources", "src") | |||
.put("sonar.modules", "moduleA,moduleB") | |||
.put("moduleB.sonar.exclusions", "src/sample.xoo") | |||
.build()) | |||
.execute(); | |||
InputFile fileA = result.inputFile("moduleA/src/sample.xoo"); | |||
assertThat(fileA).isNotNull(); | |||
InputFile fileB = result.inputFile("moduleB/src/sample.xoo"); | |||
assertThat(fileB).isNull(); | |||
assertThat(logTester.logs(LoggerLevel.WARN)) | |||
.contains("Defining inclusion/exclusions at module level is deprecated. " + | |||
"Move file inclusion/exclusion configuration from module 'moduleB' to the root project and update patterns to refer to project relative paths."); | |||
} | |||
@Test | |||
public void failForDuplicateInputFile() throws IOException { | |||
File srcDir = new File(baseDir, "src"); | |||
@@ -639,7 +731,7 @@ public class FileSystemMediumTest { | |||
.execute(); | |||
assertThat(result.inputFiles()).hasSize(4); | |||
assertThat(logs.get("INFO")).contains( | |||
assertThat(logTester.logs(LoggerLevel.INFO)).contains( | |||
"Global Sensor: module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo", | |||
"Global Sensor: module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo", | |||
"Global Sensor: module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo", |
@@ -26,7 +26,6 @@ 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.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.batch.postjob.issue.PostJobIssue; | |||
import org.sonar.api.batch.rule.Severity; | |||
@@ -54,8 +53,7 @@ public class DefaultPostJobContextTest { | |||
@Before | |||
public void setUp() throws IOException { | |||
issueCache = mock(IssueCache.class); | |||
DefaultInputProject project = TestInputFileBuilder.newDefaultInputProject("foo", temp.newFolder()); | |||
componentStore = new InputComponentStore(project, mock(BranchConfiguration.class)); | |||
componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
settings = new MapSettings(); | |||
analysisMode = mock(AnalysisMode.class); | |||
context = new DefaultPostJobContext(settings.asConfig(), settings, issueCache, componentStore, analysisMode); |
@@ -85,7 +85,7 @@ public class ComponentsPublisherTest { | |||
.setWorkDir(temp.newFolder()); | |||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||
InputComponentStore store = new InputComponentStore(project, branchConfiguration); | |||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||
Path moduleBaseDir = temp.newFolder().toPath(); | |||
ProjectDefinition module1Def = ProjectDefinition.create() | |||
@@ -150,7 +150,7 @@ public class ComponentsPublisherTest { | |||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||
InputComponentStore store = new InputComponentStore(project, branchConfiguration); | |||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||
ComponentsPublisher publisher = new ComponentsPublisher(project, store); | |||
publisher.publish(writer); | |||
@@ -175,7 +175,7 @@ public class ComponentsPublisherTest { | |||
.setWorkDir(temp.newFolder()); | |||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||
InputComponentStore store = new InputComponentStore(project, branchConfiguration); | |||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||
DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.java", 5) | |||
.setLines(2) | |||
@@ -212,7 +212,7 @@ public class ComponentsPublisherTest { | |||
.setWorkDir(temp.newFolder()); | |||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||
InputComponentStore store = new InputComponentStore(project, branchConfiguration); | |||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||
ComponentsPublisher publisher = new ComponentsPublisher(project, store); | |||
publisher.publish(writer); | |||
@@ -244,7 +244,7 @@ public class ComponentsPublisherTest { | |||
.setWorkDir(temp.newFolder()); | |||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||
InputComponentStore store = new InputComponentStore(project, branchConfiguration); | |||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||
ComponentsPublisher publisher = new ComponentsPublisher(project, store); | |||
publisher.publish(writer); | |||
@@ -58,7 +58,7 @@ public class CoveragePublisherTest { | |||
String moduleKey = "foo"; | |||
inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setLines(5).build(); | |||
DefaultInputProject rootModule = TestInputFileBuilder.newDefaultInputProject(moduleKey, temp.newFolder()); | |||
InputComponentStore componentCache = new InputComponentStore(rootModule, mock(BranchConfiguration.class)); | |||
InputComponentStore componentCache = new InputComponentStore(mock(BranchConfiguration.class)); | |||
componentCache.put(moduleKey, inputFile); | |||
measureCache = mock(MeasureCache.class); |
@@ -69,7 +69,7 @@ public class MeasuresPublisherTest { | |||
String projectKey = "foo"; | |||
project = TestInputFileBuilder.newDefaultInputProject(projectKey, temp.newFolder()); | |||
inputFile = new TestInputFileBuilder(projectKey, "src/Foo.php").setPublish(true).build(); | |||
InputComponentStore componentCache = new InputComponentStore(project, mock(BranchConfiguration.class)); | |||
InputComponentStore componentCache = new InputComponentStore(mock(BranchConfiguration.class)); | |||
componentCache.put(projectKey, inputFile); | |||
measureCache = mock(MeasureCache.class); | |||
when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList()); |
@@ -58,7 +58,7 @@ public class SourcePublisherTest { | |||
.build(); | |||
DefaultInputProject rootProject = TestInputFileBuilder.newDefaultInputProject(moduleKey, baseDir); | |||
InputComponentStore componentStore = new InputComponentStore(rootProject, mock(BranchConfiguration.class)); | |||
InputComponentStore componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
componentStore.put(moduleKey, inputFile); | |||
publisher = new SourcePublisher(componentStore); |
@@ -25,10 +25,11 @@ import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.slf4j.Logger; | |||
import org.sonar.api.batch.fs.internal.DefaultFileSystem; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.config.internal.MapSettings; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
@@ -42,13 +43,13 @@ public class QProfileVerifierTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private DefaultFileSystem fs; | |||
private InputComponentStore store; | |||
private QualityProfiles profiles; | |||
private MapSettings settings = new MapSettings(); | |||
@Before | |||
public void before() throws Exception { | |||
fs = new DefaultFileSystem(temp.newFolder().toPath()); | |||
store = new InputComponentStore(mock(BranchConfiguration.class)); | |||
profiles = mock(QualityProfiles.class); | |||
QProfile javaProfile = new QProfile("p1", "My Java profile", "java", null); | |||
when(profiles.findByLanguage("java")).thenReturn(javaProfile); | |||
@@ -58,10 +59,10 @@ public class QProfileVerifierTest { | |||
@Test | |||
public void should_log_all_used_profiles() { | |||
fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
fs.add(new TestInputFileBuilder("foo", "src/Baz.cbl").setLanguage("cobol").build()); | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Baz.cbl").setLanguage("cobol").build()); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), fs, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
Logger logger = mock(Logger.class); | |||
profileLogger.execute(logger); | |||
@@ -71,11 +72,11 @@ public class QProfileVerifierTest { | |||
@Test | |||
public void should_fail_if_default_profile_not_used() { | |||
fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
settings.setProperty("sonar.profile", "Unknown"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), fs, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
thrown.expect(MessageException.class); | |||
thrown.expectMessage("sonar.profile was set to 'Unknown' but didn't match any profile for any language. Please check your configuration."); | |||
@@ -87,7 +88,7 @@ public class QProfileVerifierTest { | |||
public void should_not_fail_if_no_language_on_project() { | |||
settings.setProperty("sonar.profile", "Unknown"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), fs, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
profileLogger.execute(); | |||
@@ -95,11 +96,11 @@ public class QProfileVerifierTest { | |||
@Test | |||
public void should_not_fail_if_default_profile_used_at_least_once() { | |||
fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
settings.setProperty("sonar.profile", "My Java profile"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), fs, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
profileLogger.execute(); | |||
} |
@@ -37,7 +37,7 @@ public class ModuleIndexerTest { | |||
private InputComponentStore componentStore; | |||
public void createIndexer(DefaultInputProject rootProject) { | |||
componentStore = new InputComponentStore(rootProject, mock(BranchConfiguration.class)); | |||
componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
moduleHierarchy = mock(DefaultInputModuleHierarchy.class); | |||
indexer = new ModuleIndexer(componentStore, moduleHierarchy); | |||
} |
@@ -60,7 +60,7 @@ public class InputComponentStoreTest { | |||
DefaultInputProject rootProject = TestInputFileBuilder.newDefaultInputProject(rootDef); | |||
DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(moduleDef); | |||
InputComponentStore store = new InputComponentStore(rootProject, mock(BranchConfiguration.class)); | |||
InputComponentStore store = new InputComponentStore(mock(BranchConfiguration.class)); | |||
store.put(subModule); | |||
DefaultInputFile fooFile = new TestInputFileBuilder(rootModuleKey, "src/main/java/Foo.java") | |||
@@ -96,7 +96,7 @@ public class InputComponentStoreTest { | |||
static class InputComponentStoreTester extends InputComponentStore { | |||
InputComponentStoreTester() throws IOException { | |||
super(TestInputFileBuilder.newDefaultInputProject("root", temp.newFolder()), mock(BranchConfiguration.class)); | |||
super(mock(BranchConfiguration.class)); | |||
} | |||
InputFile addFile(String moduleKey, String relpath, String language) { |
@@ -1,91 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.filesystem; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
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.TemporaryFolder; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.batch.fs.InputFile.Type; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.SensorStrategy; | |||
import org.sonar.scanner.scan.ScanProperties; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class InputFileBuilderTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private Path baseDir; | |||
private Path workDir; | |||
private InputFileBuilder builder; | |||
private SensorStrategy sensorStrategy; | |||
@Before | |||
public void setUp() throws IOException { | |||
baseDir = temp.newFolder().toPath(); | |||
workDir = temp.newFolder().toPath(); | |||
DefaultInputProject project = new DefaultInputProject(ProjectDefinition.create() | |||
.setBaseDir(baseDir.toFile()) | |||
.setWorkDir(workDir.toFile()) | |||
.setProperty(CoreProperties.ENCODING_PROPERTY, StandardCharsets.UTF_8.name()) | |||
.setKey("root"), 0); | |||
Path moduleBaseDir = baseDir.resolve("module1"); | |||
Files.createDirectories(moduleBaseDir); | |||
DefaultInputModule module = new DefaultInputModule(ProjectDefinition.create() | |||
.setBaseDir(moduleBaseDir.toFile()) | |||
.setWorkDir(workDir.toFile()) | |||
.setKey("module1"), 1); | |||
MetadataGenerator metadataGenerator = mock(MetadataGenerator.class); | |||
ScannerComponentIdGenerator idGenerator = new ScannerComponentIdGenerator(); | |||
ScanProperties properties = mock(ScanProperties.class); | |||
sensorStrategy = new SensorStrategy(); | |||
builder = new InputFileBuilder(project, module, metadataGenerator, idGenerator, properties, | |||
sensorStrategy); | |||
} | |||
@Test | |||
public void testBuild() { | |||
Path filePath = baseDir.resolve("module1/src/File1.xoo"); | |||
DefaultInputFile inputFile = builder.create(Type.MAIN, filePath, null); | |||
assertThat(inputFile.absolutePath()).isEqualTo(filePath.toString().replaceAll("\\\\", "/")); | |||
assertThat(inputFile.relativePath()).isEqualTo("src/File1.xoo"); | |||
assertThat(inputFile.path()).isEqualTo(filePath); | |||
assertThat(inputFile.key()).isEqualTo("root:module1/src/File1.xoo"); | |||
assertThat(inputFile.isPublished()).isFalse(); | |||
sensorStrategy.setGlobal(true); | |||
assertThat(inputFile.relativePath()).isEqualTo("module1/src/File1.xoo"); | |||
} | |||
} |
@@ -31,10 +31,8 @@ import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
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.FileMetadata; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.utils.PathUtils; | |||
@@ -64,8 +62,7 @@ public class MetadataGeneratorTest { | |||
MockitoAnnotations.initMocks(this); | |||
metadata = new FileMetadata(); | |||
IssueExclusionsLoader issueExclusionsLoader = new IssueExclusionsLoader(mock(IssueExclusionPatternInitializer.class), mock(PatternMatcher.class)); | |||
generator = new MetadataGenerator(new DefaultInputModule(ProjectDefinition.create().setKey("module").setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder())), | |||
statusDetection, metadata, issueExclusionsLoader); | |||
generator = new MetadataGenerator(statusDetection, metadata, issueExclusionsLoader); | |||
} | |||
@Test | |||
@@ -94,7 +91,7 @@ public class MetadataGeneratorTest { | |||
DefaultInputFile inputFile = new TestInputFileBuilder("struts", relativePath) | |||
.setModuleBaseDir(baseDir) | |||
.build(); | |||
generator.setMetadata(inputFile, StandardCharsets.US_ASCII); | |||
generator.setMetadata("module", inputFile, StandardCharsets.US_ASCII); | |||
return inputFile; | |||
} | |||
@@ -49,7 +49,7 @@ public class ModuleInputComponentStoreTest { | |||
@Before | |||
public void setUp() throws IOException { | |||
DefaultInputProject root = TestInputFileBuilder.newDefaultInputProject(projectKey, temp.newFolder()); | |||
componentStore = new InputComponentStore(root, mock(BranchConfiguration.class)); | |||
componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
} | |||
@Test |
@@ -32,28 +32,25 @@ import org.sonar.api.batch.fs.IndexedFile; | |||
import org.sonar.api.batch.fs.InputFile; | |||
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 { | |||
public class ProjectExclusionFiltersTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private Path moduleBaseDir; | |||
private MapSettings settings; | |||
private ExclusionFilters filter; | |||
@Before | |||
public void setUp() throws IOException { | |||
settings = new MapSettings(); | |||
moduleBaseDir = temp.newFolder().toPath(); | |||
filter = new ExclusionFilters(new FileExclusions(settings.asConfig())); | |||
} | |||
@Test | |||
public void no_inclusions_nor_exclusions() throws IOException { | |||
filter.prepare(); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); | |||
assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue(); | |||
@@ -63,7 +60,7 @@ public class ExclusionFiltersTest { | |||
@Test | |||
public void match_inclusion() throws IOException { | |||
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java"); | |||
filter.prepare(); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); | |||
assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue(); | |||
@@ -75,7 +72,7 @@ public class ExclusionFiltersTest { | |||
@Test | |||
public void match_at_least_one_inclusion() throws IOException { | |||
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java"); | |||
filter.prepare(); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null); | |||
assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse(); | |||
@@ -89,7 +86,7 @@ public class ExclusionFiltersTest { | |||
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*"); | |||
settings.setProperty(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, "src/test/java/**/*"); | |||
settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java"); | |||
filter.prepare(); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null); | |||
assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse(); | |||
@@ -108,7 +105,7 @@ public class ExclusionFiltersTest { | |||
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "src/main/java/**/*"); | |||
settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getAbsolutePath()); | |||
filter.prepare(); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java", null); | |||
assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue(); | |||
@@ -119,9 +116,9 @@ public class ExclusionFiltersTest { | |||
@Test | |||
public void trim_pattern() { | |||
settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, " **/*Dao.java "); | |||
ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig()); | |||
assertThat(filter.prepareMainExclusions()[0].toString()).isEqualTo("**/*Dao.java"); | |||
assertThat(filter.prepareMainExclusions(new String[] {" **/*Dao.java "}, new String[0])[0].toString()).isEqualTo("**/*Dao.java"); | |||
} | |||
} |
@@ -79,7 +79,7 @@ public class JSONReportTest { | |||
ProjectDefinition def = ProjectDefinition.create().setBaseDir(projectBaseDir).setWorkDir(temp.newFolder()).setKey("struts"); | |||
DefaultInputProject project = new DefaultInputProject(def, 1); | |||
InputComponentStore inputComponentStore = new InputComponentStore(project, mock(BranchConfiguration.class)); | |||
InputComponentStore inputComponentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||
DefaultInputFile inputFile = new TestInputFileBuilder("struts", "src/main/java/org/apache/struts/Action.java") | |||
.setModuleBaseDir(projectBaseDir.toPath()).build(); |