diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2013-10-02 16:00:16 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2013-10-02 16:10:29 +0200 |
commit | d1ed91b890bfec5df267fff19faca781848be242 (patch) | |
tree | 12d724f59edc85c9bdc54d2c9fc5f4cde773b57e /sonar-batch | |
parent | f6af69b24e93bb1262b9c02d8865e88cffa8d9a5 (diff) | |
download | sonarqube-d1ed91b890bfec5df267fff19faca781848be242.tar.gz sonarqube-d1ed91b890bfec5df267fff19faca781848be242.zip |
SONAR-2657 Expose changed files API in ModuleFileSystem
Diffstat (limited to 'sonar-batch')
12 files changed, 608 insertions, 30 deletions
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 b97118be253..37ef6fcedb4 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 @@ -51,6 +51,7 @@ 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; @@ -104,6 +105,7 @@ public class ModuleScanContainer extends ComponentContainer { new ModuleFileSystemProvider(), DeprecatedFileSystemAdapter.class, FileSystemLogger.class, + FileHashCache.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 ec79143de77..0615528efa0 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 @@ -33,11 +33,33 @@ import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.ProjectTree; -import org.sonar.batch.bootstrap.*; -import org.sonar.batch.index.*; -import org.sonar.batch.issue.*; +import org.sonar.batch.bootstrap.BootstrapSettings; +import org.sonar.batch.bootstrap.ExtensionInstaller; +import org.sonar.batch.bootstrap.ExtensionMatcher; +import org.sonar.batch.bootstrap.ExtensionUtils; +import org.sonar.batch.bootstrap.MetricProvider; +import org.sonar.batch.index.Caches; +import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.ComponentDataPersister; +import org.sonar.batch.index.DefaultIndex; +import org.sonar.batch.index.DefaultPersistenceManager; +import org.sonar.batch.index.DefaultResourcePersister; +import org.sonar.batch.index.DependencyPersister; +import org.sonar.batch.index.EventPersister; +import org.sonar.batch.index.LinkPersister; +import org.sonar.batch.index.MeasurePersister; +import org.sonar.batch.index.MemoryOptimizer; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.index.SnapshotCache; +import org.sonar.batch.index.SourcePersister; +import org.sonar.batch.issue.DefaultProjectIssues; +import org.sonar.batch.issue.DeprecatedViolations; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.issue.IssuePersister; +import org.sonar.batch.issue.ScanIssueStorage; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; +import org.sonar.batch.scan.filesystem.HashBuilder; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; import org.sonar.batch.source.HighlightableBuilder; @@ -50,7 +72,11 @@ import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.technicaldebt.TechnicalDebtModel; import org.sonar.core.technicaldebt.WorkUnitConverter; -import org.sonar.core.technicaldebt.functions.*; +import org.sonar.core.technicaldebt.functions.ConstantFunction; +import org.sonar.core.technicaldebt.functions.Functions; +import org.sonar.core.technicaldebt.functions.LinearFunction; +import org.sonar.core.technicaldebt.functions.LinearWithOffsetFunction; +import org.sonar.core.technicaldebt.functions.LinearWithThresholdFunction; import org.sonar.core.test.TestPlanBuilder; import org.sonar.core.test.TestPlanPerspectiveLoader; import org.sonar.core.test.TestableBuilder; @@ -116,6 +142,7 @@ public class ProjectScanContainer extends ComponentContainer { ResourceCache.class, ComponentDataCache.class, ComponentDataPersister.class, + HashBuilder.class, // issues IssueUpdater.class, 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/ChangedFileFilter.java new file mode 100644 index 00000000000..af0968d4137 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ChangedFileFilter.java @@ -0,0 +1,48 @@ +/* + * 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.FileSystemFilter; + +import java.io.File; + +/** + * When enabled this filter will only allow modified files to be analyzed. + * @since 4.0 + */ +class ChangedFileFilter implements FileSystemFilter { + + private FileHashCache fileHashCache; + + public ChangedFileFilter(FileHashCache fileHashCache) { + this.fileHashCache = fileHashCache; + } + + @Override + public boolean accept(File file, Context context) { + String previousHash = fileHashCache.getPreviousHash(file); + if (previousHash == null) { + return true; + } + String currentHash = fileHashCache.getCurrentHash(file); + return !currentHash.equals(previousHash); + } + +} 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 22ab1f9d892..1d0c76a7bee 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 @@ -34,6 +34,7 @@ import org.sonar.api.scan.filesystem.FileSystemFilter; 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.utils.SonarException; import java.io.File; import java.io.FileFilter; @@ -58,8 +59,10 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { private PathResolver pathResolver = new PathResolver(); private List<FileSystemFilter> fsFilters = Lists.newArrayList(); private LanguageFilters languageFilters; + private FileHashCache fileHashCache; - DefaultModuleFileSystem() { + DefaultModuleFileSystem(FileHashCache fileHashCache) { + this.fileHashCache = fileHashCache; } public File baseDir() { @@ -110,7 +113,26 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { } 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); + } + + @Override + public List<File> changedFiles(FileQuery query) { + return files(query, true); + } + + 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)); } @@ -142,7 +164,7 @@ public class DefaultModuleFileSystem implements ModuleFileSystem { } private void applyFilters(List<File> result, FileFilterContext context, - Collection<FileSystemFilter> filters, Collection<File> dirs) { + Collection<FileSystemFilter> filters, Collection<File> dirs) { for (File dir : dirs) { if (dir.exists()) { context.setRelativeDir(dir); 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 new file mode 100644 index 00000000000..34452fa2305 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileHashCache.java @@ -0,0 +1,116 @@ +/* + * 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.ModuleFileSystem; +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.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; + private ModuleFileSystem fs; + + public FileHashCache(ModuleFileSystem fs, ProjectDefinition module, PathResolver pathResolver, HashBuilder hashBuilder, + Snapshot snapshot, + SnapshotDataDao snapshotDataDao, + PastSnapshotFinder pastSnapshotFinder) { + this.fs = fs; + 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) { + String relativePath = pathResolver.relativePath(module.getBaseDir(), file); + if (!currentHashCache.containsKey(relativePath)) { + currentHashCache.put(relativePath, hashBuilder.computeHashNormalizeLineEnds(file, fs.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() { + } +} 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/HashBuilder.java new file mode 100644 index 00000000000..2db7946e67c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/HashBuilder.java @@ -0,0 +1,91 @@ +/* + * 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.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.nio.charset.Charset; +import java.security.MessageDigest; + +/** + * @since 4.0 + */ +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +public final class HashBuilder implements BatchExtension { + + /** + * Compute hash of a file ignoring line ends differences. + * Maximum performance is needed. + */ + public String computeHashNormalizeLineEnds(File file, Charset charset) { + Reader reader = null; + try { + MessageDigest md5Digest = DigestUtils.getMd5Digest(); + md5Digest.reset(); + reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); + int i = reader.read(); + boolean afterCR = true; + while (i != -1) { + char c = (char) i; + if (afterCR) { + afterCR = false; + if (c == '\n') { + // Ignore + i = reader.read(); + continue; + } + } + if (c == '\r') { + afterCR = true; + c = '\n'; + } + md5Digest.update(charToBytesUTF(c)); + i = reader.read(); + } + return Hex.encodeHexString(md5Digest.digest()); + } catch (IOException e) { + throw new SonarException("Unable to compute file hash", e); + } finally { + IOUtils.closeQuietly(reader); + } + } + + public static 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; + b[bpos] = (byte) ((buffer[i] & 0xFF00) >> 8); + b[bpos + 1] = (byte) (buffer[i] & 0x00FF); + } + return b; + } +} 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 1cb0bc76fe5..3efae8a0ec0 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 @@ -40,9 +40,10 @@ public class ModuleFileSystemProvider extends ProviderAdapter { private DefaultModuleFileSystem singleton; public DefaultModuleFileSystem provide(ProjectDefinition module, PathResolver pathResolver, TempDirectories tempDirectories, - LanguageFilters languageFilters, Settings settings, FileSystemFilter[] pluginFileFilters) { + LanguageFilters languageFilters, Settings settings, FileSystemFilter[] pluginFileFilters, + FileHashCache fileHashCache) { if (singleton == null) { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(fileHashCache); fs.setLanguageFilters(languageFilters); fs.setBaseDir(module.getBaseDir()); fs.setBuildDir(module.getBuildDir()); @@ -97,7 +98,6 @@ public class ModuleFileSystemProvider extends ProviderAdapter { } } - private void initBinaryDirs(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem fs) { for (String path : module.getBinaries()) { File dir = pathResolver.relativeFile(module.getBaseDir(), path); 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 c9e7ee4d478..30ca6a3fa6f 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 @@ -22,6 +22,7 @@ package org.sonar.batch.scan.filesystem; import org.apache.commons.io.filefilter.FileFilterUtils; 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; @@ -29,6 +30,7 @@ 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 java.io.File; import java.io.IOException; @@ -38,11 +40,15 @@ import java.util.List; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class DefaultModuleFileSystemTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Test public void test_new_file_system() throws IOException { File basedir = temp.newFolder("base"); @@ -50,7 +56,7 @@ public class DefaultModuleFileSystemTest { LanguageFilters languageFilters = mock(LanguageFilters.class); FileSystemFilter fileFilter = mock(FileSystemFilter.class); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) .setBaseDir(basedir) .setWorkingDir(workingDir) .addBinaryDir(new File(basedir, "target/classes")) @@ -73,7 +79,7 @@ public class DefaultModuleFileSystemTest { @Test public void default_source_encoding() { File basedir = temp.newFolder("base"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) .setBaseDir(basedir) .setSettings(new Settings()); @@ -86,7 +92,7 @@ public class DefaultModuleFileSystemTest { File basedir = temp.newFolder("base"); Settings settings = new Settings(); settings.setProperty(CoreProperties.ENCODING_PROPERTY, "UTF-8"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) .setBaseDir(basedir) .setSettings(settings); @@ -97,10 +103,11 @@ public class DefaultModuleFileSystemTest { @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() + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) .setBaseDir(basedir) .setWorkingDir(temp.newFolder()) - .addSourceDir(new File(basedir, "src")); + .addSourceDir(new File(basedir, "src")) + .setSettings(new Settings()); List<File> files = fileSystem.files(FileQuery.onSource()); assertThat(files).hasSize(1); @@ -110,12 +117,13 @@ public class DefaultModuleFileSystemTest { @Test public void should_load_source_files_by_language() throws IOException { File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + 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()))); + .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); @@ -129,11 +137,12 @@ public class DefaultModuleFileSystemTest { @Test public void should_load_test_files() throws IOException { File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + 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")); + .addTestDir(new File(basedir, "src/test/java")) + .setSettings(new Settings()); assertThat(fileSystem.testDirs()).hasSize(1); List<File> testFiles = fileSystem.files(FileQuery.onTest()); @@ -147,12 +156,13 @@ public class DefaultModuleFileSystemTest { @Test public void should_load_test_files_by_language() throws IOException { File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + 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()))); + .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); @@ -174,11 +184,12 @@ public class DefaultModuleFileSystemTest { @Test public void should_apply_file_filters() throws IOException { File basedir = new File(resourcesDir(), "main_and_test_files"); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + 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"))); + .addFilters(new FileFilterWrapper(FileFilterUtils.nameFileFilter("Foo.java"))) + .setSettings(new Settings()); List<File> files = fileSystem.files(FileQuery.onSource()); assertThat(files).hasSize(1); @@ -191,7 +202,7 @@ public class DefaultModuleFileSystemTest { } public String[] getFileSuffixes() { - return new String[]{"php"}; + return new String[] {"php"}; } } @@ -201,14 +212,14 @@ public class DefaultModuleFileSystemTest { } public String[] getFileSuffixes() { - return new String[]{"java", "jav"}; + return new String[] {"java", "jav"}; } } @Test public void test_reset_dirs() throws IOException { File basedir = temp.newFolder(); - DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem() + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(mock(FileHashCache.class)) .setBaseDir(basedir) .setWorkingDir(basedir) .addSourceDir(new File(basedir, "src/main/java")) @@ -229,4 +240,63 @@ public class DefaultModuleFileSystemTest { assertThat(fileSystem.binaryDirs()).hasSize(1); assertThat(fileSystem.binaryDirs().get(0).getCanonicalPath()).isEqualTo(existingDir.getCanonicalPath()); } + + @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()); + } + + @Test + public void should_filter_changed_files() throws Exception { + File basedir = new File(resourcesDir(), "main_and_test_files"); + Settings settings = new Settings(); + 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)).thenReturn("foohash"); + when(fileHashCache.getPreviousHash(hello)).thenReturn("oldhellohash"); + when(fileHashCache.getCurrentHash(hello)).thenReturn("oldhellohash"); + when(fileHashCache.getPreviousHash(fooTest)).thenReturn("oldfooTesthash"); + when(fileHashCache.getCurrentHash(fooTest)).thenReturn("fooTesthash"); + when(fileHashCache.getPreviousHash(helloTest)).thenReturn("oldhelloTesthash"); + when(fileHashCache.getCurrentHash(helloTest)).thenReturn("oldhelloTesthash"); + + DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem(fileHashCache) + .setBaseDir(basedir) + .setWorkingDir(temp.newFolder()) + .addSourceDir(mainDir) + .addTestDir(testDir) + .setSettings(settings); + + assertThat(fileSystem.files(FileQuery.onSource())).containsExactly(foo, hello); + assertThat(fileSystem.files(FileQuery.onTest())).containsExactly(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/FileHashCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashCacheTest.java new file mode 100644 index 00000000000..1342044a151 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileHashCacheTest.java @@ -0,0 +1,127 @@ +/* + * 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.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; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class FileHashCacheTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @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(moduleFileSystem, ProjectDefinition.create().setBaseDir(baseDir), new PathResolver(), new HashBuilder(), snapshot, + snapshotDataDao, pastSnapshotFinder); + } + + @Test + public void should_return_null_if_no_previous_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(); + } + + @Test + public void should_return_null_if_no_previous_snapshot_data() 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(); + } + + @Test + public void should_return_previous_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"); + 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"); + 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"); + String hash = "9a8742076ef9ffa5591f633704c2286b"; + assertThat(cache.getCurrentHash(file)).isEqualTo(hash); + + // Modify file + FileUtils.write(file, "bar"); + assertThat(cache.getCurrentHash(file)).isEqualTo(hash); + } +} 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 065dd3490e6..19f2ae4c62d 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,7 +38,7 @@ public class FileSystemLoggerTest { @Test public void log() { - DefaultModuleFileSystem fs = new DefaultModuleFileSystem(); + DefaultModuleFileSystem fs = new DefaultModuleFileSystem(mock(FileHashCache.class)); File src = temp.newFolder("src"); File test = temp.newFolder("test"); File base = temp.newFolder("base"); 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/HashBuilderTest.java new file mode 100644 index 00000000000..f15876f8d54 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/HashBuilderTest.java @@ -0,0 +1,75 @@ +/* + * 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.Charsets; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +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 { + + @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"); + } + + @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); + + File file2 = temp.newFile(); + FileUtils.write(file2, "foobar\r\nfofo", Charsets.UTF_8); + String hash2 = hashBuilder.computeHashNormalizeLineEnds(file2, Charsets.UTF_8); + + File file3 = temp.newFile(); + FileUtils.write(file3, "foobar\rfofo", Charsets.UTF_8); + String hash3 = hashBuilder.computeHashNormalizeLineEnds(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); + + 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 { + File tempFolder = temp.newFolder(); + hashBuilder.computeHashNormalizeLineEnds(new File(tempFolder, "unknowFile.txt"), Charsets.UTF_8); + } +} 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 cc387e9f6f4..600fa6a4aac 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 @@ -54,7 +54,7 @@ public class ModuleFileSystemProviderTest { .setBaseDir(baseDir) .setWorkDir(workDir); ModuleFileSystem fs = provider.provide(module, new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0]); + new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); assertThat(fs).isNotNull(); assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); @@ -70,7 +70,7 @@ public class ModuleFileSystemProviderTest { ModuleFileSystemProvider provider = new ModuleFileSystemProvider(); ModuleFileSystem fs = provider.provide(newSimpleModule(), new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0]); + new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); assertThat(fs.sourceCharset()).isEqualTo(Charset.defaultCharset()); } @@ -83,7 +83,7 @@ public class ModuleFileSystemProviderTest { 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]); + settings, new FileSystemFilter[0], mock(FileHashCache.class)); assertThat(fs.sourceCharset()).isEqualTo(Charsets.ISO_8859_1); } @@ -109,7 +109,7 @@ public class ModuleFileSystemProviderTest { .addBinaryDir("target/classes"); ModuleFileSystem fs = provider.provide(project, new PathResolver(), new TempDirectories(), mock(LanguageFilters.class), - new Settings(), new FileSystemFilter[0]); + new Settings(), new FileSystemFilter[0], mock(FileHashCache.class)); assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(baseDir.getCanonicalPath()); assertThat(fs.buildDir().getCanonicalPath()).isEqualTo(buildDir.getCanonicalPath()); |