@@ -5,6 +5,8 @@ | |||
*/ | |||
package it.analysis; | |||
import org.apache.commons.io.FileUtils; | |||
import org.sonar.wsclient.issue.IssueClient; | |||
import com.google.common.collect.Maps; | |||
import com.sonar.orchestrator.Orchestrator; | |||
import com.sonar.orchestrator.build.BuildFailureException; | |||
@@ -15,7 +17,10 @@ import com.sonar.orchestrator.config.FileSystem; | |||
import com.sonar.orchestrator.locator.FileLocation; | |||
import com.sonar.orchestrator.version.Version; | |||
import it.Category3Suite; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -24,6 +29,7 @@ import java.util.concurrent.ExecutionException; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.Future; | |||
import org.apache.commons.lang.ObjectUtils; | |||
import org.json.simple.JSONArray; | |||
import org.json.simple.JSONObject; | |||
@@ -41,7 +47,6 @@ import org.sonar.wsclient.services.Resource; | |||
import org.sonar.wsclient.services.ResourceQuery; | |||
import org.sonar.wsclient.user.UserParameters; | |||
import util.ItUtils; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
@@ -89,7 +94,7 @@ public class IssuesModeTest { | |||
public void project_key_with_slash() throws IOException { | |||
restoreProfile("one-issue-per-line.xml"); | |||
setDefaultQualityProfile("xoo", "one-issue-per-line"); | |||
SonarRunner runner = configureRunner("shared/xoo-sample"); | |||
runner.setProjectKey("sample/project"); | |||
runner.setProperty("sonar.analysis.mode", "issues"); | |||
@@ -97,6 +102,99 @@ public class IssuesModeTest { | |||
assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17); | |||
} | |||
// SONAR-6931 | |||
@Test | |||
public void only_scan_changed_files_qps() throws IOException { | |||
restoreProfile("one-issue-per-line.xml"); | |||
orchestrator.getServer().provisionProject("sample", "xoo-sample"); | |||
orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); | |||
SonarRunner runner = configureRunner("shared/xoo-sample", "sonar.verbose", "true"); | |||
BuildResult result = orchestrator.executeBuild(runner); | |||
List<Issue> serverIssues = ItUtils.getAllServerIssues(orchestrator); | |||
for (Issue i : serverIssues) { | |||
assertThat(i.status()).isEqualTo("OPEN"); | |||
} | |||
assertThat(serverIssues).hasSize(17); | |||
// change quality profile | |||
restoreProfile("with-many-rules.xml"); | |||
orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "with-many-rules"); | |||
// do it again, scanning nothing (all files should be unchanged) | |||
runner = configureRunnerIssues("shared/xoo-sample", | |||
"sonar.verbose", "true", | |||
"sonar.scanChangedFilesOnly", "true"); | |||
result = orchestrator.executeBuild(runner); | |||
assertThat(result.getLogs()).contains("Scanning only changed files"); | |||
assertThat(result.getLogs()).contains("'One Issue Per Line' skipped because there is no related file in current project"); | |||
ItUtils.assertIssuesInJsonReport(result, 0, 0, 17); | |||
} | |||
// SONAR-6931 | |||
@Test | |||
public void only_scan_changed_files_transitions() throws IOException { | |||
restoreProfile("one-issue-per-line.xml"); | |||
orchestrator.getServer().provisionProject("sample", "xoo-sample"); | |||
orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); | |||
SonarRunner runner = configureRunner("shared/xoo-sample", "sonar.verbose", "true"); | |||
BuildResult result = orchestrator.executeBuild(runner); | |||
List<Issue> serverIssues = ItUtils.getAllServerIssues(orchestrator); | |||
for (Issue i : serverIssues) { | |||
assertThat(i.status()).isEqualTo("OPEN"); | |||
} | |||
assertThat(serverIssues).hasSize(17); | |||
// resolve 2 issues | |||
IssueClient issueClient = orchestrator.getServer().wsClient("admin", "admin").issueClient(); | |||
issueClient.doTransition(serverIssues.get(0).key(), "wontfix"); | |||
issueClient.doTransition(serverIssues.get(1).key(), "wontfix"); | |||
// do it again, scanning nothing (all files should be unchanged) | |||
runner = configureRunnerIssues("shared/xoo-sample", | |||
"sonar.verbose", "true", | |||
"sonar.scanChangedFilesOnly", "true"); | |||
result = orchestrator.executeBuild(runner); | |||
assertThat(result.getLogs()).contains("Scanning only changed files"); | |||
assertThat(result.getLogs()).contains("'One Issue Per Line' skipped because there is no related file in current project"); | |||
ItUtils.assertIssuesInJsonReport(result, 0, 0, 15); | |||
} | |||
// SONAR-6931 | |||
@Test | |||
public void only_scan_changed_files_on_change() throws IOException { | |||
restoreProfile("one-issue-per-line.xml"); | |||
orchestrator.getServer().provisionProject("sample", "xoo-sample"); | |||
orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); | |||
SonarRunner runner = configureRunner("shared/xoo-sample", "sonar.verbose", "true"); | |||
BuildResult result = orchestrator.executeBuild(runner); | |||
// change QP | |||
restoreProfile("with-many-rules.xml"); | |||
orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "with-many-rules"); | |||
// now change file hash in a temporary location | |||
File tmpProjectDir = temp.newFolder(); | |||
FileUtils.copyDirectory(ItUtils.projectDir("shared/xoo-sample"), tmpProjectDir); | |||
File srcFile = new File(tmpProjectDir, "src/main/xoo/sample/Sample.xoo"); | |||
FileUtils.write(srcFile, "\n", StandardCharsets.UTF_8, true); | |||
// scan again, with different QP | |||
runner = SonarRunner.create(tmpProjectDir, | |||
"sonar.working.directory", temp.newFolder().getAbsolutePath(), | |||
"sonar.analysis.mode", "issues", | |||
"sonar.report.export.path", "sonar-report.json", | |||
"sonar.userHome", temp.newFolder().getAbsolutePath(), | |||
"sonar.verbose", "true", | |||
"sonar.scanChangedFilesOnly", "true"); | |||
result = orchestrator.executeBuild(runner); | |||
assertThat(result.getLogs()).contains("Scanning only changed files"); | |||
assertThat(result.getLogs()).doesNotContain("'One Issue Per Line' skipped because there is no related file in current project"); | |||
ItUtils.assertIssuesInJsonReport(result, 3, 0, 17); | |||
} | |||
@Test | |||
public void non_associated_mode() throws IOException { | |||
restoreProfile("one-issue-per-line.xml"); |
@@ -4,6 +4,10 @@ package util;/* | |||
* mailto:contact AT sonarsource DOT com | |||
*/ | |||
import org.sonar.wsclient.issue.Issue; | |||
import org.sonar.wsclient.issue.IssueQuery; | |||
import org.sonar.wsclient.issue.IssueClient; | |||
import com.google.common.base.Supplier; | |||
import com.google.common.base.Suppliers; | |||
import com.google.common.collect.ImmutableMap; | |||
@@ -12,14 +16,18 @@ import com.sonar.orchestrator.Orchestrator; | |||
import com.sonar.orchestrator.build.BuildResult; | |||
import com.sonar.orchestrator.build.SonarRunner; | |||
import com.sonar.orchestrator.locator.FileLocation; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.io.FileUtils; | |||
import org.json.simple.JSONArray; | |||
import org.json.simple.JSONObject; | |||
@@ -27,7 +35,6 @@ import org.json.simple.JSONValue; | |||
import org.sonar.wsclient.base.HttpException; | |||
import org.sonar.wsclient.services.PropertyDeleteQuery; | |||
import org.sonar.wsclient.services.PropertyUpdateQuery; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -63,6 +70,12 @@ public class ItUtils { | |||
throw new IllegalStateException("XOO plugin is not built"); | |||
} | |||
public static List<Issue> getAllServerIssues(Orchestrator orchestrator) { | |||
IssueClient issueClient = orchestrator.getServer().wsClient().issueClient(); | |||
return issueClient.find(IssueQuery.create()).list(); | |||
} | |||
/** | |||
* Locate the directory of sample project | |||
* | |||
@@ -137,6 +150,29 @@ public class ItUtils { | |||
return count; | |||
} | |||
public static void assertIssuesInJsonReport(BuildResult result, int newIssues, int resolvedIssues, int existingIssues) { | |||
JSONObject obj = getJSONReport(result); | |||
JSONArray issues = (JSONArray) obj.get("issues"); | |||
int countNew = 0; | |||
int countResolved = 0; | |||
int countExisting = 0; | |||
for (Object issue : issues) { | |||
JSONObject jsonIssue = (JSONObject) issue; | |||
if ((Boolean) jsonIssue.get("isNew")) { | |||
countNew++; | |||
} else if (jsonIssue.get("resolution") != null) { | |||
countResolved++; | |||
} else { | |||
countExisting++; | |||
} | |||
} | |||
assertThat(countNew).isEqualTo(newIssues); | |||
assertThat(countResolved).isEqualTo(resolvedIssues); | |||
assertThat(countExisting).isEqualTo(existingIssues); | |||
} | |||
public static SonarRunner runVerboseProjectAnalysis(Orchestrator orchestrator, String projectRelativePath, String... properties) { | |||
return runProjectAnalysis(orchestrator, projectRelativePath, true, properties); | |||
} | |||
@@ -164,7 +200,7 @@ public class ItUtils { | |||
} | |||
} | |||
public static void resetPeriods(Orchestrator orchestrator){ | |||
public static void resetPeriods(Orchestrator orchestrator) { | |||
setServerProperty(orchestrator, "sonar.timemachine.period1", null); | |||
setServerProperty(orchestrator, "sonar.timemachine.period2", null); | |||
setServerProperty(orchestrator, "sonar.timemachine.period3", null); | |||
@@ -204,4 +240,4 @@ public class ItUtils { | |||
} | |||
} | |||
} | |||
} |
@@ -33,7 +33,7 @@ import org.sonar.batch.scan.ImmutableProjectReactor; | |||
public class DefaultProjectTree implements Startable { | |||
private final ProjectConfigurator configurator; | |||
private ImmutableProjectReactor projectReactor; | |||
private final ImmutableProjectReactor projectReactor; | |||
private List<Project> projects; | |||
private Map<ProjectDefinition, Project> projectsByDef; |
@@ -36,9 +36,11 @@ import java.util.Map; | |||
public class DefaultAnalysisMode extends AbstractAnalysisMode implements AnalysisMode { | |||
private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class); | |||
private static final String KEY_ONLY_ANALYZE_CHANGED = "sonar.scanChangedFilesOnly"; | |||
private boolean mediumTestMode; | |||
private boolean notAssociated; | |||
private boolean onlyChanged; | |||
public DefaultAnalysisMode(GlobalProperties globalProps, AnalysisProperties props) { | |||
init(globalProps.properties(), props.properties()); | |||
@@ -52,6 +54,10 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi | |||
return notAssociated; | |||
} | |||
public boolean onlyAnalyzeChanged() { | |||
return onlyChanged; | |||
} | |||
private void init(Map<String, String> globalProps, Map<String, String> analysisProps) { | |||
// make sure analysis is consistent with global properties | |||
boolean globalPreview = isIssues(globalProps); | |||
@@ -70,6 +76,8 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi | |||
issues = CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode) || CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode); | |||
mediumTestMode = "true".equals(getPropertyWithFallback(analysisProps, globalProps, FakePluginInstaller.MEDIUM_TEST_ENABLED)); | |||
notAssociated = issues && rootProjectKeyMissing(analysisProps); | |||
String onlyChangedStr = getPropertyWithFallback(analysisProps, globalProps, KEY_ONLY_ANALYZE_CHANGED); | |||
onlyChanged = issues && "true".equals(onlyChangedStr); | |||
} | |||
public void printMode() { | |||
@@ -86,6 +94,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi | |||
if (notAssociated) { | |||
LOG.info("Local analysis"); | |||
} | |||
if (onlyChanged) { | |||
LOG.info("Scanning only changed files"); | |||
} | |||
} | |||
private static String getPropertyWithFallback(Map<String, String> props1, Map<String, String> props2, String key) { |
@@ -19,14 +19,20 @@ | |||
*/ | |||
package org.sonar.batch.issue.tracking; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.collect.Lists; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Set; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.rule.ActiveRule; | |||
@@ -54,15 +60,18 @@ public class LocalIssueTracking { | |||
private final ActiveRules activeRules; | |||
private final ServerIssueRepository serverIssueRepository; | |||
private final Date analysisDate; | |||
private final DefaultAnalysisMode mode; | |||
private boolean hasServerAnalysis; | |||
public LocalIssueTracking(BatchComponentCache resourceCache, IssueTracking tracking, ServerLineHashesLoader lastLineHashes, IssueUpdater updater, | |||
ActiveRules activeRules, ServerIssueRepository serverIssueRepository, ProjectRepositories projectRepositories, ReportPublisher reportPublisher) { | |||
ActiveRules activeRules, ServerIssueRepository serverIssueRepository, ProjectRepositories projectRepositories, ReportPublisher reportPublisher, | |||
DefaultAnalysisMode mode) { | |||
this.tracking = tracking; | |||
this.lastLineHashes = lastLineHashes; | |||
this.updater = updater; | |||
this.serverIssueRepository = serverIssueRepository; | |||
this.mode = mode; | |||
this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate(); | |||
this.changeContext = IssueChangeContext.createScan(analysisDate); | |||
this.activeRules = activeRules; | |||
@@ -78,18 +87,23 @@ public class LocalIssueTracking { | |||
public List<DefaultIssue> trackIssues(BatchComponent component, Set<BatchReport.Issue> rawIssues) { | |||
List<DefaultIssue> trackedIssues = Lists.newArrayList(); | |||
if (hasServerAnalysis) { | |||
// all the issues that are not closed in db before starting this module scan, including manual issues | |||
Collection<ServerIssue> serverIssues = loadServerIssues(component); | |||
SourceHashHolder sourceHashHolder = loadSourceHashes(component); | |||
if (shouldCopyServerIssues(component)) { | |||
// raw issues should be empty, we just need to deal with server issues (SONAR-6931) | |||
copyServerIssues(serverIssues, trackedIssues); | |||
} else { | |||
IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, rawIssues); | |||
SourceHashHolder sourceHashHolder = loadSourceHashes(component); | |||
// unmatched from server = issues that have been resolved + issues on disabled/removed rules + manual issues | |||
addUnmatchedFromServer(trackingResult.unmatched(), sourceHashHolder, trackedIssues); | |||
IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, rawIssues); | |||
mergeMatched(component, trackingResult, trackedIssues, rawIssues); | |||
// unmatched from server = issues that have been resolved + issues on disabled/removed rules + manual issues | |||
addUnmatchedFromServer(trackingResult.unmatched(), sourceHashHolder, trackedIssues); | |||
mergeMatched(component, trackingResult, trackedIssues, rawIssues); | |||
} | |||
} | |||
if (hasServerAnalysis && ResourceUtils.isRootProject(component.resource())) { | |||
@@ -100,8 +114,33 @@ public class LocalIssueTracking { | |||
return trackedIssues; | |||
} | |||
private boolean shouldCopyServerIssues(BatchComponent component) { | |||
if (mode.onlyAnalyzeChanged() && component.isFile()) { | |||
DefaultInputFile inputFile = (DefaultInputFile) component.inputComponent(); | |||
if (inputFile.status() == Status.SAME) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
private void copyServerIssues(Collection<ServerIssue> serverIssues, List<DefaultIssue> trackedIssues) { | |||
for (ServerIssue serverIssue : serverIssues) { | |||
org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) serverIssue).getDto(); | |||
DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue); | |||
ActiveRule activeRule = activeRules.find(unmatched.ruleKey()); | |||
unmatched.setNew(false); | |||
boolean isRemovedRule = activeRule == null; | |||
unmatched.setBeingClosed(isRemovedRule); | |||
unmatched.setOnDisabledRule(isRemovedRule); | |||
trackedIssues.add(unmatched); | |||
} | |||
} | |||
private DefaultIssue toTracked(BatchComponent component, BatchReport.Issue rawIssue) { | |||
DefaultIssue trackedIssue = new org.sonar.core.issue.DefaultIssueBuilder() | |||
return new org.sonar.core.issue.DefaultIssueBuilder() | |||
.componentKey(component.key()) | |||
.projectKey("unused") | |||
.ruleKey(RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey())) | |||
@@ -110,7 +149,6 @@ public class LocalIssueTracking { | |||
.message(rawIssue.hasMsg() ? rawIssue.getMsg() : null) | |||
.severity(rawIssue.getSeverity().name()) | |||
.build(); | |||
return trackedIssue; | |||
} | |||
@CheckForNull |
@@ -53,7 +53,7 @@ public class ComponentIndexer { | |||
public void execute(DefaultModuleFileSystem fs) { | |||
module.setBaseDir(fs.baseDir()); | |||
for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) { | |||
for (InputFile inputFile : fs.inputFiles()) { | |||
String languageKey = inputFile.language(); | |||
boolean unitTest = InputFile.Type.TEST == inputFile.type(); | |||
Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest); |
@@ -19,19 +19,25 @@ | |||
*/ | |||
package org.sonar.batch.scan.filesystem; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.Lists; | |||
import java.io.File; | |||
import java.nio.charset.Charset; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.fs.FilePredicate; | |||
@@ -59,7 +65,7 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module | |||
private boolean initialized; | |||
public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, Project project, | |||
Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer) { | |||
Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer, DefaultAnalysisMode mode) { | |||
super(initializer.baseDir(), moduleInputFileCache); | |||
this.componentIndexer = componentIndexer; | |||
this.moduleKey = project.getKey(); | |||
@@ -70,11 +76,16 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module | |||
this.sourceDirsOrFiles = initializer.sources(); | |||
this.testDirsOrFiles = initializer.tests(); | |||
this.binaryDirs = initializer.binaryDirs(); | |||
// filter the files that sensors have access to (SONAR-6931) | |||
if (mode.onlyAnalyzeChanged()) { | |||
setDefaultPredicate(predicates.not(predicates.hasStatus(Status.SAME))); | |||
} | |||
} | |||
@VisibleForTesting | |||
public DefaultModuleFileSystem(Project project, | |||
Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer) { | |||
Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer, DefaultAnalysisMode mode) { | |||
super(initializer.baseDir().toPath()); | |||
this.componentIndexer = componentIndexer; | |||
this.moduleKey = project.getKey(); | |||
@@ -85,6 +96,11 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module | |||
this.sourceDirsOrFiles = initializer.sources(); | |||
this.testDirsOrFiles = initializer.tests(); | |||
this.binaryDirs = initializer.binaryDirs(); | |||
// filter the files sensors have access to | |||
if (mode.onlyAnalyzeChanged()) { | |||
setDefaultPredicate(predicates.not(predicates.hasStatus(Status.SAME))); | |||
} | |||
} | |||
public boolean isInitialized() { |
@@ -19,19 +19,14 @@ | |||
*/ | |||
package org.sonar.batch.scan.filesystem; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.SortedMap; | |||
import java.util.TreeMap; | |||
import javax.annotation.CheckForNull; | |||
import com.google.common.collect.Table; | |||
import com.google.common.collect.TreeBasedTable; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import javax.annotation.CheckForNull; | |||
/** | |||
* Cache of all files and dirs. This cache is shared amongst all project modules. Inclusion and | |||
* exclusion patterns are already applied. | |||
@@ -39,91 +34,59 @@ import org.sonar.api.batch.fs.InputFile; | |||
@BatchSide | |||
public class InputPathCache { | |||
private final Map<String, SortedMap<String, InputFile>> inputFileCache = new LinkedHashMap<>(); | |||
private final Map<String, SortedMap<String, InputDir>> inputDirCache = new LinkedHashMap<>(); | |||
private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create(); | |||
private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create(); | |||
public Iterable<InputFile> allFiles() { | |||
return Iterables.concat(Iterables.transform(inputFileCache.values(), new Function<Map<String, InputFile>, Collection<InputFile>>() { | |||
@Override | |||
public Collection<InputFile> apply(Map<String, InputFile> input) { | |||
return input.values(); | |||
} | |||
})); | |||
return inputFileCache.values(); | |||
} | |||
public Iterable<InputDir> allDirs() { | |||
return Iterables.concat(Iterables.transform(inputDirCache.values(), new Function<Map<String, InputDir>, Collection<InputDir>>() { | |||
@Override | |||
public Collection<InputDir> apply(Map<String, InputDir> input) { | |||
return input.values(); | |||
} | |||
})); | |||
return inputDirCache.values(); | |||
} | |||
public Iterable<InputFile> filesByModule(String moduleKey) { | |||
if (inputFileCache.containsKey(moduleKey)) { | |||
return inputFileCache.get(moduleKey).values(); | |||
} | |||
return Collections.emptyList(); | |||
return inputFileCache.row(moduleKey).values(); | |||
} | |||
public Iterable<InputDir> dirsByModule(String moduleKey) { | |||
if (inputDirCache.containsKey(moduleKey)) { | |||
return inputDirCache.get(moduleKey).values(); | |||
} | |||
return Collections.emptyList(); | |||
return inputDirCache.row(moduleKey).values(); | |||
} | |||
public InputPathCache removeModule(String moduleKey) { | |||
inputFileCache.remove(moduleKey); | |||
inputDirCache.remove(moduleKey); | |||
inputFileCache.row(moduleKey).clear(); | |||
inputDirCache.row(moduleKey).clear(); | |||
return this; | |||
} | |||
public InputPathCache remove(String moduleKey, InputFile inputFile) { | |||
if (inputFileCache.containsKey(moduleKey)) { | |||
inputFileCache.get(moduleKey).remove(inputFile.relativePath()); | |||
} | |||
inputFileCache.remove(moduleKey, inputFile.relativePath()); | |||
return this; | |||
} | |||
public InputPathCache remove(String moduleKey, InputDir inputDir) { | |||
if (inputDirCache.containsKey(moduleKey)) { | |||
inputDirCache.get(moduleKey).remove(inputDir.relativePath()); | |||
} | |||
inputDirCache.remove(moduleKey, inputDir.relativePath()); | |||
return this; | |||
} | |||
public InputPathCache put(String moduleKey, InputFile inputFile) { | |||
if (!inputFileCache.containsKey(moduleKey)) { | |||
inputFileCache.put(moduleKey, new TreeMap<String, InputFile>()); | |||
} | |||
inputFileCache.get(moduleKey).put(inputFile.relativePath(), inputFile); | |||
inputFileCache.put(moduleKey, inputFile.relativePath(), inputFile); | |||
return this; | |||
} | |||
public InputPathCache put(String moduleKey, InputDir inputDir) { | |||
if (!inputDirCache.containsKey(moduleKey)) { | |||
inputDirCache.put(moduleKey, new TreeMap<String, InputDir>()); | |||
} | |||
inputDirCache.get(moduleKey).put(inputDir.relativePath(), inputDir); | |||
inputDirCache.put(moduleKey, inputDir.relativePath(), inputDir); | |||
return this; | |||
} | |||
@CheckForNull | |||
public InputFile getFile(String moduleKey, String relativePath) { | |||
if (inputFileCache.containsKey(moduleKey)) { | |||
return inputFileCache.get(moduleKey).get(relativePath); | |||
} | |||
return null; | |||
return inputFileCache.get(moduleKey, relativePath); | |||
} | |||
@CheckForNull | |||
public InputDir getDir(String moduleKey, String relativePath) { | |||
if (inputDirCache.containsKey(moduleKey)) { | |||
return inputDirCache.get(moduleKey).get(relativePath); | |||
} | |||
return null; | |||
return inputDirCache.get(moduleKey, relativePath); | |||
} | |||
} |
@@ -20,7 +20,6 @@ | |||
package org.sonar.batch.analysis; | |||
import org.junit.Rule; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import org.sonar.batch.analysis.AnalysisProperties; | |||
@@ -84,10 +83,30 @@ public class DefaultAnalysisModeTest { | |||
assertThat(mode.isPreview()).isFalse(); | |||
} | |||
@Test | |||
public void only_scan_changed() { | |||
Map<String, String> props = new HashMap<>(); | |||
props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES); | |||
GlobalProperties globalProps = new GlobalProperties(props); | |||
props.put("sonar.scanChangedFilesOnly", "true"); | |||
AnalysisProperties analysisProps = new AnalysisProperties(props); | |||
DefaultAnalysisMode mode = new DefaultAnalysisMode(globalProps, analysisProps); | |||
assertThat(mode.onlyAnalyzeChanged()).isTrue(); | |||
props.put(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PUBLISH); | |||
analysisProps = new AnalysisProperties(props); | |||
mode = new DefaultAnalysisMode(globalProps, analysisProps); | |||
assertThat(mode.onlyAnalyzeChanged()).isFalse(); | |||
} | |||
@Test | |||
public void default_publish_mode() { | |||
DefaultAnalysisMode mode = createMode(null); | |||
assertThat(mode.isPublish()).isTrue(); | |||
assertThat(mode.onlyAnalyzeChanged()).isFalse(); | |||
} | |||
@Test | |||
@@ -95,6 +114,7 @@ public class DefaultAnalysisModeTest { | |||
DefaultAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE_ISSUES); | |||
assertThat(mode.isIssues()).isTrue(); | |||
assertThat(mode.onlyAnalyzeChanged()).isFalse(); | |||
} | |||
private static DefaultAnalysisMode createMode(@Nullable String mode) { |
@@ -0,0 +1,186 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.batch.mediumtest.issuesmode; | |||
import org.assertj.core.api.Condition; | |||
import com.google.common.io.Resources; | |||
import org.sonar.batch.repository.FileData; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.filefilter.FileFilterUtils; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.batch.mediumtest.BatchMediumTester; | |||
import org.sonar.batch.protocol.Constants.Severity; | |||
import org.sonar.batch.protocol.input.BatchInput.ServerIssue; | |||
import org.sonar.xoo.XooPlugin; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.junit.Test; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.batch.mediumtest.TaskResult; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class ScanOnlyChangedTest { | |||
@org.junit.Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@org.junit.Rule | |||
public LogTester logTester = new LogTester(); | |||
private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); | |||
private BatchMediumTester tester; | |||
private static Long date(String date) { | |||
try { | |||
return sdf.parse(date).getTime(); | |||
} catch (ParseException e) { | |||
throw new IllegalStateException(e); | |||
} | |||
} | |||
@Before | |||
public void prepare() throws IOException { | |||
String filePath = "xources/hello/HelloJava.xoo"; | |||
String md5sum = DigestUtils.md5Hex(FileUtils.readFileToString(new File( | |||
Resources.getResource("mediumtest/xoo/sample/" + filePath).getPath()))); | |||
tester = BatchMediumTester.builder() | |||
.bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)) | |||
.registerPlugin("xoo", new XooPlugin()) | |||
.addDefaultQProfile("xoo", "Sonar Way") | |||
.addRules(new XooRulesDefinition()) | |||
.addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue") | |||
.addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue") | |||
.addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo") | |||
.addActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo") | |||
.addActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo") | |||
.addActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null) | |||
// this will cause the file to have status==SAME | |||
.addFileData("sample", filePath, new FileData(md5sum, null)) | |||
.setPreviousAnalysisDate(new Date()) | |||
// Existing issue that is copied | |||
.mockServerIssue(ServerIssue.newBuilder().setKey("xyz") | |||
.setModuleKey("sample") | |||
.setMsg("One issue per Line copied") | |||
.setPath("xources/hello/HelloJava.xoo") | |||
.setRuleRepository("xoo") | |||
.setRuleKey("OneIssuePerLine") | |||
.setLine(1) | |||
.setSeverity(Severity.MAJOR) | |||
.setCreationDate(date("14/03/2004")) | |||
.setChecksum(DigestUtils.md5Hex("packagehello;")) | |||
.setStatus("OPEN") | |||
.build()) | |||
// Existing issue on project that is still detected | |||
.mockServerIssue(ServerIssue.newBuilder().setKey("resolved-on-project") | |||
.setModuleKey("sample") | |||
.setRuleRepository("xoo") | |||
.setRuleKey("OneIssuePerModule") | |||
.setSeverity(Severity.CRITICAL) | |||
.setCreationDate(date("14/03/2004")) | |||
.setStatus("OPEN") | |||
.build()) | |||
// Manual issue | |||
.mockServerIssue(ServerIssue.newBuilder().setKey("manual") | |||
.setModuleKey("sample") | |||
.setPath("xources/hello/HelloJava.xoo") | |||
.setRuleRepository("manual") | |||
.setRuleKey("MyManualIssue") | |||
.setLine(1) | |||
.setSeverity(Severity.MAJOR) | |||
.setCreationDate(date("14/03/2004")) | |||
.setChecksum(DigestUtils.md5Hex("packagehello;")) | |||
.setStatus("OPEN") | |||
.build()) | |||
.build(); | |||
tester.start(); | |||
} | |||
@After | |||
public void stop() { | |||
tester.stop(); | |||
} | |||
private File copyProject(String path) throws Exception { | |||
File projectDir = temp.newFolder(); | |||
File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI()); | |||
FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar"))); | |||
return projectDir; | |||
} | |||
@Test | |||
public void testIssueTrackingChangedFiles() throws Exception { | |||
File projectDir = copyProject("/mediumtest/xoo/sample"); | |||
TaskResult result = tester | |||
.newScanTask(new File(projectDir, "sonar-project.properties")) | |||
.property("sonar.scanChangedFilesOnly", "true") | |||
.start(); | |||
int newIssues = 0; | |||
int openIssues = 0; | |||
int resolvedIssue = 0; | |||
for (Issue issue : result.trackedIssues()) { | |||
System.out | |||
.println(issue.message() + " " + issue.key() + " " + issue.ruleKey() + " " + issue.isNew() + " " + issue.resolution() + " " + issue.componentKey() + " " + issue.line()); | |||
if (issue.isNew()) { | |||
newIssues++; | |||
} else if (issue.resolution() != null) { | |||
resolvedIssue++; | |||
} else { | |||
openIssues++; | |||
} | |||
} | |||
/* | |||
* We have: | |||
* 6 new issues per line (open) in helloscala.xoo | |||
* 2 new issues per file in helloscala.xoo / ClassOneTest.xoo | |||
* 1 server issue (open, not new) copied from server in HelloJava.xoo (this file is unchanged) | |||
* 1 manual issue (open, not new) in HelloJava.xoo | |||
* 1 existing issue on the project (open, not new) | |||
*/ | |||
System.out.println("new: " + newIssues + " open: " + openIssues + " resolved " + resolvedIssue); | |||
assertThat(newIssues).isEqualTo(8); | |||
assertThat(openIssues).isEqualTo(3); | |||
assertThat(resolvedIssue).isEqualTo(0); | |||
// should only have server issues (HelloJava.xoo should not have been analyzed) | |||
assertThat(result.trackedIssues()).haveExactly(2, new Condition<Issue>() { | |||
@Override | |||
public boolean matches(Issue value) { | |||
return value.componentKey().endsWith("HelloJava.xoo"); | |||
} | |||
}); | |||
} | |||
} |
@@ -19,8 +19,13 @@ | |||
*/ | |||
package org.sonar.batch.scan.filesystem; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
@@ -40,7 +45,6 @@ import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.api.resources.Resource; | |||
import org.sonar.batch.index.BatchComponent; | |||
import org.sonar.batch.index.BatchComponentCache; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.argThat; | |||
import static org.mockito.Mockito.mock; | |||
@@ -57,6 +61,7 @@ public class ComponentIndexerTest { | |||
private AbstractLanguage cobolLanguage; | |||
private Project project; | |||
private ModuleFileSystemInitializer initializer; | |||
private DefaultAnalysisMode mode; | |||
@Before | |||
public void prepare() throws IOException { | |||
@@ -65,6 +70,7 @@ public class ComponentIndexerTest { | |||
sonarIndex = mock(SonarIndex.class); | |||
project = new Project("myProject"); | |||
initializer = mock(ModuleFileSystemInitializer.class); | |||
mode = mock(DefaultAnalysisMode.class); | |||
when(initializer.baseDir()).thenReturn(baseDir); | |||
when(initializer.workingDir()).thenReturn(temp.newFolder()); | |||
cobolLanguage = new AbstractLanguage("cobol") { | |||
@@ -79,10 +85,11 @@ public class ComponentIndexerTest { | |||
public void should_index_java_files() throws IOException { | |||
Languages languages = new Languages(Java.INSTANCE); | |||
ComponentIndexer indexer = createIndexer(languages); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer); | |||
fs.add(newInputFile("src/main/java/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false)); | |||
fs.add(newInputFile("src/main/java2/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false)); | |||
fs.add(newInputFile("src/test/java/foo/bar/FooTest.java", "", "foo/bar/FooTest.java", "java", true)); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode); | |||
fs.add(newInputFile("src/main/java/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED)); | |||
fs.add(newInputFile("src/main/java2/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false, Status.ADDED)); | |||
// should index even if filter is applied | |||
fs.add(newInputFile("src/test/java/foo/bar/FooTest.java", "", "foo/bar/FooTest.java", "java", true, Status.SAME)); | |||
fs.index(); | |||
@@ -110,10 +117,10 @@ public class ComponentIndexerTest { | |||
public void should_index_cobol_files() throws IOException { | |||
Languages languages = new Languages(cobolLanguage); | |||
ComponentIndexer indexer = createIndexer(languages); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer); | |||
fs.add(newInputFile("src/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false)); | |||
fs.add(newInputFile("src2/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false)); | |||
fs.add(newInputFile("src/test/foo/bar/FooTest.cbl", "", "foo/bar/FooTest.cbl", "cobol", true)); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(project, null, mock(FileIndexer.class), initializer, indexer, mode); | |||
fs.add(newInputFile("src/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED)); | |||
fs.add(newInputFile("src2/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false, Status.ADDED)); | |||
fs.add(newInputFile("src/test/foo/bar/FooTest.cbl", "", "foo/bar/FooTest.cbl", "cobol", true, Status.ADDED)); | |||
fs.index(); | |||
@@ -122,12 +129,13 @@ public class ComponentIndexerTest { | |||
verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/test/foo/bar/FooTest.cbl", cobolLanguage, true)); | |||
} | |||
private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest) throws IOException { | |||
private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest, InputFile.Status status) throws IOException { | |||
File file = new File(baseDir, path); | |||
FileUtils.write(file, content); | |||
return new DefaultInputFile("foo", path) | |||
.setLanguage(languageKey) | |||
.setType(unitTest ? InputFile.Type.TEST : InputFile.Type.MAIN); | |||
.setType(unitTest ? InputFile.Type.TEST : InputFile.Type.MAIN) | |||
.setStatus(status); | |||
} | |||
} |
@@ -19,6 +19,10 @@ | |||
*/ | |||
package org.sonar.batch.scan.filesystem; | |||
import org.sonar.api.batch.fs.InputFile.Status; | |||
import org.junit.Before; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import com.google.common.collect.Lists; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
@@ -50,22 +54,33 @@ public class DefaultModuleFileSystemTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
Settings settings = new Settings(); | |||
FileIndexer fileIndexer = mock(FileIndexer.class); | |||
ModuleFileSystemInitializer initializer = mock(ModuleFileSystemInitializer.class, Mockito.RETURNS_DEEP_STUBS); | |||
ComponentIndexer componentIndexer = mock(ComponentIndexer.class); | |||
ModuleInputFileCache moduleInputFileCache = mock(ModuleInputFileCache.class); | |||
private Settings settings; | |||
private FileIndexer fileIndexer; | |||
private ModuleFileSystemInitializer initializer; | |||
private ComponentIndexer componentIndexer; | |||
private ModuleInputFileCache moduleInputFileCache; | |||
private DefaultAnalysisMode mode; | |||
@Before | |||
public void setUp() { | |||
settings = new Settings(); | |||
fileIndexer = mock(FileIndexer.class); | |||
initializer = mock(ModuleFileSystemInitializer.class, Mockito.RETURNS_DEEP_STUBS); | |||
componentIndexer = mock(ComponentIndexer.class); | |||
moduleInputFileCache = mock(ModuleInputFileCache.class); | |||
mode = mock(DefaultAnalysisMode.class); | |||
} | |||
@Test | |||
public void test_equals_and_hashCode() throws Exception { | |||
DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
DefaultModuleFileSystem bar = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("bar"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("bar"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
DefaultModuleFileSystem branch = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("bar", "branch", "My project"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("bar", "branch", "My project"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
assertThat(foo1.moduleKey()).isEqualTo("foo"); | |||
assertThat(branch.moduleKey()).isEqualTo("bar:branch"); | |||
@@ -80,7 +95,7 @@ public class DefaultModuleFileSystemTest { | |||
@Test | |||
public void default_source_encoding() { | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); | |||
assertThat(fs.isDefaultJvmEncoding()).isTrue(); | |||
@@ -90,7 +105,7 @@ public class DefaultModuleFileSystemTest { | |||
public void source_encoding_is_set() { | |||
settings.setProperty(CoreProperties.ENCODING_PROPERTY, "Cp1124"); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
assertThat(fs.encoding()).isEqualTo(Charset.forName("Cp1124")); | |||
assertThat(fs.sourceCharset()).isEqualTo(Charset.forName("Cp1124")); | |||
@@ -99,6 +114,28 @@ public class DefaultModuleFileSystemTest { | |||
assertThat(fs.isDefaultJvmEncoding()).isFalse(); | |||
} | |||
@Test | |||
public void default_predicate_scan_only_changed() throws IOException { | |||
when(mode.onlyAnalyzeChanged()).thenReturn(true); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
File baseDir = temp.newFile(); | |||
InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN); | |||
InputFile testInput = new DefaultInputFile("foo", "Test.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.TEST); | |||
InputFile mainSameInput = new DefaultInputFile("foo", "MainSame.java").setModuleBaseDir(baseDir.toPath()) | |||
.setType(InputFile.Type.TEST).setStatus(Status.SAME); | |||
when(moduleInputFileCache.inputFiles()).thenReturn(Lists.newArrayList(mainInput, testInput, mainSameInput)); | |||
fs.index(); | |||
Iterable<InputFile> inputFiles = fs.inputFiles(fs.predicates().all()); | |||
assertThat(inputFiles).containsOnly(mainInput, testInput); | |||
Iterable<InputFile> allInputFiles = fs.inputFiles(); | |||
assertThat(allInputFiles).containsOnly(mainInput, mainSameInput, testInput); | |||
} | |||
@Test | |||
public void test_dirs() throws IOException { | |||
File basedir = temp.newFolder("base"); | |||
@@ -120,7 +157,7 @@ public class DefaultModuleFileSystemTest { | |||
when(initializer.tests()).thenReturn(Arrays.asList(javaTest, additionalTest)); | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath()); | |||
assertThat(fs.workDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath()); | |||
@@ -133,7 +170,7 @@ public class DefaultModuleFileSystemTest { | |||
@Test | |||
public void should_search_input_files() throws Exception { | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
File baseDir = temp.newFile(); | |||
InputFile mainInput = new DefaultInputFile("foo", "Main.java").setModuleBaseDir(baseDir.toPath()).setType(InputFile.Type.MAIN); | |||
@@ -151,7 +188,7 @@ public class DefaultModuleFileSystemTest { | |||
@Test | |||
public void should_index() { | |||
DefaultModuleFileSystem fs = new DefaultModuleFileSystem(moduleInputFileCache, | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer); | |||
new Project("foo"), settings, fileIndexer, initializer, componentIndexer, mode); | |||
verifyZeroInteractions(fileIndexer); | |||
@@ -54,7 +54,8 @@ public class DefaultFileSystem implements FileSystem { | |||
private final Path baseDir; | |||
private Path workDir; | |||
private Charset encoding; | |||
private final FilePredicates predicates; | |||
protected final FilePredicates predicates; | |||
private FilePredicate defaultPredicate; | |||
/** | |||
* Only for testing | |||
@@ -104,7 +105,12 @@ public class DefaultFileSystem implements FileSystem { | |||
this.workDir = d.getAbsoluteFile().toPath().normalize(); | |||
return this; | |||
} | |||
public DefaultFileSystem setDefaultPredicate(@Nullable FilePredicate predicate) { | |||
this.defaultPredicate = predicate; | |||
return this; | |||
} | |||
@Override | |||
public File workDir() { | |||
return workDir.toFile(); | |||
@@ -135,11 +141,24 @@ public class DefaultFileSystem implements FileSystem { | |||
throw new IllegalArgumentException(sb.toString()); | |||
} | |||
/** | |||
* Bypass default predicate to get all files/dirs indexed. | |||
* Default predicate is used when some files/dirs should not be processed by sensors. | |||
*/ | |||
public Iterable<InputFile> inputFiles() { | |||
doPreloadFiles(); | |||
return OptimizedFilePredicateAdapter.create(predicates.all()).get(cache); | |||
} | |||
@Override | |||
public Iterable<InputFile> inputFiles(FilePredicate predicate) { | |||
doPreloadFiles(); | |||
return OptimizedFilePredicateAdapter.create(predicate).get(cache); | |||
FilePredicate combinedPredicate = predicate; | |||
if(defaultPredicate != null) { | |||
combinedPredicate = predicates().and(defaultPredicate, predicate); | |||
} | |||
return OptimizedFilePredicateAdapter.create(combinedPredicate).get(cache); | |||
} | |||
@Override |