diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2013-10-09 18:19:01 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2013-10-09 18:21:36 +0200 |
commit | e8cbc899adb18eec17aa5df5db11eea06c46c009 (patch) | |
tree | d2f7693fcbacc03a42182b98c314dcaae3e39316 | |
parent | 2557634d1700e935047ecb190681a6cd418cc2b1 (diff) | |
download | sonarqube-e8cbc899adb18eec17aa5df5db11eea06c46c009.tar.gz sonarqube-e8cbc899adb18eec17aa5df5db11eea06c46c009.zip |
SONAR-3677 enhanced file system
52 files changed, 1469 insertions, 1211 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java index df5cd76ade5..74af986d814 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java @@ -19,39 +19,32 @@ */ package org.sonar.plugins.core.sensors; +import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.Project; -import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.FileType; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.scan.filesystem.FileHashCache; +import org.sonar.batch.scan.filesystem.InputFileCache; import org.sonar.core.source.SnapshotDataType; -import java.io.File; -import java.util.List; +import java.util.Map; /** * This sensor will retrieve hash of each file of the current module and store it in DB * in order to compare it during next analysis and see if the file was modified. * This is used by the incremental preview mode. - * @see org.sonar.plugins.core.batch.IncrementalPreviewFilter * @since 4.0 */ public final class FileHashSensor implements Sensor { - private ModuleFileSystem moduleFileSystem; - private PathResolver pathResolver; - private ComponentDataCache componentDataCache; - private FileHashCache fileHashCache; + private final InputFileCache fileCache; + private final ComponentDataCache componentDataCache; - public FileHashSensor(FileHashCache fileHashCache, ModuleFileSystem moduleFileSystem, PathResolver pathResolver, ComponentDataCache componentDataCache) { - this.fileHashCache = fileHashCache; - this.moduleFileSystem = moduleFileSystem; - this.pathResolver = pathResolver; + public FileHashSensor(InputFileCache fileCache, ComponentDataCache componentDataCache) { + this.fileCache = fileCache; this.componentDataCache = componentDataCache; } @@ -61,20 +54,17 @@ public final class FileHashSensor implements Sensor { @Override public void analyse(Project project, SensorContext context) { - StringBuilder fileHashMap = new StringBuilder(); - analyse(fileHashMap, project, FileType.SOURCE); - analyse(fileHashMap, project, FileType.TEST); - String fileHashes = fileHashMap.toString(); - if (StringUtils.isNotBlank(fileHashes)) { - componentDataCache.setStringData(project.getKey(), SnapshotDataType.FILE_HASH.getValue(), fileHashes); + Map<String, String> map = Maps.newHashMap(); + for (InputFile inputFile : fileCache.byModule(project.key())) { + String baseRelativePath = inputFile.attribute(InputFile.ATTRIBUTE_BASE_RELATIVE_PATH); + String hash = inputFile.attribute(InputFile.ATTRIBUTE_HASH); + if (StringUtils.isNotEmpty(baseRelativePath) && StringUtils.isNotEmpty(hash)) { + map.put(baseRelativePath, hash); + } } - } - - private void analyse(StringBuilder fileHashMap, Project project, FileType fileType) { - List<File> files = moduleFileSystem.files(FileQuery.on(fileType).onLanguage(project.getLanguageKey())); - for (File file : files) { - String hash = fileHashCache.getCurrentHash(file, moduleFileSystem.sourceCharset()); - fileHashMap.append(pathResolver.relativePath(moduleFileSystem.baseDir(), file)).append("=").append(hash).append("\n"); + if (!map.isEmpty()) { + String data = KeyValueFormat.format(map); + componentDataCache.setStringData(project.getKey(), SnapshotDataType.FILE_HASH.getValue(), data); } } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java index b46519c27d3..7e11a61698c 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java @@ -19,96 +19,63 @@ */ package org.sonar.plugins.core.sensors; -import com.google.common.base.Charsets; -import org.apache.commons.configuration.PropertiesConfiguration; -import org.apache.commons.io.FileUtils; -import org.junit.Before; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import edu.emory.mathcs.backport.java.util.Collections; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Java; import org.sonar.api.resources.Project; -import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.scan.filesystem.InputFile; import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.scan.filesystem.FileHashCache; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; +import org.sonar.batch.scan.filesystem.InputFileCache; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class FileHashSensorTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private FileHashSensor sensor; - @Rule public ExpectedException thrown = ExpectedException.none(); - private ModuleFileSystem fileSystem; + Project project = new Project("struts"); + InputFileCache fileCache = mock(InputFileCache.class); + ComponentDataCache componentDataCache = mock(ComponentDataCache.class); + FileHashSensor sensor = new FileHashSensor(fileCache, componentDataCache); - private ComponentDataCache componentDataCache; - - private Project project; + @Test + public void store_file_hashes() throws Exception { + when(fileCache.byModule("struts")).thenReturn(Lists.<InputFile>newArrayList( + InputFile.create(temp.newFile(), "src/Foo.java", ImmutableMap.of(InputFile.ATTRIBUTE_HASH, "ABC")), + InputFile.create(temp.newFile(), "src/Bar.java", ImmutableMap.of(InputFile.ATTRIBUTE_HASH, "DEF")) + )); - private FileHashCache fileHashCache; + SensorContext sensorContext = mock(SensorContext.class); + sensor.analyse(project, sensorContext); - @Before - public void prepare() { - fileSystem = mock(ModuleFileSystem.class); - when(fileSystem.sourceCharset()).thenReturn(Charsets.UTF_8); - componentDataCache = mock(ComponentDataCache.class); - fileHashCache = mock(FileHashCache.class); - sensor = new FileHashSensor(fileHashCache, fileSystem, new PathResolver(), componentDataCache); - PropertiesConfiguration conf = new PropertiesConfiguration(); - conf.setProperty("sonar.language", "java"); - project = new Project("java_project").setConfiguration(conf).setLanguage(Java.INSTANCE); + verify(componentDataCache).setStringData("struts", "file_hash", "src/Foo.java=ABC;src/Bar.java=DEF"); + verifyZeroInteractions(sensorContext); } @Test - public void improve_code_coverage() throws Exception { + public void various_tests() throws Exception { assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); assertThat(sensor.toString()).isEqualTo("FileHashSensor"); } @Test - public void computeHashes() throws Exception { - File baseDir = temp.newFolder(); - File file1 = new File(baseDir, "src/com/foo/Bar.java"); - FileUtils.write(file1, "Bar", Charsets.UTF_8); - when(fileHashCache.getCurrentHash(file1, Charsets.UTF_8)).thenReturn("barhash"); - File file2 = new File(baseDir, "src/com/foo/Foo.java"); - FileUtils.write(file2, "Foo", Charsets.UTF_8); - when(fileHashCache.getCurrentHash(file2, Charsets.UTF_8)).thenReturn("foohash"); - when(fileSystem.baseDir()).thenReturn(baseDir); - when(fileSystem.files(any(FileQuery.class))).thenReturn(Arrays.asList(file1, file2)).thenReturn(Collections.<File> emptyList()); - sensor.analyse(project, mock(SensorContext.class)); + public void dont_save_hashes_if_no_files() throws Exception { + when(fileCache.byModule("struts")).thenReturn(Collections.<InputFile>emptyList()); - verify(componentDataCache).setStringData("java_project", "hash", - "src/com/foo/Bar.java=barhash\n" - + "src/com/foo/Foo.java=foohash\n"); - } - - @Test - public void dont_save_hashes_if_no_file() throws Exception { - File baseDir = temp.newFolder(); - when(fileSystem.baseDir()).thenReturn(baseDir); - when(fileSystem.files(any(FileQuery.class))).thenReturn(Collections.<File> emptyList()); - sensor.analyse(project, mock(SensorContext.class)); + SensorContext sensorContext = mock(SensorContext.class); + sensor.analyse(project, sensorContext); - verify(componentDataCache, never()).setStringData(anyString(), anyString(), anyString()); + verifyZeroInteractions(componentDataCache); + verifyZeroInteractions(sensorContext); } } diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java index 3b877f3d479..6b883439371 100644 --- a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java +++ b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java @@ -226,11 +226,11 @@ public class MavenProjectConverter implements TaskExtension { public static void synchronizeFileSystem(MavenProject pom, DefaultModuleFileSystem into) { into.resetDirs( - pom.getBasedir(), - getBuildDir(pom), - resolvePaths(pom.getCompileSourceRoots(), pom.getBasedir()), - resolvePaths(pom.getTestCompileSourceRoots(), pom.getBasedir()), - Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir()))); + pom.getBasedir(), + getBuildDir(pom), + resolvePaths(pom.getCompileSourceRoots(), pom.getBasedir()), + resolvePaths(pom.getTestCompileSourceRoots(), pom.getBasedir()), + Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir()))); } static File resolvePath(@Nullable String path, File basedir) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java b/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java index 77f3f7dd31b..c32b0dbd68b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java @@ -35,7 +35,7 @@ public class ResourceFilters { private ResourceFilter[] filters; public ResourceFilters(ResourceFilter[] filters) { - this.filters = (filters == null ? new ResourceFilter[0] : filters); + this.filters = filters == null ? new ResourceFilter[0] : filters; } public ResourceFilters() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 37ef6fcedb4..d6ce991ec10 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -28,7 +28,6 @@ import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; -import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.DefaultProjectClasspath; import org.sonar.batch.DefaultSensorContext; import org.sonar.batch.DefaultTimeMachine; @@ -49,12 +48,7 @@ import org.sonar.batch.issue.IssueFilters; import org.sonar.batch.issue.ModuleIssues; import org.sonar.batch.phases.PhaseExecutor; import org.sonar.batch.phases.PhasesTimeProfiler; -import org.sonar.batch.scan.filesystem.DeprecatedFileSystemAdapter; -import org.sonar.batch.scan.filesystem.ExclusionFilters; -import org.sonar.batch.scan.filesystem.FileHashCache; -import org.sonar.batch.scan.filesystem.FileSystemLogger; -import org.sonar.batch.scan.filesystem.LanguageFilters; -import org.sonar.batch.scan.filesystem.ModuleFileSystemProvider; +import org.sonar.batch.scan.filesystem.*; import org.sonar.core.component.ScanPerspectives; import org.sonar.core.measure.MeasurementFilters; @@ -97,15 +91,16 @@ public class ModuleScanContainer extends ComponentContainer { Languages.class, // file system - PathResolver.class, FileExclusions.class, - LanguageFilters.class, ExclusionFilters.class, + FileHashes.class, + RemoteFileHashes.class, + FileIndexer.class, + LanguageRecognizer.class, + FileSystemLogger.class, DefaultProjectClasspath.class, new ModuleFileSystemProvider(), - DeprecatedFileSystemAdapter.class, - FileSystemLogger.class, - FileHashCache.class, + ProjectFileSystemAdapter.class, // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) getComponentByType(ResourcePersister.class).getSnapshot(module), diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index a425e6386da..4b8a82fbe4b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -28,6 +28,7 @@ import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; +import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.SonarException; import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; @@ -38,7 +39,7 @@ import org.sonar.batch.index.*; import org.sonar.batch.issue.*; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; -import org.sonar.batch.scan.filesystem.HashBuilder; +import org.sonar.batch.scan.filesystem.InputFileCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; import org.sonar.batch.source.HighlightableBuilder; @@ -117,7 +118,10 @@ public class ProjectScanContainer extends ComponentContainer { ResourceCache.class, ComponentDataCache.class, ComponentDataPersister.class, - HashBuilder.class, + + // file system + InputFileCache.class, + PathResolver.class, // issues IssueUpdater.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java b/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java index 5f83ff8654f..cce1d037c06 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java @@ -20,7 +20,9 @@ package org.sonar.batch.scan; import org.sonar.api.BatchComponent; +import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; +import org.sonar.api.utils.MessageException; public class UnsupportedProperties implements BatchComponent { private final Settings settings; @@ -31,11 +33,18 @@ public class UnsupportedProperties implements BatchComponent { public void start() { verify("sonar.light", "The property 'sonar.light' is no longer supported. Please use 'sonar.dynamicAnalysis'"); + verifyIncrementalPreviewMode(); } private void verify(String key, String message) { if (settings.hasKey(key)) { - throw new IllegalArgumentException(message); + throw MessageException.of(message); + } + } + + private void verifyIncrementalPreviewMode() { + if (settings.getBoolean(CoreProperties.INCREMENTAL_PREVIEW) && !settings.getBoolean(CoreProperties.DRY_RUN)) { + throw MessageException.of("Incremental mode is only supported with preview mode"); } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AttributeFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AttributeFilter.java new file mode 100644 index 00000000000..f2565480997 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AttributeFilter.java @@ -0,0 +1,41 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; + +import java.util.Collection; + +class AttributeFilter implements InputFileFilter { + private final String key; + private final Collection<String> values; + + AttributeFilter(String key, Collection<String> values) { + this.key = key; + this.values = values; + } + + @Override + public boolean accept(InputFile inputFile) { + String value = inputFile.attribute(key); + return values.contains(value); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java index 1d0c76a7bee..327a5663106 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java @@ -22,24 +22,17 @@ package org.sonar.batch.scan.filesystem; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.FileFilterUtils; -import org.apache.commons.io.filefilter.HiddenFileFilter; -import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.FileSystemFilter; -import org.sonar.api.scan.filesystem.FileType; +import org.sonar.api.scan.filesystem.InputFile; import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.SonarException; +import javax.annotation.CheckForNull; import java.io.File; -import java.io.FileFilter; import java.nio.charset.Charset; -import java.util.Collection; import java.util.List; /** @@ -49,42 +42,57 @@ import java.util.List; */ public class DefaultModuleFileSystem implements ModuleFileSystem { - private static final IOFileFilter DIR_FILTER = FileFilterUtils.and(HiddenFileFilter.VISIBLE, FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter("."))); + private final String moduleKey; + private final InputFileCache cache; + private final FileIndexer indexer; + private final Settings settings; - private Settings settings; private File baseDir, workingDir, buildDir; private List<File> sourceDirs = Lists.newArrayList(); private List<File> testDirs = Lists.newArrayList(); private List<File> binaryDirs = Lists.newArrayList(); - private PathResolver pathResolver = new PathResolver(); - private List<FileSystemFilter> fsFilters = Lists.newArrayList(); - private LanguageFilters languageFilters; - private FileHashCache fileHashCache; + private List<File> additionalSourceFiles = Lists.newArrayList(); + private List<File> additionalTestFiles = Lists.newArrayList(); - DefaultModuleFileSystem(FileHashCache fileHashCache) { - this.fileHashCache = fileHashCache; + public DefaultModuleFileSystem(String moduleKey, Settings settings, InputFileCache cache, FileIndexer indexer) { + this.moduleKey = moduleKey; + this.settings = settings; + this.cache = cache; + this.indexer = indexer; } + @Override + public String moduleKey() { + return moduleKey; + } + + @Override public File baseDir() { return baseDir; } + @Override + @CheckForNull public File buildDir() { return buildDir; } + @Override public List<File> sourceDirs() { return sourceDirs; } + @Override public List<File> testDirs() { return testDirs; } + @Override public List<File> binaryDirs() { return binaryDirs; } + @Override public Charset sourceCharset() { final Charset charset; String encoding = settings.getString(CoreProperties.ENCODING_PROPERTY); @@ -100,97 +108,69 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { return !settings.hasKey(CoreProperties.ENCODING_PROPERTY); } + @Override public File workingDir() { return workingDir; } - List<FileSystemFilter> filters() { - return fsFilters; + List<File> additionalSourceFiles() { + return additionalSourceFiles; } - LanguageFilters languageFilters() { - return languageFilters; + List<File> additionalTestFiles() { + return additionalTestFiles; } - public List<File> files(FileQuery query) { - boolean changedFilesOnly = false; - if (settings.getBoolean(CoreProperties.INCREMENTAL_PREVIEW)) { - if (!settings.getBoolean(CoreProperties.DRY_RUN)) { - throw new SonarException("Incremental preview is only supported with preview mode"); - } - changedFilesOnly = true; - } - return files(query, changedFilesOnly); + void setBaseDir(File baseDir) { + this.baseDir = baseDir; } - @Override - public List<File> changedFiles(FileQuery query) { - return files(query, true); + void setWorkingDir(File workingDir) { + this.workingDir = workingDir; } - private List<File> files(FileQuery query, boolean changedFilesOnly) { - List<FileSystemFilter> filters = Lists.newArrayList(fsFilters); - if (changedFilesOnly) { - filters.add(new ChangedFileFilter(fileHashCache)); - } - for (FileFilter fileFilter : query.filters()) { - filters.add(new FileFilterWrapper(fileFilter)); - } - for (String language : query.languages()) { - filters.add(new FileFilterWrapper(languageFilters.forLang(language))); - } - for (String inclusion : query.inclusions()) { - filters.add(new InclusionFilter(inclusion)); - } - for (String exclusion : query.exclusions()) { - filters.add(new ExclusionFilter(exclusion)); - } - List<File> result = Lists.newLinkedList(); - FileFilterContext context = new FileFilterContext(this); - for (FileType type : query.types()) { - context.setType(type); - switch (type) { - case SOURCE: - applyFilters(result, context, filters, sourceDirs); - break; - case TEST: - applyFilters(result, context, filters, testDirs); - break; - default: - throw new IllegalArgumentException("Unknown file type: " + type); - } - } - return result; + void setBuildDir(File buildDir) { + this.buildDir = buildDir; } - private void applyFilters(List<File> result, FileFilterContext context, - Collection<FileSystemFilter> filters, Collection<File> dirs) { - for (File dir : dirs) { - if (dir.exists()) { - context.setRelativeDir(dir); - Collection<File> files = FileUtils.listFiles(dir, HiddenFileFilter.VISIBLE, DIR_FILTER); - for (File file : files) { - if (accept(file, context, filters)) { - result.add(file); - } - } - } - } + void addSourceDir(File d) { + this.sourceDirs.add(d); } - private boolean accept(File file, FileFilterContext context, Collection<FileSystemFilter> filters) { - context.setRelativePath(pathResolver.relativePath(context.relativeDir(), file)); - try { - context.setCanonicalPath(file.getCanonicalPath()); - } catch (Exception e) { - throw new IllegalStateException("Fail to get the canonical path of: " + file); - } - for (FileSystemFilter filter : filters) { - if (!filter.accept(file, context)) { - return false; + void addTestDir(File d) { + this.testDirs.add(d); + } + + void addBinaryDir(File d) { + this.binaryDirs.add(d); + } + + void setAdditionalSourceFiles(List<File> files) { + this.additionalSourceFiles = files; + } + + void setAdditionalTestFiles(List<File> files) { + this.additionalTestFiles = files; + } + + /** + * @since 4.0 + */ + public Iterable<InputFile> inputFiles(FileQuery query) { + List<InputFile> result = Lists.newArrayList(); + + FileQueryFilter filter = new FileQueryFilter(settings, query); + for (InputFile input : cache.byModule(moduleKey)) { + if (filter.accept(input)) { + result.add(input); } } - return true; + return result; + } + + @Override + public List<File> files(FileQuery query) { + return InputFile.toFiles(inputFiles(query)); } public void resetDirs(File basedir, File buildDir, List<File> sourceDirs, List<File> testDirs, List<File> binaryDirs) { @@ -200,6 +180,11 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { this.sourceDirs = existingDirs(sourceDirs); this.testDirs = existingDirs(testDirs); this.binaryDirs = existingDirs(binaryDirs); + indexer.index(this); + } + + void index() { + indexer.index(this); } private List<File> existingDirs(List<File> dirs) { @@ -212,50 +197,21 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { return builder.build(); } - DefaultModuleFileSystem setSettings(Settings settings) { - this.settings = settings; - return this; - } - - DefaultModuleFileSystem setBaseDir(File baseDir) { - this.baseDir = baseDir; - return this; - } - - DefaultModuleFileSystem setWorkingDir(File workingDir) { - this.workingDir = workingDir; - return this; - } - DefaultModuleFileSystem setBuildDir(File buildDir) { - this.buildDir = buildDir; - return this; - } - - DefaultModuleFileSystem addFilters(FileSystemFilter... f) { - for (FileSystemFilter filter : f) { - this.fsFilters.add(filter); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - return this; - } - - DefaultModuleFileSystem setLanguageFilters(LanguageFilters languageFilters) { - this.languageFilters = languageFilters; - return this; - } - - DefaultModuleFileSystem addSourceDir(File d) { - this.sourceDirs.add(d); - return this; - } - - DefaultModuleFileSystem addTestDir(File d) { - this.testDirs.add(d); - return this; + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultModuleFileSystem that = (DefaultModuleFileSystem) o; + return moduleKey.equals(that.moduleKey); } - DefaultModuleFileSystem addBinaryDir(File d) { - this.binaryDirs.add(d); - return this; + @Override + public int hashCode() { + return moduleKey.hashCode(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilter.java index 172b58b28fa..5cbe77975fb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilter.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilter.java @@ -19,19 +19,19 @@ */ package org.sonar.batch.scan.filesystem; -import org.sonar.api.scan.filesystem.FileSystemFilter; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; -import java.io.File; - -class ExclusionFilter implements FileSystemFilter { +class ExclusionFilter implements InputFileFilter { private final PathPattern pattern; ExclusionFilter(String s) { this.pattern = PathPattern.create(s); } - public boolean accept(File file, FileSystemFilter.Context context) { - return !pattern.match(context); + @Override + public boolean accept(InputFile inputFile) { + return !pattern.match(inputFile); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java index e8e3eab9943..2e1130d55be 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java @@ -26,12 +26,10 @@ import org.sonar.api.batch.ResourceFilter; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.scan.filesystem.FileExclusions; -import org.sonar.api.scan.filesystem.FileSystemFilter; -import org.sonar.api.scan.filesystem.FileType; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; -import java.io.File; - -public class ExclusionFilters implements FileSystemFilter, ResourceFilter, BatchComponent { +public class ExclusionFilters implements InputFileFilter, ResourceFilter, BatchComponent { private final FileExclusions exclusionSettings; public ExclusionFilters(FileExclusions exclusions) { @@ -45,7 +43,6 @@ public class ExclusionFilters implements FileSystemFilter, ResourceFilter, Batch log("Excluded tests: ", testExclusions()); } - private void log(String title, PathPattern[] patterns) { if (patterns.length > 0) { Logger log = LoggerFactory.getLogger(ExclusionFilters.class); @@ -56,26 +53,38 @@ public class ExclusionFilters implements FileSystemFilter, ResourceFilter, Batch } } - public boolean accept(File file, Context context) { - PathPattern[] inclusionPatterns = context.type() == FileType.TEST ? testInclusions() : sourceInclusions(); - if (inclusionPatterns.length > 0) { + @Override + public boolean accept(InputFile inputFile) { + String type = inputFile.attribute(InputFile.ATTRIBUTE_TYPE); + PathPattern[] inclusionPatterns = null; + PathPattern[] exclusionPatterns = null; + if (InputFile.TYPE_SOURCE.equals(type)) { + inclusionPatterns = sourceInclusions(); + exclusionPatterns = sourceExclusions(); + } else if (InputFile.TYPE_TEST.equals(type)) { + inclusionPatterns = testInclusions(); + exclusionPatterns = testExclusions(); + } + if (inclusionPatterns != null && inclusionPatterns.length > 0) { boolean matchInclusion = false; for (PathPattern pattern : inclusionPatterns) { - matchInclusion |= pattern.match(context); + matchInclusion |= pattern.match(inputFile); } if (!matchInclusion) { return false; } } - PathPattern[] exclusionPatterns = context.type() == FileType.TEST ? testExclusions() : sourceExclusions(); - for (PathPattern pattern : exclusionPatterns) { - if (pattern.match(context)) { - return false; + if (exclusionPatterns != null && exclusionPatterns.length > 0) { + for (PathPattern pattern : exclusionPatterns) { + if (pattern.match(inputFile)) { + return false; + } } } return true; } + public boolean isIgnored(Resource resource) { if (ResourceUtils.isFile(resource)) { PathPattern[] inclusionPatterns = ResourceUtils.isUnitTestClass(resource) ? testInclusions() : sourceInclusions(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java deleted file mode 100644 index 738291750c0..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.scan.filesystem; - -import org.apache.commons.io.FilenameUtils; -import org.sonar.api.batch.FileFilter; -import org.sonar.api.scan.filesystem.FileType; -import org.sonar.api.scan.filesystem.ModuleFileSystem; - -import java.io.File; - -class FileFilterContext implements FileFilter.Context { - private final ModuleFileSystem fileSystem; - private FileType type; - private File sourceDir; - private String fileRelativePath; - private String fileCanonicalPath; - - FileFilterContext(ModuleFileSystem fileSystem) { - this.fileSystem = fileSystem; - } - - public ModuleFileSystem fileSystem() { - return fileSystem; - } - - public FileType type() { - return type; - } - - FileFilterContext setType(FileType t) { - this.type = t; - return this; - } - - public File relativeDir() { - return sourceDir; - } - - public String relativePath() { - return fileRelativePath; - } - - public String canonicalPath() { - return fileCanonicalPath; - } - - FileFilterContext setRelativeDir(File d) { - this.sourceDir = d; - return this; - } - - FileFilterContext setRelativePath(String s) { - this.fileRelativePath = s; - return this; - } - - FileFilterContext setCanonicalPath(String s) { - this.fileCanonicalPath = FilenameUtils.separatorsToUnix(s); - return this; - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashCache.java deleted file mode 100644 index c17733c7be9..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashCache.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.scan.filesystem; - -import com.google.common.collect.Maps; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.picocontainer.Startable; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.api.utils.SonarException; -import org.sonar.batch.components.PastSnapshot; -import org.sonar.batch.components.PastSnapshotFinder; -import org.sonar.core.source.SnapshotDataType; -import org.sonar.core.source.jdbc.SnapshotDataDao; -import org.sonar.core.source.jdbc.SnapshotDataDto; - -import javax.annotation.CheckForNull; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public class FileHashCache implements BatchComponent, Startable { - - private Map<String, String> currentHashCache = Maps.newHashMap(); - private Map<String, String> previousHashCache = Maps.newHashMap(); - - private PathResolver pathResolver; - private HashBuilder hashBuilder; - private SnapshotDataDao snapshotDataDao; - private PastSnapshotFinder pastSnapshotFinder; - private Snapshot snapshot; - private ProjectDefinition module; - - public FileHashCache(ProjectDefinition module, PathResolver pathResolver, HashBuilder hashBuilder, - Snapshot snapshot, - SnapshotDataDao snapshotDataDao, - PastSnapshotFinder pastSnapshotFinder) { - this.module = module; - this.pathResolver = pathResolver; - this.hashBuilder = hashBuilder; - this.snapshot = snapshot; - this.snapshotDataDao = snapshotDataDao; - this.pastSnapshotFinder = pastSnapshotFinder; - } - - @Override - public void start() { - // Extract previous checksum of all files of this module and store them in a map - PastSnapshot pastSnapshot = pastSnapshotFinder.findPreviousAnalysis(snapshot); - if (pastSnapshot.isRelatedToSnapshot()) { - Collection<SnapshotDataDto> selectSnapshotData = snapshotDataDao.selectSnapshotData(pastSnapshot.getProjectSnapshot().getId().longValue(), - Arrays.asList(SnapshotDataType.FILE_HASH.getValue())); - if (!selectSnapshotData.isEmpty()) { - SnapshotDataDto snapshotDataDto = selectSnapshotData.iterator().next(); - String data = snapshotDataDto.getData(); - try { - List<String> lines = IOUtils.readLines(new StringReader(data)); - for (String line : lines) { - String[] keyValue = StringUtils.split(line, "="); - if (keyValue.length == 2) { - previousHashCache.put(keyValue[0], keyValue[1]); - } - } - } catch (IOException e) { - throw new SonarException("Unable to read previous file hashes", e); - } - } - } - } - - public String getCurrentHash(File file, Charset sourceCharset) { - String relativePath = pathResolver.relativePath(module.getBaseDir(), file); - if (!currentHashCache.containsKey(relativePath)) { - currentHashCache.put(relativePath, hashBuilder.computeHashNormalizeLineEnds(file, sourceCharset)); - } - return currentHashCache.get(relativePath); - } - - @CheckForNull - public String getPreviousHash(File file) { - String relativePath = pathResolver.relativePath(module.getBaseDir(), file); - return previousHashCache.get(relativePath); - } - - @Override - public void stop() { - // Nothing to do - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/HashBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashDigest.java index 2db7946e67c..e7f5cb31827 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/HashBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashDigest.java @@ -22,30 +22,29 @@ package org.sonar.batch.scan.filesystem; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; -import org.sonar.api.BatchExtension; -import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.api.utils.SonarException; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.nio.charset.Charset; import java.security.MessageDigest; /** - * @since 4.0 + * Computes hash of files. Ends of Lines are ignored, so files with + * same content but different EOL encoding have the same hash. */ -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public final class HashBuilder implements BatchExtension { +class FileHashDigest { + + // This singleton aims only to increase the coverage by allowing + // to test the private method ! + static final FileHashDigest INSTANCE = new FileHashDigest(); + + private FileHashDigest() { + } /** * Compute hash of a file ignoring line ends differences. * Maximum performance is needed. */ - public String computeHashNormalizeLineEnds(File file, Charset charset) { + String hash(File file, Charset charset) { Reader reader = null; try { MessageDigest md5Digest = DigestUtils.getMd5Digest(); @@ -72,14 +71,14 @@ public final class HashBuilder implements BatchExtension { } return Hex.encodeHexString(md5Digest.digest()); } catch (IOException e) { - throw new SonarException("Unable to compute file hash", e); + throw new IllegalStateException(String.format("Fail to compute hash of file %s with charset %s", file.getAbsolutePath(), charset), e); } finally { IOUtils.closeQuietly(reader); } } - public static byte[] charToBytesUTF(char c) { - char[] buffer = new char[] {c}; + private byte[] charToBytesUTF(char c) { + char[] buffer = new char[]{c}; byte[] b = new byte[buffer.length << 1]; for (int i = 0; i < buffer.length; i++) { int bpos = i << 1; diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ChangedFileFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashes.java index 509fb2ef08b..5ffcc11b9dc 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ChangedFileFilter.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashes.java @@ -19,30 +19,30 @@ */ package org.sonar.batch.scan.filesystem; -import org.sonar.api.scan.filesystem.FileSystemFilter; +import org.sonar.api.BatchComponent; +import javax.annotation.CheckForNull; import java.io.File; +import java.nio.charset.Charset; /** - * When enabled this filter will only allow modified files to be analyzed. - * @since 4.0 + * Facade for local and remote file hashes */ -class ChangedFileFilter implements FileSystemFilter { +public class FileHashes implements BatchComponent { - private FileHashCache fileHashCache; + private final RemoteFileHashes remoteFileHashes; - public ChangedFileFilter(FileHashCache fileHashCache) { - this.fileHashCache = fileHashCache; + public FileHashes(RemoteFileHashes remoteFileHashes) { + this.remoteFileHashes = remoteFileHashes; } - @Override - public boolean accept(File file, Context context) { - String previousHash = fileHashCache.getPreviousHash(file); - if (previousHash == null) { - return true; - } - String currentHash = fileHashCache.getCurrentHash(file, context.fileSystem().sourceCharset()); - return !currentHash.equals(previousHash); + @CheckForNull + public String hash(File file, Charset charset) { + return FileHashDigest.INSTANCE.hash(file, charset); } + @CheckForNull + public String remoteHash(String baseRelativePath) { + return remoteFileHashes.remoteHash(baseRelativePath); + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java new file mode 100644 index 00000000000..4ffcb0ee37c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java @@ -0,0 +1,151 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import com.google.common.collect.Maps; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.filefilter.HiddenFileFilter; +import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; +import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.scan.filesystem.PathResolver; + +import javax.annotation.Nullable; +import java.io.File; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Index input files into {@link InputFileCache}. + */ +public class FileIndexer implements BatchComponent { + + private static final IOFileFilter DIR_FILTER = FileFilterUtils.and(HiddenFileFilter.VISIBLE, FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter("."))); + private static final IOFileFilter FILE_FILTER = HiddenFileFilter.VISIBLE; + + private final PathResolver pathResolver = new PathResolver(); + private final List<InputFileFilter> filters; + private final LanguageRecognizer languageRecognizer; + private final InputFileCache cache; + private final FileHashes fileHashes; + + // TODO support deprecated filters + public FileIndexer(List<InputFileFilter> filters, LanguageRecognizer languageRecognizer, + InputFileCache cache, FileHashes fileHashes) { + this.filters = filters; + this.languageRecognizer = languageRecognizer; + this.cache = cache; + this.fileHashes = fileHashes; + } + + public void index(ModuleFileSystem fileSystem) { + Logger logger = LoggerFactory.getLogger(FileIndexer.class); + logger.info("Index files"); + + cache.removeModule(fileSystem.moduleKey()); + int count = 0; + for (File sourceDir : fileSystem.sourceDirs()) { + count += indexDirectory(fileSystem, sourceDir, InputFile.TYPE_SOURCE); + } + for (File testDir : fileSystem.testDirs()) { + count += indexDirectory(fileSystem, testDir, InputFile.TYPE_TEST); + } + + // TODO index additional sources and test files + + logger.info(String.format("%d files indexed", count)); + } + + private int indexDirectory(ModuleFileSystem fileSystem, File sourceDir, String type) { + int count = 0; + Collection<File> files = FileUtils.listFiles(sourceDir, FILE_FILTER, DIR_FILTER); + for (File file : files) { + InputFile input = newInputFile(fileSystem, sourceDir, type, file); + if (accept(input)) { + cache.put(fileSystem.moduleKey(), input); + count++; + } + } + return count; + } + + private InputFile newInputFile(ModuleFileSystem fileSystem, File sourceDir, String type, File file) { + try { + Map<String, String> attributes = Maps.newHashMap(); + + // paths + String baseRelativePath = pathResolver.relativePath(fileSystem.baseDir(), file); + set(attributes, InputFile.ATTRIBUTE_SOURCEDIR_PATH, FilenameUtils.normalize(sourceDir.getCanonicalPath(), true)); + set(attributes, InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, pathResolver.relativePath(sourceDir, file)); + set(attributes, InputFile.ATTRIBUTE_CANONICAL_PATH, FilenameUtils.normalize(file.getCanonicalPath(), true)); + + // other metadata + set(attributes, InputFile.ATTRIBUTE_TYPE, type); + String extension = FilenameUtils.getExtension(file.getName()); + set(attributes, InputFile.ATTRIBUTE_EXTENSION, extension); + set(attributes, InputFile.ATTRIBUTE_LANGUAGE, languageRecognizer.ofExtension(extension)); + initStatus(file, fileSystem.sourceCharset(), baseRelativePath, attributes); + + return InputFile.create(file, baseRelativePath, attributes); + + } catch (Exception e) { + throw new IllegalStateException("Fail to read file: " + file.getAbsolutePath(), e); + } + } + + private void initStatus(File file, Charset charset, String baseRelativePath, Map<String, String> attributes) { + String hash = fileHashes.hash(file, charset); + set(attributes, InputFile.ATTRIBUTE_HASH, hash); + + String remoteHash = fileHashes.remoteHash(baseRelativePath); + // currently no need to store this remote hash in attributes + if (StringUtils.equals(hash, remoteHash)) { + set(attributes, InputFile.ATTRIBUTE_STATUS, InputFile.STATUS_SAME); + } else if (StringUtils.isEmpty(remoteHash)) { + set(attributes, InputFile.ATTRIBUTE_STATUS, InputFile.STATUS_ADDED); + } else { + set(attributes, InputFile.ATTRIBUTE_STATUS, InputFile.STATUS_CHANGED); + } + } + + private void set(Map<String, String> attributes, String key, @Nullable String value) { + if (value != null) { + attributes.put(key, value); + } + } + + private boolean accept(InputFile inputFile) { + for (InputFileFilter filter : filters) { + if (!filter.accept(inputFile)) { + return false; + } + } + return true; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileQueryFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileQueryFilter.java new file mode 100644 index 00000000000..b62694c3b6c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileQueryFilter.java @@ -0,0 +1,67 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import com.google.common.collect.Lists; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.scan.filesystem.FileQuery; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +class FileQueryFilter { + + private final List<InputFileFilter> filters; + + FileQueryFilter(Settings settings, FileQuery query) { + filters = Lists.newArrayList(); + for (String pattern : query.inclusions()) { + filters.add(new InclusionFilter(pattern)); + } + for (String pattern : query.exclusions()) { + filters.add(new ExclusionFilter(pattern)); + } + for (Map.Entry<String, Collection<String>> entry : query.attributes().entrySet()) { + filters.add(new AttributeFilter(entry.getKey(), entry.getValue())); + } + + // TODO speed-up the following algorithm. Cache ? + if (settings.getBoolean(CoreProperties.INCREMENTAL_PREVIEW)) { + Collection<String> status = query.attributes().get(InputFile.ATTRIBUTE_STATUS); + if (status == null || status.isEmpty()) { + // TODO should be not(SAME) instead of is(ADDED, CHANGED) + filters.add(new AttributeFilter(InputFile.ATTRIBUTE_STATUS, Lists.newArrayList(InputFile.STATUS_ADDED, InputFile.STATUS_CHANGED))); + } + } + } + + boolean accept(InputFile inputFile) { + for (InputFileFilter filter : filters) { + if (!filter.accept(inputFile)) { + return false; + } + } + return true; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileSystemLogger.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileSystemLogger.java index c52b00c32df..2d402d358b0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileSystemLogger.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileSystemLogger.java @@ -23,13 +23,14 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; import java.io.File; import java.nio.charset.Charset; import java.util.List; import java.util.Locale; -public class FileSystemLogger { +public class FileSystemLogger implements BatchComponent { private final DefaultModuleFileSystem fs; diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFilter.java index 43c776121e8..ef5d391e0cc 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFilter.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFilter.java @@ -19,19 +19,19 @@ */ package org.sonar.batch.scan.filesystem; -import org.sonar.api.scan.filesystem.FileSystemFilter; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.api.scan.filesystem.InputFileFilter; -import java.io.File; - -class InclusionFilter implements FileSystemFilter { +class InclusionFilter implements InputFileFilter { private final PathPattern pattern; InclusionFilter(String s) { this.pattern = PathPattern.create(s); } - public boolean accept(File file, FileSystemFilter.Context context) { - return pattern.match(context); + @Override + public boolean accept(InputFile inputFile) { + return pattern.match(inputFile); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFilters.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java index fcc887b7d49..f45f8088f85 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFilters.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java @@ -19,31 +19,39 @@ */ package org.sonar.batch.scan.filesystem; -import org.apache.commons.io.IOCase; -import org.apache.commons.io.filefilter.FalseFileFilter; -import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.filefilter.SuffixFileFilter; -import org.apache.commons.io.filefilter.TrueFileFilter; import org.sonar.api.BatchComponent; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Caches; -public class LanguageFilters implements BatchComponent { - private final Languages languages; +/** + * Cache of all files. This cache is shared amongst all project modules. Inclusion and + * exclusion patterns are already applied. + */ +public class InputFileCache implements BatchComponent { + + // module key -> path -> InputFile + private final Cache<String, InputFile> cache; + + public InputFileCache(Caches caches) { + cache = caches.createCache("inputFiles"); + } + + public Iterable<InputFile> byModule(String moduleKey) { + return cache.values(moduleKey); + } + + public InputFileCache removeModule(String moduleKey) { + cache.clear(moduleKey); + return this; + } - public LanguageFilters(Languages languages) { - this.languages = languages; + public Iterable<InputFile> all() { + return cache.allValues(); } - public IOFileFilter forLang(String lang) { - Language language = languages.get(lang); - if (language == null) { - return FalseFileFilter.FALSE; - } - String[] suffixes = language.getFileSuffixes(); - if (suffixes != null && suffixes.length>0) { - return new SuffixFileFilter(suffixes, IOCase.SENSITIVE); - } - return TrueFileFilter.TRUE; + public InputFileCache put(String moduleKey, InputFile file) { + cache.put(moduleKey, file.path(), file); + return this; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java new file mode 100644 index 00000000000..61396830599 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchComponent; +import org.sonar.api.resources.Language; + +import javax.annotation.CheckForNull; +import java.util.Map; + +/** + * Based on file extensions. + */ +public class LanguageRecognizer implements BatchComponent { + + private final Map<String, String> byExtensions = Maps.newHashMap(); + + public LanguageRecognizer(Language[] languages) { + for (Language language : languages) { + for (String suffix : language.getFileSuffixes()) { + String extension = StringUtils.removeStart(suffix, "."); + + String s = byExtensions.get(extension); + if (s != null) { + throw new IllegalStateException(String.format( + "File extension '%s' is declared by two languages: %s and %s", extension, s, language.getKey() + )); + } + byExtensions.put(extension, language.getKey()); + } + } + } + + // TODO what about cobol files without extension ? + @CheckForNull + String ofExtension(String fileExtension) { + if (StringUtils.isNotBlank(fileExtension)) { + return byExtensions.get(fileExtension); + } + return null; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java index 3efae8a0ec0..7bd84e87ef7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java @@ -19,13 +19,11 @@ */ package org.sonar.batch.scan.filesystem; -import com.google.common.collect.ImmutableSet; import org.apache.commons.io.FileUtils; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.FileSystemFilter; -import org.sonar.api.scan.filesystem.FileType; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.TempDirectories; @@ -37,22 +35,21 @@ import java.util.List; */ public class ModuleFileSystemProvider extends ProviderAdapter { + private PathResolver pathResolver = new PathResolver(); private DefaultModuleFileSystem singleton; - public DefaultModuleFileSystem provide(ProjectDefinition module, PathResolver pathResolver, TempDirectories tempDirectories, - LanguageFilters languageFilters, Settings settings, FileSystemFilter[] pluginFileFilters, - FileHashCache fileHashCache) { + public DefaultModuleFileSystem provide( + ProjectDefinition module, TempDirectories tempDirectories,Settings settings, InputFileCache cache, FileIndexer indexer) { + if (singleton == null) { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(fileHashCache); - fs.setLanguageFilters(languageFilters); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(module.getKey(), settings, cache, indexer); fs.setBaseDir(module.getBaseDir()); fs.setBuildDir(module.getBuildDir()); - fs.setSettings(settings); fs.setWorkingDir(guessWorkingDir(module, tempDirectories)); - fs.addFilters(pluginFileFilters); - initBinaryDirs(module, pathResolver, fs); - initSources(module, pathResolver, fs); - initTests(module, pathResolver, fs); + initBinaryDirs(module, fs); + initSources(module, fs); + initTests(module, fs); + fs.index(); singleton = fs; } return singleton; @@ -72,7 +69,7 @@ public class ModuleFileSystemProvider extends ProviderAdapter { return workDir; } - private void initSources(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem fs) { + private void initSources(ProjectDefinition module, DefaultModuleFileSystem fs) { for (String sourcePath : module.getSourceDirs()) { File dir = pathResolver.relativeFile(module.getBaseDir(), sourcePath); if (dir.isDirectory() && dir.exists()) { @@ -80,12 +77,10 @@ public class ModuleFileSystemProvider extends ProviderAdapter { } } List<File> sourceFiles = pathResolver.relativeFiles(module.getBaseDir(), module.getSourceFiles()); - if (!sourceFiles.isEmpty()) { - fs.addFilters(new WhiteListFileFilter(FileType.SOURCE, ImmutableSet.copyOf(sourceFiles))); - } + fs.setAdditionalSourceFiles(sourceFiles); } - private void initTests(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem fs) { + private void initTests(ProjectDefinition module, DefaultModuleFileSystem fs) { for (String testPath : module.getTestDirs()) { File dir = pathResolver.relativeFile(module.getBaseDir(), testPath); if (dir.exists() && dir.isDirectory()) { @@ -93,12 +88,10 @@ public class ModuleFileSystemProvider extends ProviderAdapter { } } List<File> testFiles = pathResolver.relativeFiles(module.getBaseDir(), module.getTestFiles()); - if (!testFiles.isEmpty()) { - fs.addFilters(new WhiteListFileFilter(FileType.TEST, ImmutableSet.copyOf(testFiles))); - } + fs.setAdditionalTestFiles(testFiles); } - private void initBinaryDirs(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem fs) { + private void initBinaryDirs(ProjectDefinition module, DefaultModuleFileSystem fs) { for (String path : module.getBinaries()) { File dir = pathResolver.relativeFile(module.getBaseDir(), path); fs.addBinaryDir(dir); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathPattern.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathPattern.java index bd6eb8479e0..2df7c53e4d6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathPattern.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathPattern.java @@ -21,20 +21,21 @@ package org.sonar.batch.scan.filesystem; import org.apache.commons.lang.StringUtils; import org.sonar.api.resources.Resource; -import org.sonar.api.scan.filesystem.FileSystemFilter; +import org.sonar.api.scan.filesystem.InputFile; import org.sonar.api.utils.WildcardPattern; abstract class PathPattern { + final WildcardPattern pattern; PathPattern(String pattern) { this.pattern = WildcardPattern.create(pattern); } - abstract boolean match(FileSystemFilter.Context context); - abstract boolean match(Resource resource); + abstract boolean match(InputFile inputFile); + abstract boolean supportResource(); static PathPattern create(String s) { @@ -58,14 +59,17 @@ abstract class PathPattern { super(pattern); } - boolean match(FileSystemFilter.Context context) { - return pattern.match(context.canonicalPath()); + @Override + boolean match(InputFile inputFile) { + return pattern.match(inputFile.attribute(InputFile.ATTRIBUTE_CANONICAL_PATH)); } + @Override boolean match(Resource resource) { return false; } + @Override boolean supportResource() { return false; } @@ -76,19 +80,26 @@ abstract class PathPattern { } } + /** + * Path relative to source directory + */ private static class RelativePathPattern extends PathPattern { private RelativePathPattern(String pattern) { super(pattern); } - boolean match(FileSystemFilter.Context context) { - return pattern.match(context.relativePath()); + @Override + boolean match(InputFile inputFile) { + String path = inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH); + return path != null && pattern.match(path); } + @Override boolean match(Resource resource) { return resource.matchFilePattern(pattern.toString()); } + @Override boolean supportResource() { return true; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ProjectFileSystemAdapter.java index 4379089c86e..1f5ab333658 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ProjectFileSystemAdapter.java @@ -24,19 +24,12 @@ import com.google.common.collect.Lists; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.CharEncoding; import org.apache.maven.project.MavenProject; -import org.sonar.api.resources.InputFile; -import org.sonar.api.resources.InputFileUtils; -import org.sonar.api.resources.Java; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; -import org.sonar.api.resources.Resource; +import org.sonar.api.resources.*; import org.sonar.api.scan.filesystem.FileQuery; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.SonarException; import javax.annotation.Nullable; - import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -47,14 +40,14 @@ import java.util.List; * * @since 3.5 */ -public class DeprecatedFileSystemAdapter implements ProjectFileSystem { +public class ProjectFileSystemAdapter implements ProjectFileSystem { private final DefaultModuleFileSystem target; private final PathResolver pathResolver = new PathResolver(); private final MavenProject pom; - public DeprecatedFileSystemAdapter(DefaultModuleFileSystem target, Project project, @Nullable MavenProject pom) { + public ProjectFileSystemAdapter(DefaultModuleFileSystem target, Project project, @Nullable MavenProject pom) { this.target = target; this.pom = pom; @@ -63,7 +56,7 @@ public class DeprecatedFileSystemAdapter implements ProjectFileSystem { project.setFileSystem(this); } - public DeprecatedFileSystemAdapter(DefaultModuleFileSystem target, Project project) { + public ProjectFileSystemAdapter(DefaultModuleFileSystem target, Project project) { this(target, project, null); } @@ -192,11 +185,12 @@ public class DeprecatedFileSystemAdapter implements ProjectFileSystem { public List<InputFile> mainFiles(String... langs) { List<InputFile> result = Lists.newArrayList(); - List<File> files = target.files(FileQuery.onSource().onLanguage(langs)); - for (File file : files) { - PathResolver.RelativePath relativePath = pathResolver.relativePath(getSourceDirs(), file); - if (relativePath != null) { - result.add(InputFileUtils.create(relativePath.dir(), relativePath.path())); + Iterable<org.sonar.api.scan.filesystem.InputFile> files = target.inputFiles(FileQuery.onSource().onLanguage(langs)); + for (org.sonar.api.scan.filesystem.InputFile file : files) { + String sourceDir = file.attribute(org.sonar.api.scan.filesystem.InputFile.ATTRIBUTE_SOURCEDIR_PATH); + String sourceRelativePath = file.attribute(org.sonar.api.scan.filesystem.InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH); + if (sourceDir != null && sourceRelativePath != null) { + result.add(InputFileUtils.create(new File(sourceDir), sourceRelativePath)); } } return result; @@ -204,14 +198,14 @@ public class DeprecatedFileSystemAdapter implements ProjectFileSystem { public List<InputFile> testFiles(String... langs) { List<InputFile> result = Lists.newArrayList(); - List<File> files = target.files(FileQuery.onTest().onLanguage(langs)); - for (File file : files) { - PathResolver.RelativePath relativePath = pathResolver.relativePath(getTestDirs(), file); - if (relativePath != null) { - result.add(InputFileUtils.create(relativePath.dir(), relativePath.path())); + Iterable<org.sonar.api.scan.filesystem.InputFile> files = target.inputFiles(FileQuery.onTest().onLanguage(langs)); + for (org.sonar.api.scan.filesystem.InputFile file : files) { + String sourceDir = file.attribute(org.sonar.api.scan.filesystem.InputFile.ATTRIBUTE_SOURCEDIR_PATH); + String sourceRelativePath = file.attribute(org.sonar.api.scan.filesystem.InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH); + if (sourceDir != null && sourceRelativePath != null) { + result.add(InputFileUtils.create(new File(sourceDir), sourceRelativePath)); } } - return result; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/RemoteFileHashes.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/RemoteFileHashes.java new file mode 100644 index 00000000000..b2d8853e799 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/RemoteFileHashes.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import com.google.common.collect.Maps; +import org.picocontainer.Startable; +import org.sonar.api.BatchComponent; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.batch.components.PastSnapshot; +import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.core.source.SnapshotDataType; +import org.sonar.core.source.jdbc.SnapshotDataDao; +import org.sonar.core.source.jdbc.SnapshotDataDto; + +import javax.annotation.CheckForNull; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +public class RemoteFileHashes implements BatchComponent, Startable { + + private final SnapshotDataDao dao; + private final PastSnapshotFinder pastSnapshotFinder; + private final Snapshot snapshot; + + private Map<String, String> pathToHash = Maps.newHashMap(); + + public RemoteFileHashes(Snapshot snapshot, SnapshotDataDao dao, PastSnapshotFinder pastSnapshotFinder) { + this.snapshot = snapshot; + this.dao = dao; + this.pastSnapshotFinder = pastSnapshotFinder; + } + + @Override + public void start() { + // Extract previous checksum of all files of this module and store them in a map + PastSnapshot pastSnapshot = pastSnapshotFinder.findPreviousAnalysis(snapshot); + if (pastSnapshot.isRelatedToSnapshot()) { + Collection<SnapshotDataDto> selectSnapshotData = dao.selectSnapshotData( + pastSnapshot.getProjectSnapshot().getId().longValue(), + Arrays.asList(SnapshotDataType.FILE_HASH.getValue()) + ); + if (!selectSnapshotData.isEmpty()) { + SnapshotDataDto snapshotDataDto = selectSnapshotData.iterator().next(); + String data = snapshotDataDto.getData(); + pathToHash = KeyValueFormat.parse(data); + } + } + } + + @CheckForNull + public String remoteHash(String baseRelativePath) { + return pathToHash.get(baseRelativePath); + } + + @Override + public void stop() { + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java deleted file mode 100644 index fb4cdb6b64e..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.scan.filesystem; - -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; -import org.sonar.api.scan.filesystem.FileSystemFilter; -import org.sonar.api.scan.filesystem.FileType; - -import java.io.File; -import java.util.Set; - -/** - * @since 3.5 - */ -class WhiteListFileFilter implements FileSystemFilter { - private final FileType fileType; - private final Set<File> files; - - WhiteListFileFilter(FileType fileType, Set<File> files) { - Preconditions.checkNotNull(fileType); - Preconditions.checkNotNull(files); - this.fileType = fileType; - this.files = files; - } - - public boolean accept(File file, Context context) { - return !context.type().equals(fileType) || files.contains(file); - } - - @Override - public String toString() { - return StringUtils.capitalize(fileType.name().toLowerCase()) + " files: " + SystemUtils.LINE_SEPARATOR + - Joiner.on(SystemUtils.LINE_SEPARATOR).join(files); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java index 648bee96caa..f5246e64099 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java @@ -22,8 +22,9 @@ package org.sonar.batch.scan; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; -import org.sonar.batch.scan.UnsupportedProperties; +import org.sonar.api.utils.MessageException; public class UnsupportedPropertiesTest { @@ -32,10 +33,11 @@ public class UnsupportedPropertiesTest { @Test public void should_fail_if_sonar_light_is_set() { + thrown.expect(MessageException.class); + thrown.expectMessage("The property 'sonar.light' is no longer supported. Please use 'sonar.dynamicAnalysis'"); + Settings settings = new Settings(); settings.setProperty("sonar.light", true); - - thrown.expect(IllegalArgumentException.class); new UnsupportedProperties(settings).start(); } @@ -44,4 +46,23 @@ public class UnsupportedPropertiesTest { Settings settings = new Settings(); new UnsupportedProperties(settings).start(); } + + @Test + public void should_fail_if_incremental_but_not_preview_mode() { + thrown.expect(MessageException.class); + thrown.expectMessage("Incremental mode is only supported with preview mode"); + + Settings settings = new Settings(); + settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); + settings.setProperty(CoreProperties.DRY_RUN, false); + new UnsupportedProperties(settings).start(); + } + + @Test + public void should_not_fail_if_incremental_preview_mode() { + Settings settings = new Settings(); + settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); + settings.setProperty(CoreProperties.DRY_RUN, true); + new UnsupportedProperties(settings).start(); + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileFilterContextTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AttributeFilterTest.java index aa74da4c145..1ce0a8a4019 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileFilterContextTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/AttributeFilterTest.java @@ -19,27 +19,27 @@ */ package org.sonar.batch.scan.filesystem; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.scan.filesystem.ModuleFileSystem; - -import java.io.File; -import java.io.IOException; +import org.sonar.api.scan.filesystem.InputFile; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; -public class FileFilterContextTest { +public class AttributeFilterTest { + @Rule public TemporaryFolder temp = new TemporaryFolder(); @Test - public void should_use_slash_for_canonical_path() throws IOException { - // even on windows - File file = temp.newFile("foo.txt"); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setCanonicalPath(file.getCanonicalPath()); - assertThat(context.canonicalPath()).doesNotContain("\\").contains("/"); + public void should_check_attribute_value() throws Exception { + AttributeFilter filter = new AttributeFilter("foo", Lists.newArrayList("one", "two")); + + assertThat(filter.accept(InputFile.create(temp.newFile(), "Why.java", ImmutableMap.of("foo", "two")))).isTrue(); + assertThat(filter.accept(InputFile.create(temp.newFile(), "Where.java", ImmutableMap.of("foo", "three")))).isFalse(); + assertThat(filter.accept(InputFile.create(temp.newFile(), "What.java", ImmutableMap.of("bar", "one")))).isFalse(); + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java index 61f0ccbe5bd..c0dfddb5a7e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java @@ -19,286 +19,336 @@ */ package org.sonar.batch.scan.filesystem; -import com.google.common.base.Charsets; -import org.apache.commons.io.filefilter.FileFilterUtils; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; -import org.sonar.api.resources.AbstractLanguage; -import org.sonar.api.resources.Languages; import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.FileSystemFilter; -import org.sonar.api.utils.SonarException; +import org.sonar.api.scan.filesystem.InputFile; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Arrays; import java.util.List; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class DefaultModuleFileSystemTest { + @Rule public TemporaryFolder temp = new TemporaryFolder(); @Rule public ExpectedException thrown = ExpectedException.none(); + InputFileCache fileCache = mock(InputFileCache.class); + Settings settings = new Settings(); + FileIndexer fileIndexer = mock(FileIndexer.class); + @Test - public void test_new_file_system() throws IOException { - File basedir = temp.newFolder("base"); - File workingDir = temp.newFolder("work"); - LanguageFilters languageFilters = mock(LanguageFilters.class); - FileSystemFilter fileFilter = mock(FileSystemFilter.class); - - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(workingDir) - .addBinaryDir(new File(basedir, "target/classes")) - .addSourceDir(new File(basedir, "src/main/java")) - .addSourceDir(new File(basedir, "src/main/groovy")) - .addTestDir(new File(basedir, "src/test/java")) - .addFilters(fileFilter) - .setLanguageFilters(languageFilters); - - assertThat(fileSystem).isNotNull(); - assertThat(fileSystem.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath()); - assertThat(fileSystem.workingDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath()); - assertThat(fileSystem.sourceDirs()).hasSize(2); - assertThat(fileSystem.testDirs()).hasSize(1); - assertThat(fileSystem.binaryDirs()).hasSize(1); - assertThat(fileSystem.filters()).containsOnly(fileFilter); - assertThat(fileSystem.languageFilters()).isSameAs(languageFilters); + public void test_equals_and_hashCode() throws Exception { + DefaultModuleFileSystem foo1 = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); + DefaultModuleFileSystem foo2 = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); + DefaultModuleFileSystem bar = new DefaultModuleFileSystem("bar", settings, fileCache, fileIndexer); + + assertThat(foo1.moduleKey()).isEqualTo("foo"); + assertThat(foo1.equals(foo1)).isTrue(); + assertThat(foo1.equals(foo2)).isTrue(); + assertThat(foo1.equals(bar)).isFalse(); + assertThat(foo1.equals("foo")).isFalse(); + assertThat(foo1.hashCode()).isEqualTo(foo1.hashCode()); + assertThat(foo1.hashCode()).isEqualTo(foo2.hashCode()); } @Test public void default_source_encoding() { - File basedir = temp.newFolder("base"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setSettings(new Settings()); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); - assertThat(fileSystem.sourceCharset()).isEqualTo(Charset.defaultCharset()); - assertThat(fileSystem.isDefaultSourceCharset()).isTrue(); + assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(fs.isDefaultSourceCharset()).isTrue(); } @Test public void source_encoding_is_set() { - File basedir = temp.newFolder("base"); - Settings settings = new Settings(); - settings.setProperty(CoreProperties.ENCODING_PROPERTY, "UTF-8"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setSettings(settings); - - assertThat(fileSystem.sourceCharset()).isEqualTo(Charset.forName("UTF-8")); - assertThat(fileSystem.isDefaultSourceCharset()).isFalse(); - } + settings.setProperty(CoreProperties.ENCODING_PROPERTY, "Cp1124"); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); - @Test - public void should_exclude_dirs_starting_with_dot() throws IOException { - File basedir = new File(resourcesDir(), "exclude_dir_starting_with_dot"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src")) - .setSettings(new Settings()); - - List<File> files = fileSystem.files(FileQuery.onSource()); - assertThat(files).hasSize(1); - assertThat(files.get(0).getName()).isEqualTo("Included.java"); + assertThat(fs.sourceCharset()).isEqualTo(Charset.forName("Cp1124")); + + // This test fails when default Java encoding is "IBM AIX Ukraine". Sorry for that. + assertThat(fs.isDefaultSourceCharset()).isFalse(); } @Test - public void should_load_source_files_by_language() throws IOException { - File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src/main/java")) - .addTestDir(new File(basedir, "src/test/java")) - .setLanguageFilters(new LanguageFilters(new Languages(new Java(), new Php()))) - .setSettings(new Settings()); - - List<File> files = fileSystem.files(FileQuery.onSource().onLanguage("java")); - assertThat(files).hasSize(2); - for (File sourceFiles : files) { - assertThat(sourceFiles).exists().isFile(); - assertThat(sourceFiles.getName()).isIn("Hello.java", "Foo.java"); - } - assertThat(fileSystem.files(FileQuery.onSource().onLanguage("php"))).isEmpty(); + public void test_dirs() throws IOException { + File basedir = temp.newFolder("base"); + File buildDir = temp.newFolder("build"); + File workingDir = temp.newFolder("work"); + + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); + fs.setBaseDir(basedir); + fs.setBuildDir(buildDir); + fs.setWorkingDir(workingDir); + fs.addBinaryDir(new File(basedir, "target/classes")); + fs.addSourceDir(new File(basedir, "src/main/java")); + fs.addSourceDir(new File(basedir, "src/main/groovy")); + fs.addTestDir(new File(basedir, "src/test/java")); + + assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath()); + assertThat(fs.workingDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath()); + assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath()); + assertThat(fs.sourceDirs()).hasSize(2); + assertThat(fs.testDirs()).hasSize(1); + assertThat(fs.binaryDirs()).hasSize(1); } @Test - public void should_load_test_files() throws IOException { - File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src/main/java")) - .addTestDir(new File(basedir, "src/test/java")) - .setSettings(new Settings()); - - assertThat(fileSystem.testDirs()).hasSize(1); - List<File> testFiles = fileSystem.files(FileQuery.onTest()); - assertThat(testFiles).hasSize(2); - for (File testFile : testFiles) { - assertThat(testFile).exists().isFile(); - assertThat(testFile.getName()).endsWith("Test.java"); - } + public void test_additional_source_files() throws IOException { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); + assertThat(fs.additionalSourceFiles()).isEmpty(); + assertThat(fs.additionalTestFiles()).isEmpty(); + + File main = temp.newFile("Main.java"); + File test = temp.newFile("Test.java"); + fs.setAdditionalSourceFiles(Lists.newArrayList(main)); + fs.setAdditionalTestFiles(Lists.newArrayList(test)); + assertThat(fs.additionalSourceFiles()).containsOnly(main); + assertThat(fs.additionalTestFiles()).containsOnly(test); } @Test - public void should_load_test_files_by_language() throws IOException { - File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src/main/java")) - .addTestDir(new File(basedir, "src/test/java")) - .setLanguageFilters(new LanguageFilters(new Languages(new Java(), new Php()))) - .setSettings(new Settings()); - - List<File> testFiles = fileSystem.files(FileQuery.onTest().onLanguage("java")); - assertThat(testFiles).hasSize(2); - for (File testFile : testFiles) { - assertThat(testFile).exists().isFile(); - assertThat(testFile.getName()).endsWith("Test.java"); - } - assertThat(fileSystem.files(FileQuery.onTest().onLanguage("php"))).isEmpty(); - } + public void should_reset_dirs() throws IOException { + File basedir = temp.newFolder(); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); + fs.setBaseDir(basedir); + fs.setWorkingDir(basedir); + fs.addSourceDir(new File(basedir, "src/main/java")); - private File resourcesDir() { - File dir = new File("test-resources/DefaultModuleFileSystemTest"); - if (!dir.exists()) { - dir = new File("sonar-batch/test-resources/DefaultModuleFileSystemTest"); - } - return dir; + File existingDir = temp.newFolder("new_folder"); + File notExistingDir = new File(existingDir, "not_exist"); + + fs.resetDirs(existingDir, existingDir, + Lists.newArrayList(existingDir, notExistingDir), Lists.newArrayList(existingDir, notExistingDir), Lists.newArrayList(existingDir, notExistingDir)); + + assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); + assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); + assertThat(fs.sourceDirs()).hasSize(1); + assertThat(fs.sourceDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); + assertThat(fs.testDirs()).hasSize(1); + assertThat(fs.testDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); + assertThat(fs.binaryDirs()).hasSize(1); + assertThat(fs.binaryDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); + verify(fileIndexer).index(fs); } @Test - public void should_apply_file_filters() throws IOException { - File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src/main/java")) - .addFilters(new FileFilterWrapper(FileFilterUtils.nameFileFilter("Foo.java"))) - .setSettings(new Settings()); - - List<File> files = fileSystem.files(FileQuery.onSource()); - assertThat(files).hasSize(1); - assertThat(files.get(0).getName()).isEqualTo("Foo.java"); - } + public void should_search_input_files() throws Exception { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); - static class Php extends AbstractLanguage { - public Php() { - super("php"); - } + File mainFile = temp.newFile(); + InputFile mainInput = InputFile.create(mainFile, "Main.java", ImmutableMap.of(InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE)); + InputFile testInput = InputFile.create(temp.newFile(), "Test.java", ImmutableMap.of(InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_TEST)); - public String[] getFileSuffixes() { - return new String[] {"php"}; - } - } + when(fileCache.byModule("foo")).thenReturn(Lists.newArrayList(mainInput, testInput)); - static class Java extends AbstractLanguage { - public Java() { - super("java"); - } + Iterable<InputFile> inputFiles = fs.inputFiles(FileQuery.onSource()); + assertThat(inputFiles).containsOnly(mainInput); - public String[] getFileSuffixes() { - return new String[] {"java", "jav"}; - } + List<File> files = fs.files(FileQuery.onSource()); + assertThat(files).containsOnly(mainFile); } @Test - public void test_reset_dirs() throws IOException { - File basedir = temp.newFolder(); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(basedir) - .addSourceDir(new File(basedir, "src/main/java")) - .addFilters(new FileFilterWrapper(FileFilterUtils.nameFileFilter("Foo.java"))); - - File existingDir = temp.newFolder("new_folder"); - File notExistingDir = new File(existingDir, "not_exist"); + public void should_index() throws Exception { + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", settings, fileCache, fileIndexer); - fileSystem.resetDirs(existingDir, existingDir, - Arrays.asList(existingDir, notExistingDir), Arrays.asList(existingDir, notExistingDir), Arrays.asList(existingDir, notExistingDir)); - - assertThat(fileSystem.baseDir().getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); - assertThat(fileSystem.buildDir().getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); - assertThat(fileSystem.sourceDirs()).hasSize(1); - assertThat(fileSystem.sourceDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); - assertThat(fileSystem.testDirs()).hasSize(1); - assertThat(fileSystem.testDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); - assertThat(fileSystem.binaryDirs()).hasSize(1); - assertThat(fileSystem.binaryDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); - } + verifyZeroInteractions(fileIndexer); - @Test - public void should_throw_if_incremental_mode_and_not_in_dryrun() throws Exception { - File basedir = temp.newFolder(); - Settings settings = new Settings(); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src/main/java")) - .setSettings(settings); - - settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); - - thrown.expect(SonarException.class); - thrown.expectMessage("Incremental preview is only supported with preview mode"); - fileSystem.files(FileQuery.onSource()); + fs.index(); + verify(fileIndexer).index(fs); } - @Test - public void should_filter_changed_files() throws Exception { - File basedir = new File(resourcesDir(), "main_and_test_files"); - Settings settings = new Settings(); - settings.setProperty(CoreProperties.ENCODING_PROPERTY, "UTF-8"); - File mainDir = new File(basedir, "src/main/java"); - File testDir = new File(basedir, "src/test/java"); - File foo = new File(mainDir, "Foo.java"); - File hello = new File(mainDir, "Hello.java"); - File fooTest = new File(testDir, "FooTest.java"); - File helloTest = new File(testDir, "HelloTest.java"); - - FileHashCache fileHashCache = mock(FileHashCache.class); - when(fileHashCache.getPreviousHash(foo)).thenReturn("oldfoohash"); - when(fileHashCache.getCurrentHash(foo, Charsets.UTF_8)).thenReturn("foohash"); - when(fileHashCache.getPreviousHash(hello)).thenReturn("oldhellohash"); - when(fileHashCache.getCurrentHash(hello, Charsets.UTF_8)).thenReturn("oldhellohash"); - when(fileHashCache.getPreviousHash(fooTest)).thenReturn("oldfooTesthash"); - when(fileHashCache.getCurrentHash(fooTest, Charsets.UTF_8)).thenReturn("fooTesthash"); - when(fileHashCache.getPreviousHash(helloTest)).thenReturn("oldhelloTesthash"); - when(fileHashCache.getCurrentHash(helloTest, Charsets.UTF_8)).thenReturn("oldhelloTesthash"); - - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(fileHashCache) - .setBaseDir(basedir) - .setWorkingDir(temp.newFolder()) - .addSourceDir(mainDir) - .addTestDir(testDir) - .setSettings(settings); - - assertThat(fileSystem.files(FileQuery.onSource())).containsOnly(foo, hello); - assertThat(fileSystem.files(FileQuery.onTest())).containsOnly(fooTest, helloTest); - - assertThat(fileSystem.changedFiles(FileQuery.onSource())).containsExactly(foo); - assertThat(fileSystem.changedFiles(FileQuery.onTest())).containsExactly(fooTest); - - settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); - settings.setProperty(CoreProperties.DRY_RUN, true); - - assertThat(fileSystem.files(FileQuery.onSource())).containsExactly(foo); - assertThat(fileSystem.files(FileQuery.onTest())).containsExactly(fooTest); - } + // +// +// +// @Test +// public void should_exclude_dirs_starting_with_dot() throws IOException { +// File basedir = new File(resourcesDir(), "exclude_dir_starting_with_dot"); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src")) +// .setSettings(new Settings()); +// +// List<File> files = fileSystem.files(FileQuery.onSource()); +// assertThat(files).hasSize(1); +// assertThat(files.get(0).getName()).isEqualTo("Included.java"); +// } +// +// @Test +// public void should_load_source_files_by_language() throws IOException { +// File basedir = new File(resourcesDir(), "main_and_test_files"); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src/main/java")) +// .addTestDir(new File(basedir, "src/test/java")) +// .setSettings(new Settings()); +// +// List<File> files = fileSystem.files(FileQuery.onSource().onLanguage("java")); +// assertThat(files).hasSize(2); +// for (File sourceFiles : files) { +// assertThat(sourceFiles).exists().isFile(); +// assertThat(sourceFiles.getName()).isIn("Hello.java", "Foo.java"); +// } +// assertThat(fileSystem.files(FileQuery.onSource().onLanguage("php"))).isEmpty(); +// } +// +// @Test +// public void should_load_test_files() throws IOException { +// File basedir = new File(resourcesDir(), "main_and_test_files"); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src/main/java")) +// .addTestDir(new File(basedir, "src/test/java")) +// .setSettings(new Settings()); +// +// assertThat(fileSystem.testDirs()).hasSize(1); +// List<File> testFiles = fileSystem.files(FileQuery.onTest()); +// assertThat(testFiles).hasSize(2); +// for (File testFile : testFiles) { +// assertThat(testFile).exists().isFile(); +// assertThat(testFile.getName()).endsWith("Test.java"); +// } +// } +// +// @Test +// public void should_load_test_files_by_language() throws IOException { +// File basedir = new File(resourcesDir(), "main_and_test_files"); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src/main/java")) +// .addTestDir(new File(basedir, "src/test/java")) +// .setSettings(new Settings()); +// +// List<File> testFiles = fileSystem.files(FileQuery.onTest().onLanguage("java")); +// assertThat(testFiles).hasSize(2); +// for (File testFile : testFiles) { +// assertThat(testFile).exists().isFile(); +// assertThat(testFile.getName()).endsWith("Test.java"); +// } +// assertThat(fileSystem.files(FileQuery.onTest().onLanguage("php"))).isEmpty(); +// } +// +// private File resourcesDir() { +// File dir = new File("test-resources/DefaultModuleFileSystemTest"); +// if (!dir.exists()) { +// dir = new File("sonar-batch/test-resources/DefaultModuleFileSystemTest"); +// } +// return dir; +// } +// +// @Test +// public void should_apply_file_filters() throws IOException { +// File basedir = new File(resourcesDir(), "main_and_test_files"); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src/main/java")) +// .addFilters(new FileFilterWrapper(FileFilterUtils.nameFileFilter("Foo.java"))) +// .setSettings(new Settings()); +// +// List<File> files = fileSystem.files(FileQuery.onSource()); +// assertThat(files).hasSize(1); +// assertThat(files.get(0).getName()).isEqualTo("Foo.java"); +// } +// +// static class Php extends AbstractLanguage { +// public Php() { +// super("php"); +// } +// +// public String[] getFileSuffixes() { +// return new String[] {"php"}; +// } +// } +// +// static class Java extends AbstractLanguage { +// public Java() { +// super("java"); +// } +// +// public String[] getFileSuffixes() { +// return new String[] {"java", "jav"}; +// } +// } +// +// +// +// @Test +// public void should_throw_if_incremental_mode_and_not_in_dryrun() throws Exception { +// File basedir = temp.newFolder(); +// Settings settings = new Settings(); +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(RemoteFileHashes.class)) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(new File(basedir, "src/main/java")) +// .setSettings(settings); +// +// settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); +// +// thrown.expect(SonarException.class); +// thrown.expectMessage("Incremental preview is only supported with preview mode"); +// fileSystem.files(FileQuery.onSource()); +// } +// +// @Test +// public void should_filter_changed_files() throws Exception { +// File basedir = new File(resourcesDir(), "main_and_test_files"); +// Settings settings = new Settings(); +// settings.setProperty(CoreProperties.ENCODING_PROPERTY, "UTF-8"); +// File mainDir = new File(basedir, "src/main/java"); +// File testDir = new File(basedir, "src/test/java"); +// File foo = new File(mainDir, "Foo.java"); +// File hello = new File(mainDir, "Hello.java"); +// File fooTest = new File(testDir, "FooTest.java"); +// File helloTest = new File(testDir, "HelloTest.java"); +// +// RemoteFileHashes remoteFileHashes = mock(RemoteFileHashes.class); +// when(remoteFileHashes.getPreviousHash(foo)).thenReturn("oldfoohash"); +// when(remoteFileHashes.getCurrentHash(foo, Charsets.UTF_8)).thenReturn("foohash"); +// when(remoteFileHashes.getPreviousHash(hello)).thenReturn("oldhellohash"); +// when(remoteFileHashes.getCurrentHash(hello, Charsets.UTF_8)).thenReturn("oldhellohash"); +// when(remoteFileHashes.getPreviousHash(fooTest)).thenReturn("oldfooTesthash"); +// when(remoteFileHashes.getCurrentHash(fooTest, Charsets.UTF_8)).thenReturn("fooTesthash"); +// when(remoteFileHashes.getPreviousHash(helloTest)).thenReturn("oldhelloTesthash"); +// when(remoteFileHashes.getCurrentHash(helloTest, Charsets.UTF_8)).thenReturn("oldhelloTesthash"); +// +// DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(remoteFileHashes) +// .setBaseDir(basedir) +// .setWorkingDir(temp.newFolder()) +// .addSourceDir(mainDir) +// .addTestDir(testDir) +// .setSettings(settings); +// +// assertThat(fileSystem.files(FileQuery.onSource())).containsOnly(foo, hello); +// assertThat(fileSystem.files(FileQuery.onTest())).containsOnly(fooTest, helloTest); +// +// assertThat(fileSystem.changedFiles(FileQuery.onSource())).containsExactly(foo); +// assertThat(fileSystem.changedFiles(FileQuery.onTest())).containsExactly(fooTest); +// +// settings.setProperty(CoreProperties.INCREMENTAL_PREVIEW, true); +// settings.setProperty(CoreProperties.DRY_RUN, true); +// +// assertThat(fileSystem.files(FileQuery.onSource())).containsExactly(foo); +// assertThat(fileSystem.files(FileQuery.onTest())).containsExactly(fooTest); +// } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFilterTest.java index 269441d3ebb..9272d2cf012 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFilterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFilterTest.java @@ -19,35 +19,42 @@ */ package org.sonar.batch.scan.filesystem; +import com.google.common.collect.ImmutableMap; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.scan.filesystem.InputFile; import java.io.File; import java.io.IOException; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class ExclusionFilterTest { + @Rule public TemporaryFolder temp = new TemporaryFolder(); @Test - public void accept() throws IOException { - ExclusionFilter filter = new ExclusionFilter("**/*Foo.java"); - File file = temp.newFile(); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); + public void accept() throws Exception { + ExclusionFilter sourceRelativeFilter = new ExclusionFilter("**/*Foo.java"); + ExclusionFilter absoluteFilter = new ExclusionFilter("file:**/src/main/**Foo.java"); - context.setCanonicalPath("/absolute/path/to/MyFoo.java"); - context.setRelativePath("relative/path/to/MyFoo.java"); - assertThat(filter.accept(file, context)).isFalse(); + File file = temp.newFile(); + InputFile inputFile = InputFile.create(file, "src/main/java/org/MyFoo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_CANONICAL_PATH, "/absolute/path/to/src/main/java/org/MyFoo.java", + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "org/MyFoo.java" + )); - context.setCanonicalPath("/absolute/path/to/Other.java"); - context.setRelativePath("relative/path/to/Other.java"); + assertThat(sourceRelativeFilter.accept(inputFile)).isFalse(); + assertThat(absoluteFilter.accept(inputFile)).isFalse(); - assertThat(filter.accept(file, context)).isTrue(); + inputFile = InputFile.create(file, "src/main/java/org/Other.java", ImmutableMap.of( + InputFile.ATTRIBUTE_CANONICAL_PATH, "/absolute/path/to/src/main/java/org/Other.java", + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "org/Other.java" + )); + assertThat(sourceRelativeFilter.accept(inputFile)).isTrue(); + assertThat(absoluteFilter.accept(inputFile)).isTrue(); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java index 0bea3c05040..05c3d065458 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFiltersTest.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.scan.filesystem; +import com.google.common.collect.ImmutableMap; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -28,15 +29,14 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; -import org.sonar.api.scan.filesystem.FileType; -import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.scan.filesystem.InputFile; import java.io.IOException; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class ExclusionFiltersTest { + @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -46,19 +46,26 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java"); ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.SOURCE); - context.setRelativePath("com/mycompany/Foo.java"); - assertThat(filter.accept(temp.newFile(), context)).isFalse(); + java.io.File file = temp.newFile(); + InputFile inputFile = InputFile.create(file, "src/main/java/com/mycompany/Foo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/Foo.java" + )); + + assertThat(filter.accept(inputFile)).isFalse(); - context.setRelativePath("com/mycompany/FooDao.java"); - assertThat(filter.accept(temp.newFile(), context)).isTrue(); + inputFile = InputFile.create(file, "src/main/java/com/mycompany/FooDao.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/FooDao.java" + )); + assertThat(filter.accept(inputFile)).isTrue(); // source inclusions do not apply to tests - context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.TEST); - context.setRelativePath("com/mycompany/Foo.java"); - assertThat(filter.accept(temp.newFile(), context)).isTrue(); + inputFile = InputFile.create(file, "src/main/java/com/mycompany/Foo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_TEST, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/Foo.java" + )); + assertThat(filter.accept(inputFile)).isTrue(); } @Test @@ -67,13 +74,19 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java"); ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.SOURCE); - context.setRelativePath("com/mycompany/Foo.java"); - assertThat(filter.accept(temp.newFile(), context)).isFalse(); + java.io.File file = temp.newFile(); + InputFile inputFile = InputFile.create(file, "src/main/java/com/mycompany/Foo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/Foo.java" + )); - context.setRelativePath("com/mycompany/FooDto.java"); - assertThat(filter.accept(temp.newFile(), context)).isTrue(); + assertThat(filter.accept(inputFile)).isFalse(); + + inputFile = InputFile.create(file, "src/main/java/com/mycompany/FooDto.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/FooDto.java" + )); + assertThat(filter.accept(inputFile)).isTrue(); } @Test @@ -82,19 +95,25 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java"); ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.SOURCE); - context.setRelativePath("com/mycompany/FooDao.java"); - assertThat(filter.accept(temp.newFile(), context)).isFalse(); + java.io.File file = temp.newFile(); + InputFile inputFile = InputFile.create(file, "src/main/java/com/mycompany/FooDao.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/FooDao.java" + )); + assertThat(filter.accept(inputFile)).isFalse(); - context.setRelativePath("com/mycompany/Foo.java"); - assertThat(filter.accept(temp.newFile(), context)).isTrue(); + inputFile = InputFile.create(file, "src/main/java/com/mycompany/Foo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/Foo.java" + )); + assertThat(filter.accept(inputFile)).isTrue(); // source exclusions do not apply to tests - context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.TEST); - context.setRelativePath("com/mycompany/FooDao.java"); - assertThat(filter.accept(temp.newFile(), context)).isTrue(); + inputFile = InputFile.create(file, "src/main/java/com/mycompany/FooDao.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_TEST, + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "com/mycompany/FooDao.java" + )); + assertThat(filter.accept(inputFile)).isTrue(); } @Test @@ -106,15 +125,17 @@ public class ExclusionFiltersTest { settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getCanonicalPath()); ExclusionFilters filter = new ExclusionFilters(new FileExclusions(settings)); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.SOURCE); - context.setRelativePath("org/bar/Foo.java"); - context.setCanonicalPath(includedFile.getCanonicalPath()); - assertThat(filter.accept(includedFile, context)).isTrue(); - - context.setRelativePath("org/bar/Bar.java"); - context.setCanonicalPath(excludedFile.getCanonicalPath()); - assertThat(filter.accept(excludedFile, context)).isFalse(); + InputFile includedInput = InputFile.create(includedFile, "src/main/java/org/bar/Foo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_CANONICAL_PATH, includedFile.getCanonicalPath() + )); + assertThat(filter.accept(includedInput)).isTrue(); + + InputFile excludedInput = InputFile.create(excludedFile, "src/main/java/org/bar/Bar.java", ImmutableMap.of( + InputFile.ATTRIBUTE_TYPE, InputFile.TYPE_SOURCE, + InputFile.ATTRIBUTE_CANONICAL_PATH, excludedFile.getCanonicalPath() + )); + assertThat(filter.accept(excludedInput)).isFalse(); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/HashBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashDigestTest.java index f15876f8d54..c3345c26af3 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/HashBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashDigestTest.java @@ -23,53 +23,60 @@ import com.google.common.base.Charsets; import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.api.utils.SonarException; import java.io.File; import static org.fest.assertions.Assertions.assertThat; -public class HashBuilderTest { +public class FileHashDigestTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); @Rule public TemporaryFolder temp = new TemporaryFolder(); - private HashBuilder hashBuilder = new HashBuilder(); @Test public void should_compute_hash() throws Exception { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\r\nbar", Charsets.UTF_8, true); - assertThat(hashBuilder.computeHashNormalizeLineEnds(tempFile, Charsets.UTF_8)).isEqualTo("daef8a22a3f12580beadf086a9e11519"); + assertThat(FileHashDigest.INSTANCE.hash(tempFile, Charsets.UTF_8)).isEqualTo("daef8a22a3f12580beadf086a9e11519"); } @Test public void should_normalize_line_ends() throws Exception { File file1 = temp.newFile(); FileUtils.write(file1, "foobar\nfofo", Charsets.UTF_8); - String hash1 = hashBuilder.computeHashNormalizeLineEnds(file1, Charsets.UTF_8); + String hash1 = FileHashDigest.INSTANCE.hash(file1, Charsets.UTF_8); File file2 = temp.newFile(); FileUtils.write(file2, "foobar\r\nfofo", Charsets.UTF_8); - String hash2 = hashBuilder.computeHashNormalizeLineEnds(file2, Charsets.UTF_8); + String hash2 = FileHashDigest.INSTANCE.hash(file2, Charsets.UTF_8); File file3 = temp.newFile(); FileUtils.write(file3, "foobar\rfofo", Charsets.UTF_8); - String hash3 = hashBuilder.computeHashNormalizeLineEnds(file3, Charsets.UTF_8); + String hash3 = FileHashDigest.INSTANCE.hash(file3, Charsets.UTF_8); File file4 = temp.newFile(); FileUtils.write(file4, "foobar\nfofo\n", Charsets.UTF_8); - String hash4 = hashBuilder.computeHashNormalizeLineEnds(file4, Charsets.UTF_8); + String hash4 = FileHashDigest.INSTANCE.hash(file4, Charsets.UTF_8); assertThat(hash1).isEqualTo(hash2); assertThat(hash1).isEqualTo(hash3); assertThat(hash1).isNotEqualTo(hash4); } - @Test(expected = SonarException.class) - public void should_throw_on_not_existing_file() throws Exception { + @Test + public void should_throw_if_file_does_not_exist() throws Exception { File tempFolder = temp.newFolder(); - hashBuilder.computeHashNormalizeLineEnds(new File(tempFolder, "unknowFile.txt"), Charsets.UTF_8); + File file = new File(tempFolder, "doesNotExist.txt"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Fail to compute hash of file " + file.getAbsolutePath() + " with charset UTF-8"); + + FileHashDigest.INSTANCE.hash(file, Charsets.UTF_8); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileFilterWrapperTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashesTest.java index fbaeec9f366..98ae99d7006 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileFilterWrapperTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashesTest.java @@ -19,29 +19,40 @@ */ package org.sonar.batch.scan.filesystem; -import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.mockito.Mockito; -import org.sonar.api.scan.filesystem.FileSystemFilter; import java.io.File; -import java.io.IOException; +import java.nio.charset.Charset; -public class FileFilterWrapperTest { +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class FileHashesTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - @Test - public void should_wrap_java_io_filefilter() throws IOException { - IOFileFilter filter = Mockito.mock(IOFileFilter.class); - FileFilterWrapper wrapper = new FileFilterWrapper(filter); + RemoteFileHashes remoteFileHashes = mock(RemoteFileHashes.class); + @Test + public void hash() throws Exception { File file = temp.newFile(); - wrapper.accept(file, Mockito.mock(FileSystemFilter.Context.class)); + FileUtils.write(file, "fooo"); + + FileHashes hashes = new FileHashes(remoteFileHashes); + assertThat(hashes.hash(file, Charset.forName("UTF-8"))).isEqualTo("efc4470c96a94b1ff400175ef8368444"); + verifyZeroInteractions(remoteFileHashes); + } + + @Test + public void remote_hash() throws Exception { + String path = "src/main/java/Foo.java"; + when(remoteFileHashes.remoteHash(path)).thenReturn("ABCDE"); - Mockito.verify(filter).accept(file); + FileHashes hashes = new FileHashes(remoteFileHashes); + assertThat(hashes.remoteHash(path)).isEqualTo("ABCDE"); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileSystemLoggerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileSystemLoggerTest.java index 19f2ae4c62d..f218d0bba4a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileSystemLoggerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileSystemLoggerTest.java @@ -38,11 +38,10 @@ public class FileSystemLoggerTest { @Test public void log() { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(mock(FileHashCache.class)); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem("foo", mock(Settings.class), mock(InputFileCache.class), mock(FileIndexer.class)); File src = temp.newFolder("src"); File test = temp.newFolder("test"); File base = temp.newFolder("base"); - fs.setSettings(new Settings()); fs.setBaseDir(base); fs.addSourceDir(src); fs.addTestDir(test); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFilterTest.java index 8333c6c2286..38f391fa8ce 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFilterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFilterTest.java @@ -19,35 +19,41 @@ */ package org.sonar.batch.scan.filesystem; +import com.google.common.collect.ImmutableMap; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.scan.filesystem.InputFile; import java.io.File; -import java.io.IOException; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class InclusionFilterTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @Test - public void accept() throws IOException { - InclusionFilter filter = new InclusionFilter("**/*Foo.java"); - File file = temp.newFile(); - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); + public void accept() throws Exception { + InclusionFilter sourceRelativeFilter = new InclusionFilter("**/*Foo.java"); + InclusionFilter absoluteFilter = new InclusionFilter("file:**/src/main/**Foo.java"); - context.setCanonicalPath("/absolute/path/to/MyFoo.java"); - context.setRelativePath("relative/path/to/MyFoo.java"); - assertThat(filter.accept(file, context)).isTrue(); - context.setCanonicalPath("/absolute/path/to/Other.java"); - context.setRelativePath("relative/path/to/Other.java"); + File file = temp.newFile(); + InputFile inputFile = InputFile.create(file, "src/main/java/org/MyFoo.java", ImmutableMap.of( + InputFile.ATTRIBUTE_CANONICAL_PATH, "/absolute/path/to/src/main/java/org/MyFoo.java", + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "org/MyFoo.java" + )); + + assertThat(sourceRelativeFilter.accept(inputFile)).isTrue(); + assertThat(absoluteFilter.accept(inputFile)).isTrue(); - assertThat(filter.accept(file, context)).isFalse(); + inputFile = InputFile.create(file, "src/main/java/org/Other.java", ImmutableMap.of( + InputFile.ATTRIBUTE_CANONICAL_PATH, "/absolute/path/to/src/main/java/org/Other.java", + InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "org/Other.java" + )); + assertThat(sourceRelativeFilter.accept(inputFile)).isFalse(); + assertThat(absoluteFilter.accept(inputFile)).isFalse(); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java new file mode 100644 index 00000000000..25b82b83961 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java @@ -0,0 +1,65 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.scan.filesystem; + +import com.google.common.collect.Maps; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.scan.filesystem.InputFile; +import org.sonar.batch.index.Caches; + +import static org.fest.assertions.Assertions.assertThat; + +public class InputFileCacheTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + Caches caches = new Caches(); + + @Before + public void start() { + caches.start(); + } + + @After + public void stop() { + caches.stop(); + } + + @Test + public void should_add_input_file() throws Exception { + InputFileCache cache = new InputFileCache(caches); + cache.put("struts", InputFile.create(temp.newFile(), "src/main/java/Foo.java", Maps.<String, String>newHashMap())); + cache.put("struts-core", InputFile.create(temp.newFile(), "src/main/java/Foo.java", Maps.<String, String>newHashMap())); + + assertThat(cache.byModule("struts")).hasSize(1); + assertThat(cache.byModule("struts-core")).hasSize(1); + assertThat(cache.all()).hasSize(2); + + cache.removeModule("struts"); + assertThat(cache.byModule("struts")).hasSize(0); + assertThat(cache.byModule("struts-core")).hasSize(1); + assertThat(cache.all()).hasSize(1); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFiltersTest.java deleted file mode 100644 index 7e09c3d59cd..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFiltersTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.scan.filesystem; - -import org.apache.commons.io.filefilter.FalseFileFilter; -import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.filefilter.SuffixFileFilter; -import org.apache.commons.io.filefilter.TrueFileFilter; -import org.junit.Test; -import org.sonar.api.resources.AbstractLanguage; -import org.sonar.api.resources.Languages; - -import java.lang.reflect.Field; - -import static org.fest.assertions.Assertions.assertThat; - -public class LanguageFiltersTest { - @Test - public void forLang() throws Exception { - LanguageFilters filters = new LanguageFilters(new Languages(new Java(), new Php())); - - IOFileFilter filter = filters.forLang("php"); - assertThat(filter).isInstanceOf(SuffixFileFilter.class); - assertThat(suffixes((SuffixFileFilter) filter)).containsOnly("php"); - - filter = filters.forLang("java"); - assertThat(filter).isInstanceOf(SuffixFileFilter.class); - assertThat(suffixes((SuffixFileFilter) filter)).containsOnly("java", "jav"); - - assertThat(filters.forLang("unknown")).isSameAs(FalseFileFilter.FALSE); - } - - private String[] suffixes(SuffixFileFilter filter) throws Exception { - Field privateField = SuffixFileFilter.class.getDeclaredField("suffixes"); - privateField.setAccessible(true); - - return (String[]) privateField.get(filter); - } - - static class Php extends AbstractLanguage { - public Php() { - super("php"); - } - - public String[] getFileSuffixes() { - return new String[]{"php"}; - } - } - - static class Java extends AbstractLanguage { - public Java() { - super("java"); - } - - public String[] getFileSuffixes() { - return new String[]{"java", "jav"}; - } - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java index 600fa6a4aac..d2aa69e6dc1 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java @@ -25,13 +25,12 @@ import org.apache.commons.io.FilenameUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.FileSystemFilter; import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.TempDirectories; import java.io.File; @@ -45,6 +44,10 @@ public class ModuleFileSystemProviderTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + InputFileCache fileCache = mock(InputFileCache.class, Mockito.RETURNS_DEEP_STUBS); + Settings settings = new Settings(); + FileIndexer fileIndexer = mock(FileIndexer.class, Mockito.RETURNS_DEEP_STUBS); + @Test public void test_provide() throws IOException { ModuleFileSystemProvider provider = new ModuleFileSystemProvider(); @@ -53,8 +56,7 @@ public class ModuleFileSystemProviderTest { ProjectDefinition module = ProjectDefinition.create() .setBaseDir(baseDir) .setWorkDir(workDir); - ModuleFileSystem fs = provider.provide(module, new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); + ModuleFileSystem fs = provider.provide(module, new TempDirectories(), settings, fileCache, fileIndexer); assertThat(fs).isNotNull(); assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); @@ -69,8 +71,8 @@ public class ModuleFileSystemProviderTest { public void default_charset_is_platform_dependent() throws IOException { ModuleFileSystemProvider provider = new ModuleFileSystemProvider(); - ModuleFileSystem fs = provider.provide(newSimpleModule(), new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); + ModuleFileSystem fs = provider.provide(newSimpleModule(), new TempDirectories(), + new Settings(), mock(InputFileCache.class), mock(FileIndexer.class)); assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); } @@ -82,8 +84,8 @@ public class ModuleFileSystemProviderTest { Settings settings = new Settings(); settings.setProperty(CoreProperties.ENCODING_PROPERTY, Charsets.ISO_8859_1.name()); - ModuleFileSystem fs = provider.provide(module, new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - settings, new FileSystemFilter[0], mock(FileHashCache.class)); + ModuleFileSystem fs = provider.provide(module, new TempDirectories(), + settings, mock(InputFileCache.class), mock(FileIndexer.class)); assertThat(fs.sourceCharset()).isEqualTo(Charsets.ISO_8859_1); } @@ -108,8 +110,8 @@ public class ModuleFileSystemProviderTest { .addTestDirs("src/test/java", "src/test/unknown") .addBinaryDir("target/classes"); - ModuleFileSystem fs = provider.provide(project, new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); + ModuleFileSystem fs = provider.provide(project, new TempDirectories(), + new Settings(), mock(InputFileCache.class), mock(FileIndexer.class)); assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ProjectFileSystemAdapterTest.java index 6207a13c97c..0bdb33e03a4 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ProjectFileSystemAdapterTest.java @@ -29,18 +29,17 @@ import java.io.File; import java.io.IOException; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; + +public class ProjectFileSystemAdapterTest { -public class DeprecatedFileSystemAdapterTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @Test public void should_wrap_module_file_system() { DefaultModuleFileSystem target = mock(DefaultModuleFileSystem.class, Mockito.RETURNS_SMART_NULLS); - DeprecatedFileSystemAdapter adapter = new DeprecatedFileSystemAdapter(target, new Project("my-project")); + ProjectFileSystemAdapter adapter = new ProjectFileSystemAdapter(target, new Project("my-project")); assertThat(adapter.getBasedir()).isNotNull(); verify(target).baseDir(); @@ -63,7 +62,7 @@ public class DeprecatedFileSystemAdapterTest { File workingDir = temp.newFile("work"); DefaultModuleFileSystem target = mock(DefaultModuleFileSystem.class); when(target.workingDir()).thenReturn(workingDir); - DeprecatedFileSystemAdapter adapter = new DeprecatedFileSystemAdapter(target, new Project("my-project")); + ProjectFileSystemAdapter adapter = new ProjectFileSystemAdapter(target, new Project("my-project")); File buildDir = adapter.getBuildDir(); assertThat(buildDir.getParentFile().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/RemoteFileHashesTest.java index 1c70890ffec..91b2f5e34d1 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/RemoteFileHashesTest.java @@ -19,24 +19,17 @@ */ package org.sonar.batch.scan.filesystem; -import com.google.common.base.Charsets; -import org.apache.commons.io.FileUtils; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.database.model.Snapshot; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.PastSnapshotFinder; import org.sonar.core.source.SnapshotDataType; import org.sonar.core.source.jdbc.SnapshotDataDao; import org.sonar.core.source.jdbc.SnapshotDataDto; -import java.io.File; import java.util.Arrays; import java.util.Date; @@ -44,7 +37,7 @@ import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class FileHashCacheTest { +public class RemoteFileHashesTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -52,76 +45,45 @@ public class FileHashCacheTest { @Rule public ExpectedException thrown = ExpectedException.none(); - private FileHashCache cache; - - private PastSnapshotFinder pastSnapshotFinder; - - private Snapshot snapshot; - - private File baseDir; - - private SnapshotDataDao snapshotDataDao; - - private ModuleFileSystem moduleFileSystem; - - @Before - public void prepare() throws Exception { - pastSnapshotFinder = mock(PastSnapshotFinder.class); - snapshot = mock(Snapshot.class); - baseDir = temp.newFolder(); - snapshotDataDao = mock(SnapshotDataDao.class); - moduleFileSystem = mock(ModuleFileSystem.class); - cache = new FileHashCache(ProjectDefinition.create().setBaseDir(baseDir), new PathResolver(), new HashBuilder(), snapshot, - snapshotDataDao, pastSnapshotFinder); - } + PastSnapshotFinder pastSnapshotFinder = mock(PastSnapshotFinder.class); + Snapshot snapshot = mock(Snapshot.class); + SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class); + RemoteFileHashes hashes = new RemoteFileHashes(snapshot, snapshotDataDao, pastSnapshotFinder); @Test - public void should_return_null_if_no_previous_snapshot() throws Exception { + public void should_return_null_if_no_remote_snapshot() throws Exception { when(pastSnapshotFinder.findPreviousAnalysis(snapshot)).thenReturn(new PastSnapshot("foo")); - cache.start(); - assertThat(cache.getPreviousHash(new File(baseDir, "src/main/java/foo/Bar.java"))).isNull(); + hashes.start(); + assertThat(hashes.remoteHash("src/main/java/foo/Bar.java")).isNull(); + hashes.stop(); } @Test - public void should_return_null_if_no_previous_snapshot_data() throws Exception { + public void should_return_null_if_no_remote_hashes() throws Exception { Snapshot previousSnapshot = mock(Snapshot.class); PastSnapshot pastSnapshot = new PastSnapshot("foo", new Date(), previousSnapshot); when(pastSnapshotFinder.findPreviousAnalysis(snapshot)).thenReturn(pastSnapshot); - cache.start(); - assertThat(cache.getPreviousHash(new File(baseDir, "src/main/java/foo/Bar.java"))).isNull(); + hashes.start(); + assertThat(hashes.remoteHash("src/main/java/foo/Bar.java")).isNull(); + hashes.stop(); } @Test - public void should_return_previous_hash() throws Exception { + public void should_return_remote_hash() throws Exception { Snapshot previousSnapshot = mock(Snapshot.class); when(previousSnapshot.getId()).thenReturn(123); PastSnapshot pastSnapshot = new PastSnapshot("foo", new Date(), previousSnapshot); when(pastSnapshotFinder.findPreviousAnalysis(snapshot)).thenReturn(pastSnapshot); SnapshotDataDto snapshotDataDto = new SnapshotDataDto(); - snapshotDataDto.setData("src/main/java/foo/Bar.java=abcd1234\n"); + snapshotDataDto.setData("src/main/java/foo/Bar.java=abcd1234"); when(snapshotDataDao.selectSnapshotData(123, Arrays.asList(SnapshotDataType.FILE_HASH.getValue()))) .thenReturn(Arrays.asList(snapshotDataDto)); - File file = new File(baseDir, "src/main/java/foo/Bar.java"); - FileUtils.write(file, "foo", Charsets.UTF_8); - cache.start(); - assertThat(cache.getPreviousHash(file)).isEqualTo("abcd1234"); - } - - @Test - public void should_compute_and_cache_current_hash() throws Exception { - when(moduleFileSystem.sourceCharset()).thenReturn(Charsets.UTF_8); - - File file = new File(baseDir, "src/main/java/foo/Bar.java"); - FileUtils.write(file, "foo", Charsets.UTF_8); - String hash = "9a8742076ef9ffa5591f633704c2286b"; - assertThat(cache.getCurrentHash(file, Charsets.UTF_8)).isEqualTo(hash); - - // Modify file - FileUtils.write(file, "bar", Charsets.UTF_8); - assertThat(cache.getCurrentHash(file, Charsets.UTF_8)).isEqualTo(hash); + hashes.start(); + assertThat(hashes.remoteHash("src/main/java/foo/Bar.java")).isEqualTo("abcd1234"); + hashes.stop(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java deleted file mode 100644 index 7d337a63f15..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.scan.filesystem; - -import com.google.common.collect.Sets; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.scan.filesystem.FileType; -import org.sonar.api.scan.filesystem.ModuleFileSystem; - -import java.io.File; -import java.io.IOException; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -public class WhiteListFileFilterTest { - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - @Test - public void should_accept() throws IOException { - WhiteListFileFilter filter = new WhiteListFileFilter(FileType.SOURCE, Sets.newHashSet( - new File("Foo.java"), - new File("Bar.java") - )); - - FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.SOURCE); - assertThat(filter.accept(new File("Foo.java"), context)).isTrue(); - assertThat(filter.accept(new File("Other.java"), context)).isFalse(); - - context = new FileFilterContext(mock(ModuleFileSystem.class)); - context.setType(FileType.TEST); - assertThat(filter.accept(new File("Foo.java"), context)).isTrue(); - assertThat(filter.accept(new File("Other.java"), context)).isTrue(); - } - - @Test - public void test_toString() throws IOException { - WhiteListFileFilter filter = new WhiteListFileFilter(FileType.SOURCE, Sets.newHashSet( - new File("Foo.java"), - new File("Bar.java") - )); - - assertThat(filter.toString()) - .contains("Source files:") - .contains("Foo.java") - .contains("Bar.java"); - } -} diff --git a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataType.java b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataType.java index b45eb9d32af..52fddd558cd 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataType.java +++ b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataType.java @@ -24,7 +24,7 @@ public enum SnapshotDataType { SYNTAX_HIGHLIGHTING("highlight_syntax"), SYMBOL_HIGHLIGHTING("symbol"), - FILE_HASH("hash"); + FILE_HASH("file_hash"); private SnapshotDataType(String value) { this.value = value; diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java index 79c5461b892..e5389e96277 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java @@ -54,7 +54,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_create_database_without_project() throws IOException, SQLException { + public void should_create_database_without_project() throws Exception { setupData("should_create_database"); byte[] db = createDb(null); @@ -71,7 +71,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_create_database_with_project() throws IOException, SQLException { + public void should_create_database_with_project() throws Exception { setupData("should_create_database"); byte[] database = createDb(123L); @@ -85,7 +85,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_create_database_with_issues() throws IOException, SQLException { + public void should_create_database_with_issues() throws Exception { setupData("should_create_database_with_issues"); byte[] database = createDb(399L); @@ -95,7 +95,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_export_issues_of_project_tree() throws IOException, SQLException { + public void should_export_issues_of_project_tree() throws Exception { setupData("multi-modules-with-issues"); // 300 : root module -> export issues of all modules @@ -109,7 +109,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_export_issues_of_sub_module() throws IOException, SQLException { + public void should_export_issues_of_sub_module() throws Exception { setupData("multi-modules-with-issues"); // 301 : sub module with 1 closed issue and 1 open issue @@ -122,7 +122,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase { } @Test - public void should_export_issues_of_sub_module_2() throws IOException, SQLException { + public void should_export_issues_of_sub_module_2() throws Exception { setupData("multi-modules-with-issues"); // 302 : sub module without any issues diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFile.java index 8584e22b07b..d33cd146ea4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFile.java @@ -25,7 +25,9 @@ import java.io.InputStream; /** * @since 2.6 + * @deprecated in 4.0. Replaced by {@link org.sonar.api.scan.filesystem.InputFile} */ +@Deprecated public interface InputFile { /** * The source base directory, different than the project basedir. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java index 576d2c18cec..9e018844b19 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java @@ -34,7 +34,9 @@ import java.util.List; /** * @since 2.8 + * @deprecated in 4.0. Replaced by {@link org.sonar.api.scan.filesystem.InputFile}. */ +@Deprecated public final class InputFileUtils { private InputFileUtils() { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileQuery.java index e69b13e92fa..3135b7c27cf 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileQuery.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileQuery.java @@ -19,12 +19,17 @@ */ package org.sonar.api.scan.filesystem; -import com.google.common.collect.Lists; +import com.google.common.base.Function; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Collections2; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Sets; +import javax.annotation.Nullable; import java.io.FileFilter; import java.util.Arrays; import java.util.Collection; +import java.util.Map; import java.util.Set; /** @@ -32,8 +37,14 @@ import java.util.Set; */ public class FileQuery { + // TODO better builders, for example FileQuery.ALL + public static FileQuery on(FileType... types) { - return new FileQuery(types); + FileQuery query = new FileQuery(); + for (FileType type : types) { + query.on(InputFile.ATTRIBUTE_TYPE, type.name().toLowerCase()); + } + return query; } public static FileQuery onSource() { @@ -44,27 +55,40 @@ public class FileQuery { return on(FileType.TEST); } - private final Set<FileType> types; - private final Set<String> languages = Sets.newLinkedHashSet(); - private final Set<String> inclusions = Sets.newLinkedHashSet(); - private final Set<String> exclusions = Sets.newLinkedHashSet(); - private final Collection<FileFilter> filters = Lists.newLinkedList(); + private final ListMultimap<String, String> attributes = ArrayListMultimap.create(); + private final Set<String> inclusions = Sets.newHashSet(); + private final Set<String> exclusions = Sets.newHashSet(); + + private FileQuery() { + } + + private FileQuery on(String attribute, String... values) { + for (String value : values) { + attributes.put(attribute, value); + } + return this; + } - private FileQuery(FileType... types) { - this.types = Sets.newHashSet(types); + public Map<String, Collection<String>> attributes() { + return attributes.asMap(); } + @Deprecated public Collection<FileType> types() { - return types; + return Collections2.transform(attributes.get(InputFile.ATTRIBUTE_TYPE), new Function<String, FileType>() { + @Override + public FileType apply(@Nullable String input) { + return input != null ? FileType.valueOf(input) : null; + } + }); } public Collection<String> languages() { - return languages; + return attributes.get(InputFile.ATTRIBUTE_LANGUAGE); } public FileQuery onLanguage(String... languages) { - this.languages.addAll(Arrays.asList(languages)); - return this; + return on(InputFile.ATTRIBUTE_LANGUAGE, languages); } public Collection<String> inclusions() { @@ -85,13 +109,14 @@ public class FileQuery { return this; } + // TODO deprecate public Collection<FileFilter> filters() { - return filters; + throw new UnsupportedOperationException("TODO"); } + // TODO deprecate ? public FileQuery withFilters(FileFilter... filters) { - this.filters.addAll(Arrays.asList(filters)); - return this; + throw new UnsupportedOperationException("TODO"); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemFilter.java index 7d03441df56..3a215937d23 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemFilter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemFilter.java @@ -30,7 +30,9 @@ import java.io.File; * <li>exclude the files which names start with Generated</li> * </ul> * @since 3.5 + * @deprecated in 4.0. Replaced by {@link InputFileFilter}. */ +@Deprecated public interface FileSystemFilter extends BatchExtension { /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileType.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileType.java index 6600ae2099a..31f3938da22 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileType.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileType.java @@ -21,7 +21,9 @@ package org.sonar.api.scan.filesystem; /** * @since 3.5 + * @deprecated in 4.0. Replaced by more flexible {@link InputFile} attributes. */ +@Deprecated public enum FileType { SOURCE, TEST } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/InputFile.java new file mode 100644 index 00000000000..c28298ef5ae --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/InputFile.java @@ -0,0 +1,140 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.api.scan.filesystem; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.CheckForNull; +import java.io.File; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @since 4.0 + */ +public class InputFile implements Serializable { + + // TODO refactor attribute constants as classes or enums ? + + /** + * Path relative to module base directory. + */ + public static final String ATTRIBUTE_BASE_RELATIVE_PATH = "baseRelPath"; + + // TODO ambiguity of term "source" with sourceDir versus testDir properties + // Here it does not depend on type. + public static final String ATTRIBUTE_SOURCEDIR_PATH = "srcDirPath"; + public static final String ATTRIBUTE_SOURCE_RELATIVE_PATH = "srcRelPath"; + + public static final String ATTRIBUTE_CANONICAL_PATH = "canonicalPath"; + public static final String ATTRIBUTE_LANGUAGE = "lang"; + public static final String ATTRIBUTE_TYPE = "type"; + public static final String ATTRIBUTE_STATUS = "status"; + public static final String STATUS_SAME = "same"; + public static final String STATUS_CHANGED = "changed"; + public static final String STATUS_ADDED = "added"; + public static final String ATTRIBUTE_HASH = "checksum"; + public static final String ATTRIBUTE_EXTENSION = "extension"; + public static final String TYPE_SOURCE = "source"; + public static final String TYPE_TEST = "test"; + + // TODO limitation of persistit -> add unit test + private transient File transientFile; + private Map<String, String> attributes; + + private InputFile(File file, Map<String, String> attributes) { + this.transientFile = file; + this.attributes = attributes; + } + + /** + * Plugins should not build their own instances of {@link InputFile}. This method + * aims to be used by unit tests. + * // TODO provide builder ? + */ + public static InputFile create(File file, String baseRelativePath, Map<String, String> attributes) { + Map<String,String> copy = Maps.newHashMap(attributes); + copy.put(InputFile.ATTRIBUTE_BASE_RELATIVE_PATH, baseRelativePath); + return new InputFile(file, copy); + } + + /** + * Path from module base directory. Path is unique and identifies file within given + * <code>{@link ModuleFileSystem}</code>. File separator is the forward slash ('/'), + * even on MSWindows. + */ + public String path() { + return attribute(ATTRIBUTE_BASE_RELATIVE_PATH); + } + + public File file() { + if (transientFile == null) { + transientFile = new File(attribute(ATTRIBUTE_CANONICAL_PATH)); + } + return transientFile; + } + + public String name() { + return file().getName(); + } + + public boolean has(String attribute, String value) { + return StringUtils.equals(attributes.get(attribute), value); + } + + @CheckForNull + public String attribute(String key) { + return attributes.get(key); + } + + public Map<String, String> attributes() { + return attributes; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InputFile inputFile = (InputFile) o; + return attribute(ATTRIBUTE_CANONICAL_PATH).equals(inputFile.attribute(ATTRIBUTE_CANONICAL_PATH)); + } + + @Override + public int hashCode() { + return path().hashCode(); + } + + public static List<File> toFiles(Iterable<InputFile> inputFiles) { + List<File> files = Lists.newArrayList(); + for (InputFile inputFile : inputFiles) { + files.add(inputFile.file()); + } + return files; + } + + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterWrapper.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/InputFileFilter.java index 632aa6839d3..2b166827db8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterWrapper.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/InputFileFilter.java @@ -17,24 +17,16 @@ * 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.scan.filesystem; +package org.sonar.api.scan.filesystem; -import org.sonar.api.scan.filesystem.FileSystemFilter; - -import java.io.File; -import java.io.FileFilter; +import org.sonar.api.BatchExtension; /** - * @since 3.5 + * TODO document lifecycle -> executed when initializing project + * @since 4.0 */ -class FileFilterWrapper implements FileSystemFilter { - private final FileFilter filter; +public interface InputFileFilter extends BatchExtension { - FileFilterWrapper(FileFilter filter) { - this.filter = filter; - } + boolean accept(InputFile inputFile); - public boolean accept(File file, Context context) { - return filter.accept(file); - } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java index 31882d0ac28..f8c8257141f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java @@ -20,9 +20,9 @@ package org.sonar.api.scan.filesystem; import org.sonar.api.BatchComponent; +import org.sonar.api.resources.InputFile; import javax.annotation.CheckForNull; - import java.io.File; import java.nio.charset.Charset; import java.util.List; @@ -31,6 +31,14 @@ import java.util.List; * @since 3.5 */ public interface ModuleFileSystem extends BatchComponent { + + /** + * Unique module key + * + * @since 4.0 + */ + String moduleKey(); + /** * Base directory. */ @@ -47,12 +55,14 @@ public interface ModuleFileSystem extends BatchComponent { * Source directories. Non-existing directories are excluded. * Example in Maven : ${project.basedir}/src/main/java */ + // TODO mark as dangerous to use List<File> sourceDirs(); /** * Test directories. Non-existing directories are excluded. * Example in Maven : ${project.basedir}/src/test/java */ + // TODO mark as dangerous to use List<File> testDirs(); /** @@ -72,12 +82,6 @@ public interface ModuleFileSystem extends BatchComponent { List<File> files(FileQuery query); /** - * Search for changed files. Never return null. - * @since 4.0 - */ - List<File> changedFiles(FileQuery query); - - /** * Charset of source and test files. If it's not defined, then return the platform default charset. */ Charset sourceCharset(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/SimpleModuleFileSystem.java b/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/SimpleModuleFileSystem.java index 48bb0a3fcb0..4d821c0bf8e 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/SimpleModuleFileSystem.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/scan/filesystem/SimpleModuleFileSystem.java @@ -42,6 +42,11 @@ public class SimpleModuleFileSystem implements ModuleFileSystem { this.buildDir = new File(baseDir, "build"); } + @Override + public String moduleKey() { + return null; + } + public File baseDir() { return baseDir; } @@ -81,11 +86,6 @@ public class SimpleModuleFileSystem implements ModuleFileSystem { return Collections.emptyList(); } - @Override - public List<File> changedFiles(FileQuery query) { - return Collections.emptyList(); - } - public Charset sourceCharset() { return Charset.forName(CharEncoding.UTF_8); } |