]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6068 Improve performance of FileSystem query operation
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 10 Feb 2015 13:59:36 +0000 (14:59 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 12 Feb 2015 15:01:52 +0000 (16:01 +0100)
46 files changed:
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/RandomAccessSensor.java [new file with mode: 0644]
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/MeasureSensorTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SymbolReferencesSensorTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/XooTokenizerSensorTest.java
sonar-batch/pom.xml
sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectConverter.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/AdditionalFilePredicates.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java
sonar-batch/src/test/java/org/sonar/batch/debt/SqaleRatingDecoratorTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/ignore/scanner/IssueExclusionsLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/Benchmark.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java
sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/LanguageVerifierTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java
sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbsolutePathPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AndPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FalsePredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/LanguagePredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/NotPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OrPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/PathPatternPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/RelativePathPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/StatusPredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TruePredicate.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TypePredicate.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/AndPredicateTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/OrPredicateTest.java [new file with mode: 0644]

index a307f859edf30f945529a58a5c3a858d7b23510e..906bc09049d1ccff6c8c86a17fb31428914137f4 100644 (file)
@@ -20,7 +20,9 @@
 package org.sonar.plugins.cpd;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
@@ -28,24 +30,29 @@ import org.sonar.api.resources.Java;
 import org.sonar.batch.duplication.BlockCache;
 import org.sonar.plugins.cpd.index.IndexFactory;
 
+import java.io.IOException;
+
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class CpdSensorTest {
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
   JavaCpdEngine sonarEngine;
   DefaultCpdEngine sonarBridgeEngine;
   CpdSensor sensor;
   Settings settings;
 
   @Before
-  public void setUp() {
+  public void setUp() throws IOException {
     IndexFactory indexFactory = mock(IndexFactory.class);
     sonarEngine = new JavaCpdEngine(indexFactory, null, null, null);
     sonarBridgeEngine = new DefaultCpdEngine(indexFactory, new CpdMappings(), null, null, mock(BlockCache.class), null);
     settings = new Settings(new PropertyDefinitions(CpdPlugin.class));
 
-    DefaultFileSystem fs = new DefaultFileSystem();
+    DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
     sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings, fs);
   }
 
index de8a28913fa8877ca8d020d6312faf6e459f5a83..a4e0bc25438146dd25ce0a612d3183398ead85ec 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.xoo.rule.ChecksSensor;
 import org.sonar.xoo.rule.CreateIssueByInternalKeySensor;
 import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor;
 import org.sonar.xoo.rule.OneIssuePerLineSensor;
+import org.sonar.xoo.rule.RandomAccessSensor;
 import org.sonar.xoo.rule.XooFakeExporter;
 import org.sonar.xoo.rule.XooFakeImporter;
 import org.sonar.xoo.rule.XooFakeImporterWithMessages;
@@ -64,6 +65,7 @@ public class XooPlugin extends SonarPlugin {
       SymbolReferencesSensor.class,
       XooTokenizerSensor.class,
       ChecksSensor.class,
+      RandomAccessSensor.class,
 
       OneIssuePerLineSensor.class,
       OneIssueOnDirPerFileSensor.class,
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/RandomAccessSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/RandomAccessSensor.java
new file mode 100644 (file)
index 0000000..ecd3eb1
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.xoo.rule;
+
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.xoo.Xoo;
+
+import java.io.File;
+import java.io.IOException;
+
+public class RandomAccessSensor implements Sensor {
+
+  private static final String SONAR_XOO_RANDOM_ACCESS_ISSUE_PATHS = "sonar.xoo.randomAccessIssue.paths";
+  public static final String RULE_KEY = "RandomAccessIssue";
+
+  @Override
+  public void describe(SensorDescriptor descriptor) {
+    descriptor
+      .name("One Issue Per File with Random Access")
+      .workOnLanguages(Xoo.KEY)
+      .createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY);
+  }
+
+  @Override
+  public void execute(SensorContext context) {
+    if (!context.settings().hasKey(SONAR_XOO_RANDOM_ACCESS_ISSUE_PATHS)) {
+      return;
+    }
+    File f = new File(context.settings().getString(SONAR_XOO_RANDOM_ACCESS_ISSUE_PATHS));
+    FileSystem fs = context.fileSystem();
+    FilePredicates p = fs.predicates();
+    try {
+      for (String path : FileUtils.readLines(f)) {
+        createIssues(fs.inputFile(p.and(p.hasPath(path), p.hasType(Type.MAIN), p.hasLanguage(Xoo.KEY))), context);
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  private void createIssues(InputFile file, SensorContext context) {
+    RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY);
+    context.addIssue(context.issueBuilder()
+      .ruleKey(ruleKey)
+      .onFile(file)
+      .atLine(1)
+      .message("This issue is generated on each file")
+      .build());
+  }
+}
index 172c826c3c3c12f4b4a69ff6f664e3053bcf58f6..d37f383ca3ffbb93bd4d43b54b938a335a60be9f 100644 (file)
@@ -27,6 +27,6 @@ public class XooPluginTest {
 
   @Test
   public void provide_extensions() {
-    assertThat(new XooPlugin().getExtensions()).hasSize(15);
+    assertThat(new XooPlugin().getExtensions()).hasSize(16);
   }
 }
index c06d4f307d36f3931a0c509fcbae88214b370620..bad1b641ebcaa18e8dba97da03c868ebcf9ad714 100644 (file)
@@ -61,7 +61,7 @@ public class MeasureSensorTest {
     baseDir = temp.newFolder();
     metricFinder = mock(MetricFinder.class);
     sensor = new MeasureSensor(metricFinder);
-    fileSystem = new DefaultFileSystem();
+    fileSystem = new DefaultFileSystem(baseDir);
     when(context.fileSystem()).thenReturn(fileSystem);
   }
 
index 68fd40d0f80beaf4bdabd2c2801344b0b6860a49..be3c48d7953b28c0a8c75de68178ac4f6d37e37c 100644 (file)
@@ -52,7 +52,7 @@ public class SymbolReferencesSensorTest {
   public void prepare() throws IOException {
     baseDir = temp.newFolder();
     sensor = new SymbolReferencesSensor();
-    fileSystem = new DefaultFileSystem();
+    fileSystem = new DefaultFileSystem(baseDir);
     when(context.fileSystem()).thenReturn(fileSystem);
   }
 
index 9918a3625ef8f16d33f9f8e37380003300e2f123..70ffaf2bdb2e54b33a7e9af0918dbf902d8125c4 100644 (file)
@@ -52,7 +52,7 @@ public class SyntaxHighlightingSensorTest {
   public void prepare() throws IOException {
     baseDir = temp.newFolder();
     sensor = new SyntaxHighlightingSensor();
-    fileSystem = new DefaultFileSystem();
+    fileSystem = new DefaultFileSystem(baseDir);
     when(context.fileSystem()).thenReturn(fileSystem);
   }
 
index 941798f9436eb37e5957fd80d1a2a19643375367..29107b3ec76d2c0959dbf69f23d8ef23ab747546 100644 (file)
@@ -57,7 +57,7 @@ public class XooTokenizerSensorTest {
   public void prepare() throws IOException {
     baseDir = temp.newFolder();
     sensor = new XooTokenizerSensor();
-    fileSystem = new DefaultFileSystem();
+    fileSystem = new DefaultFileSystem(baseDir);
     when(context.fileSystem()).thenReturn(fileSystem);
     settings = new Settings();
     when(context.settings()).thenReturn(settings);
index 8d8c4bd9e94dda475ff584cd0718ecaceb6b0883..dc8acae0f8e6938ee8295e10e612386f96745bb8 100644 (file)
@@ -9,6 +9,10 @@
 
   <artifactId>sonar-batch</artifactId>
   <name>SonarQube :: Batch</name>
+  
+  <properties>
+    <enableBenchmarkAssertions>false</enableBenchmarkAssertions>
+  </properties>
 
   <dependencies>
     <dependency>
       </plugin>
     </plugins>
   </build>
+  
+  <profiles>
+    <profile>
+      <id>runBenchmarks</id>
+      <activation>
+        <property>
+          <name>runBenchmarks</name>
+        </property>
+      </activation>
+      <properties>
+        <enableBenchmarkAssertions>true</enableBenchmarkAssertions>
+      </properties>
+    </profile>
+  </profiles>
 </project>
index 2ef3fe6fa5481cf64de08c2c427191c9ca90f711..ebd0294f9a7edf421758d03192a7bf6965f4cfd9 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.java.api.JavaUtils;
 
 import javax.annotation.Nullable;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
index 6332d51a2ed5b6bee82297e990941b1fc318d6a0..f4d217f744240009d97e4b6b510bc1aca8698bcc 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.batch.scan.filesystem;
 
 import org.apache.commons.io.FilenameUtils;
-import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.AbstractFilePredicate;
 import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
 
 /**
@@ -34,7 +34,7 @@ class AdditionalFilePredicates {
     // only static inner classes
   }
 
-  static class KeyPredicate implements FilePredicate {
+  static class KeyPredicate extends AbstractFilePredicate {
     private final String key;
 
     KeyPredicate(String key) {
@@ -47,7 +47,7 @@ class AdditionalFilePredicates {
     }
   }
 
-  static class DeprecatedKeyPredicate implements FilePredicate {
+  static class DeprecatedKeyPredicate extends AbstractFilePredicate {
     private final String key;
 
     DeprecatedKeyPredicate(String key) {
@@ -60,7 +60,7 @@ class AdditionalFilePredicates {
     }
   }
 
-  static class SourceRelativePathPredicate implements FilePredicate {
+  static class SourceRelativePathPredicate extends AbstractFilePredicate {
     private final String path;
 
     SourceRelativePathPredicate(String s) {
@@ -73,7 +73,7 @@ class AdditionalFilePredicates {
     }
   }
 
-  static class SourceDirPredicate implements FilePredicate {
+  static class SourceDirPredicate extends AbstractFilePredicate {
     private final String path;
 
     SourceDirPredicate(String s) {
index 2706e54731fe1f28c3e72dec1f07816e93ec5c1c..ad75475ce85a66959c249f92c184f5adb0c5f348 100644 (file)
@@ -84,14 +84,11 @@ public class DefaultModuleFileSystem extends DefaultFileSystem implements Module
   private DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, String moduleKey, Settings settings,
     FileIndexer indexer, ModuleFileSystemInitializer initializer,
     @Nullable ComponentIndexer componentIndexer) {
-    super(moduleInputFileCache);
+    super(initializer.baseDir(), moduleInputFileCache);
     this.componentIndexer = componentIndexer;
     this.moduleKey = moduleKey;
     this.settings = settings;
     this.indexer = indexer;
-    if (initializer.baseDir() != null) {
-      setBaseDir(initializer.baseDir());
-    }
     setWorkDir(initializer.workingDir());
     this.buildDir = initializer.buildDir();
     this.sourceDirsOrFiles = initializer.sources();
index 8472e5726e5f250937cbff74a0523fc5de31674e..b932295dc40ecded55ee2b4d9ab794f23c55644f 100644 (file)
@@ -24,7 +24,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.RelativePathPredicate;
 
 public class ModuleInputFileCache extends DefaultFileSystem.Cache implements BatchComponent {
 
@@ -37,17 +36,17 @@ public class ModuleInputFileCache extends DefaultFileSystem.Cache implements Bat
   }
 
   @Override
-  protected Iterable<InputFile> inputFiles() {
+  public Iterable<InputFile> inputFiles() {
     return projectCache.filesByModule(moduleKey);
   }
 
   @Override
-  protected InputFile inputFile(RelativePathPredicate predicate) {
-    return projectCache.getFile(moduleKey, predicate.path());
+  public InputFile inputFile(String relativePath) {
+    return projectCache.getFile(moduleKey, relativePath);
   }
 
   @Override
-  protected InputDir inputDir(String relativePath) {
+  public InputDir inputDir(String relativePath) {
     return projectCache.getDir(moduleKey, relativePath);
   }
 
index 1b1840a84439ccb299fa92dd9ab1649b27a1e60e..aafe7375f6d795c5feaf4c177538d9350da15435 100644 (file)
@@ -44,7 +44,10 @@ import static com.google.common.collect.Lists.newArrayList;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class SqaleRatingDecoratorTest {
@@ -70,7 +73,7 @@ public class SqaleRatingDecoratorTest {
   public void setUp() throws Exception {
     settings = new Settings();
 
-    fs = new DefaultFileSystem();
+    fs = new DefaultFileSystem(temp.newFolder());
     fs.add(new DefaultInputFile(file.getPath())
       .setLanguage("java")
       .setFile(temp.newFile("Foo.java")));
index 5b97df2c7f5cf269c1e5576e18e79e4bbb24a46b..2ef3f0507bb90e5c70b4afdf210217b7b0701add 100644 (file)
@@ -62,13 +62,14 @@ public class IssueExclusionsLoaderTest {
   @Mock
   private PatternMatcher patternMatcher;
 
-  DefaultFileSystem fs = new DefaultFileSystem().setEncoding(UTF_8);
-  IssueExclusionsLoader scanner;
-  File baseDir;
+  private DefaultFileSystem fs;
+  private IssueExclusionsLoader scanner;
+  private File baseDir;
 
   @Before
-  public void before() throws IOException {
+  public void before() throws Exception {
     baseDir = temp.newFolder();
+    fs = new DefaultFileSystem(baseDir).setEncoding(UTF_8);
     MockitoAnnotations.initMocks(this);
     scanner = new IssueExclusionsLoader(regexpScanner, exclusionPatternInitializer, inclusionPatternInitializer, fs);
   }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/Benchmark.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/Benchmark.java
new file mode 100644 (file)
index 0000000..49d0ff3
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest;
+
+import org.hamcrest.Matchers;
+import org.junit.rules.ErrorCollector;
+import org.slf4j.LoggerFactory;
+
+public class Benchmark extends ErrorCollector {
+
+  private static final boolean ENABLED = "true".equals(System.getProperty("enableBenchmarkAssertions"));
+
+  static {
+    if (ENABLED) {
+      LoggerFactory.getLogger(Benchmark.class).warn("Assertions are calibrated for SonarSource dedicated box. " +
+        "They can be disabled by setting the property -DenableBenchmarkAssertions=false.");
+    }
+  }
+
+  public void expectBetween(String label, long val, long min, long max) {
+    if (ENABLED) {
+      checkThat(label, val, Matchers.allOf(Matchers.greaterThan(min), Matchers.lessThan(max)));
+    }
+  }
+
+  public void expectLessThanOrEqualTo(String label, long val, long max) {
+    if (ENABLED) {
+      checkThat(label, val, Matchers.lessThan(max));
+    }
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
new file mode 100644 (file)
index 0000000..91bc57e
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.fs;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
+import org.sonar.batch.mediumtest.Benchmark;
+import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RandomFsAccessMediumTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Rule
+  public Benchmark bench = new Benchmark();
+
+  public BatchMediumTester tester = BatchMediumTester.builder()
+    .registerPlugin("xoo", new XooPlugin())
+    .addDefaultQProfile("xoo", "Sonar Way")
+    .activateRule(new ActiveRule("xoo", "RandomAccessIssue", null, "One issue per line", "MAJOR", null, "xoo"))
+    .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+    .build();
+
+  @Before
+  public void prepare() {
+    tester.start();
+  }
+
+  @After
+  public void stop() {
+    tester.stop();
+  }
+
+  @Test
+  public void testRandomFsAccessByAbsolutePath() throws IOException {
+
+    File baseDir = temp.newFolder();
+    File srcDir = prepareBigProject(baseDir);
+
+    File paths = new File(baseDir, "paths.txt");
+    int ISSUE_COUNT = 10000;
+    for (int i = 0; i < ISSUE_COUNT; i++) {
+      File xooFile = new File(srcDir, "sample" + (i / 10 + 1) + ".xoo");
+      FileUtils.write(paths, xooFile.getAbsolutePath() + "\n", Charsets.UTF_8, true);
+    }
+
+    long start = System.currentTimeMillis();
+    TaskResult result = tester.newTask()
+      .properties(ImmutableMap.<String, String>builder()
+        .put("sonar.task", "scan")
+        .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+        .put("sonar.projectKey", "com.foo.project")
+        .put("sonar.projectName", "Foo Project")
+        .put("sonar.projectVersion", "1.0-SNAPSHOT")
+        .put("sonar.projectDescription", "Description of Foo Project")
+        .put("sonar.sources", "src")
+        .put("sonar.xoo.randomAccessIssue.paths", paths.getAbsolutePath())
+        .build())
+      .start();
+
+    assertThat(result.issues()).hasSize(ISSUE_COUNT);
+    bench.expectLessThanOrEqualTo("Time to create " + ISSUE_COUNT + " issues on random files using FileSystem query", System.currentTimeMillis() - start, 2000);
+  }
+
+  @Test
+  public void testRandomFsAccessByRelativePath() throws IOException {
+
+    File baseDir = temp.newFolder();
+    File srcDir = prepareBigProject(baseDir);
+
+    File paths = new File(baseDir, "paths.txt");
+    int ISSUE_COUNT = 10000;
+    for (int i = 0; i < ISSUE_COUNT; i++) {
+      FileUtils.write(paths, "src/sample" + (i / 10 + 1) + ".xoo\n", Charsets.UTF_8, true);
+    }
+
+    TaskResult result = tester.newTask()
+      .properties(ImmutableMap.<String, String>builder()
+        .put("sonar.task", "scan")
+        .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+        .put("sonar.projectKey", "com.foo.project")
+        .put("sonar.projectName", "Foo Project")
+        .put("sonar.projectVersion", "1.0-SNAPSHOT")
+        .put("sonar.projectDescription", "Description of Foo Project")
+        .put("sonar.sources", "src")
+        .put("sonar.xoo.randomAccessIssue.paths", paths.getAbsolutePath())
+        .build())
+      .start();
+
+    assertThat(result.issues()).hasSize(ISSUE_COUNT);
+
+  }
+
+  private File prepareBigProject(File baseDir) throws IOException {
+    File srcDir = new File(baseDir, "src");
+    srcDir.mkdir();
+
+    for (int i = 1; i <= 1000; i++) {
+      File xooFile = new File(srcDir, "sample" + i + ".xoo");
+      FileUtils.write(xooFile, "foo");
+    }
+    return srcDir;
+  }
+
+}
index cae57ace39e7a455f18049ac4e625b83962236b6..10ba3383a6c4ba786ccf1a81f3f47e879766745a 100644 (file)
  */
 package org.sonar.batch.rule;
 
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.measures.CoreMetrics;
@@ -32,10 +35,15 @@ import java.util.Date;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class QProfileSensorTest {
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
   static final Date DATE = UtcDateUtils.parseDateTime("2014-01-15T12:00:00+0000");
   static final QProfile JAVA_PROFILE = new QProfile().setKey("java-two").setName("Java Two").setLanguage("java")
     .setRulesUpdatedAt(DATE);
@@ -45,7 +53,12 @@ public class QProfileSensorTest {
   ModuleQProfiles moduleQProfiles = mock(ModuleQProfiles.class);
   Project project = mock(Project.class);
   SensorContext sensorContext = mock(SensorContext.class);
-  DefaultFileSystem fs = new DefaultFileSystem();
+  DefaultFileSystem fs;
+
+  @Before
+  public void prepare() throws Exception {
+    fs = new DefaultFileSystem(temp.newFolder());
+  }
 
   @Test
   public void to_string() throws Exception {
index da457212cd582e21ea6f569f2ba11011a1de700b..9c5db0f6d6af723fee02588cd24952455a905185 100644 (file)
@@ -23,24 +23,31 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
 import org.slf4j.Logger;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.MessageException;
 
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class QProfileVerifierTest {
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  DefaultFileSystem fs = new DefaultFileSystem();
-  ModuleQProfiles profiles;
-  Settings settings = new Settings();
+  private DefaultFileSystem fs;
+  private ModuleQProfiles profiles;
+  private Settings settings = new Settings();
 
   @Before
-  public void before() {
+  public void before() throws Exception {
+    fs = new DefaultFileSystem(temp.newFolder());
     profiles = mock(ModuleQProfiles.class);
     QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java");
     when(profiles.findByLanguage("java")).thenReturn(javaProfile);
index eb19d5428ea30a8309634e38291f9f365c910dfd..bb174f67f0b3fa35dd0efa8f0d9e4862706a04ba 100644 (file)
  */
 package org.sonar.batch.scan;
 
+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.fs.internal.DefaultFileSystem;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Java;
@@ -34,13 +36,21 @@ import static org.fest.assertions.Assertions.assertThat;
 
 public class LanguageVerifierTest {
 
-  Settings settings = new Settings();
-  LanguagesReferential languages = new DefaultLanguagesReferential(new Languages(Java.INSTANCE));
-  DefaultFileSystem fs = new DefaultFileSystem();
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
 
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  private Settings settings = new Settings();
+  private LanguagesReferential languages = new DefaultLanguagesReferential(new Languages(Java.INSTANCE));
+  private DefaultFileSystem fs;
+
+  @Before
+  public void prepare() throws Exception {
+    fs = new DefaultFileSystem(temp.newFolder());
+  }
+
   @Test
   public void language_is_not_set() throws Exception {
     LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
index a93bf9bae83eb55b00de5ac89d99ea0fd2861034..5decff6ff5c7fde901cdd844f0714482e00ea977 100644 (file)
@@ -23,6 +23,7 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.fs.InputFile;
@@ -46,6 +47,8 @@ import org.sonar.batch.duplication.BlockCache;
 import org.sonar.batch.duplication.DuplicationCache;
 import org.sonar.batch.index.ComponentDataCache;
 
+import java.io.IOException;
+
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -56,6 +59,9 @@ public class SensorContextAdapterTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
   private ActiveRules activeRules;
   private DefaultFileSystem fs;
   private SensorContextAdaptor adaptor;
@@ -64,9 +70,9 @@ public class SensorContextAdapterTest {
   private ResourcePerspectives resourcePerspectives;
 
   @Before
-  public void prepare() {
+  public void prepare() throws IOException {
     activeRules = new ActiveRulesBuilder().build();
-    fs = new DefaultFileSystem();
+    fs = new DefaultFileSystem(temp.newFolder());
     MetricFinder metricFinder = mock(MetricFinder.class);
     when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
     sensorContext = mock(SensorContext.class);
index 122b9c618deb6a69d684476f3dca3aac3d472afa..4ac1a0bc4783a1bdaafc7288ed22f46d92d8a72d 100644 (file)
@@ -56,19 +56,20 @@ public class ComponentIndexerTest {
 
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
-  File baseDir;
-  DefaultFileSystem fs = new DefaultFileSystem();
-  SonarIndex sonarIndex;
-  AbstractLanguage cobolLanguage;
-  Project project;
-  Settings settings;
+  private File baseDir;
+  private DefaultFileSystem fs;
+  private SonarIndex sonarIndex;
+  private AbstractLanguage cobolLanguage;
+  private Project project;
+  private Settings settings;
 
-  String aClaess;
-  String explicacao;
+  private String aClaess;
+  private String explicacao;
 
   @Before
   public void prepare() throws IOException {
     baseDir = temp.newFolder();
+    fs = new DefaultFileSystem(baseDir);
     sonarIndex = mock(SonarIndex.class);
     project = mock(Project.class);
     settings = new Settings();
index 6cd93d93da013ccb9e286f6a91c49331e73cc8b4..de39dbeb3f9047fb2f3f9667f6d8dff1445aa39f 100644 (file)
@@ -67,11 +67,11 @@ public class JsonReportTest {
   private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
 
   @org.junit.Rule
-  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+  public TemporaryFolder temp = new TemporaryFolder();
 
   JsonReport jsonReport;
   Resource resource = mock(Resource.class);
-  DefaultFileSystem fs = new DefaultFileSystem();
+  DefaultFileSystem fs;
   Server server = mock(Server.class);
   RuleFinder ruleFinder = mock(RuleFinder.class);
   Settings settings = new Settings();
@@ -80,7 +80,8 @@ public class JsonReportTest {
   private UserFinder userFinder;
 
   @Before
-  public void before() {
+  public void before() throws Exception {
+    fs = new DefaultFileSystem(temp.newFolder());
     SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
     when(resource.getEffectiveKey()).thenReturn("Action.java");
     when(server.getVersion()).thenReturn("3.6");
@@ -169,7 +170,7 @@ public class JsonReportTest {
 
   @Test
   public void should_export_issues_to_file() throws IOException {
-    File workDir = temporaryFolder.newFolder("sonar");
+    File workDir = temp.newFolder("sonar");
     fs.setWorkDir(workDir);
 
     Rule rule = Rule.create("squid", "AvoidCycles").setName("Avoid Cycles");
index 6c21f0c11848783ed4d02bb152c82927bcae58e8..e32a577a189362ac7acc36d1d88ce481d14e7cbe 100644 (file)
  */
 package org.sonar.batch.scan2;
 
-import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-
 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.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
 import org.sonar.api.rule.RuleKey;
+
 import static org.fest.assertions.Assertions.assertThat;
 
 public class AnalyzerOptimizerTest {
 
-  DefaultFileSystem fs = new DefaultFileSystem();
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
 
   @Rule
   public ExpectedException thrown = ExpectedException.none();
+
+  private DefaultFileSystem fs;
   private AnalyzerOptimizer optimizer;
 
   @Before
-  public void prepare() {
+  public void prepare() throws Exception {
+    fs = new DefaultFileSystem(temp.newFolder());
     optimizer = new AnalyzerOptimizer(fs, new ActiveRulesBuilder().build());
   }
 
index 830859ed3ba24cb59a42f862b71808596a94c5f9..1174c4cc0f78127377cf2b14a58adbf8f3155f73 100644 (file)
  */
 package org.sonar.api.batch.fs;
 
+
 /**
  * Determines if a file must be kept in search results. See {@link org.sonar.api.batch.fs.FileSystem}
  * and {@link org.sonar.api.batch.fs.FilePredicates}.
  * @since 4.2
  */
 public interface FilePredicate {
+  /**
+   * Test if provided file is valid for this predicate
+   */
   boolean apply(InputFile inputFile);
+
 }
index 0c285f686e2b4075cc89021a5bd9e9de74f3e523..be0ba6a9848c18c187f920cf2a6c993f3f467256 100644 (file)
@@ -141,4 +141,17 @@ public interface FileSystem extends BatchComponent {
    * Languages detected in all files, whatever their type (main or test)
    */
   SortedSet<String> languages();
+
+  /**
+   * Interface of the underlying file index.
+   */
+  public static interface Index {
+    Iterable<InputFile> inputFiles();
+
+    @CheckForNull
+    InputFile inputFile(String relativePath);
+
+    @CheckForNull
+    InputDir inputDir(String relativePath);
+  }
 }
index 5090f09ce4a1ff8f8a3d877ca5f82f365446638d..816333126f8f7d9ed44824e98a8fa5563043bee6 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.api.utils.PathUtils;
 
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+
 /**
  * @since 4.2
  */
-class AbsolutePathPredicate implements FilePredicate {
+class AbsolutePathPredicate extends AbstractFilePredicate {
 
   private final String path;
+  private final File baseDir;
 
-  AbsolutePathPredicate(String path) {
+  AbsolutePathPredicate(String path, File baseDir) {
+    this.baseDir = baseDir;
     this.path = PathUtils.sanitize(path);
   }
 
@@ -38,4 +45,19 @@ class AbsolutePathPredicate implements FilePredicate {
   public boolean apply(InputFile f) {
     return path.equals(f.absolutePath());
   }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    String relative = PathUtils.sanitize(new PathResolver().relativePath(baseDir, new File(path)));
+    if (relative == null) {
+      return Collections.<InputFile>emptyList();
+    }
+    InputFile f = index.inputFile(relative);
+    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
+  }
+
+  @Override
+  public int priority() {
+    return USE_INDEX;
+  }
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractFilePredicate.java
new file mode 100644 (file)
index 0000000..ba0560b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * Partial implementation of {@link FilePredicate}.
+ * @since 5.1
+ */
+public abstract class AbstractFilePredicate implements OptimizedFilePredicate {
+
+  protected static final int DEFAULT_PRIORITY = 10;
+  protected static final int USE_INDEX = 20;
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return Iterables.filter(target, new Predicate<InputFile>() {
+      @Override
+      public boolean apply(InputFile input) {
+        return AbstractFilePredicate.this.apply(input);
+      }
+    });
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return filter(index.inputFiles());
+  }
+
+  @Override
+  public int priority() {
+    return DEFAULT_PRIORITY;
+  }
+
+  @Override
+  public final int compareTo(OptimizedFilePredicate o) {
+    return o.priority() - priority();
+  }
+
+}
index 0494ed524dc8c9866c013800147ed0af9b0b8210..a9d266f324d73d5f7b8de0714c2d685fb3c15412 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
 import org.sonar.api.batch.fs.InputFile;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @since 4.2
  */
-class AndPredicate implements FilePredicate {
+class AndPredicate extends AbstractFilePredicate {
 
-  private final Collection<FilePredicate> predicates;
+  private final List<OptimizedFilePredicate> predicates = new ArrayList<OptimizedFilePredicate>();
 
-  AndPredicate(Collection<FilePredicate> predicates) {
-    this.predicates = predicates;
+  private AndPredicate() {
+  }
+
+  public static FilePredicate create(Collection<FilePredicate> predicates) {
+    if (predicates.isEmpty()) {
+      return TruePredicate.TRUE;
+    }
+    AndPredicate result = new AndPredicate();
+    for (FilePredicate filePredicate : predicates) {
+      if (filePredicate == TruePredicate.TRUE) {
+        continue;
+      } else if (filePredicate == FalsePredicate.FALSE) {
+        return FalsePredicate.FALSE;
+      } else if (filePredicate instanceof AndPredicate) {
+        result.predicates.addAll(((AndPredicate) filePredicate).predicates);
+      } else {
+        result.predicates.add(OptimizedFilePredicateAdapter.create(filePredicate));
+      }
+    }
+    Collections.sort(result.predicates);
+    return result;
   }
 
   @Override
   public boolean apply(InputFile f) {
-    for (FilePredicate predicate : predicates) {
+    for (OptimizedFilePredicate predicate : predicates) {
       if (!predicate.apply(f)) {
         return false;
       }
@@ -45,4 +69,31 @@ class AndPredicate implements FilePredicate {
     return true;
   }
 
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    Iterable<InputFile> result = target;
+    for (OptimizedFilePredicate predicate : predicates) {
+      result = predicate.filter(result);
+    }
+    return result;
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    if (predicates.isEmpty()) {
+      return index.inputFiles();
+    }
+    // Optimization, use get on first predicate then filter with next predicates
+    Iterable<InputFile> result = predicates.get(0).get(index);
+    for (int i = 1; i < predicates.size(); i++) {
+      result = predicates.get(i).filter(result);
+    }
+    return result;
+  }
+
+  @VisibleForTesting
+  Collection<OptimizedFilePredicate> predicates() {
+    return predicates;
+  }
+
 }
index 8036d90a3ead20941b13d70c644cbc697d697358..1a5097e811904d608532b2483d907f4b9af859f8 100644 (file)
@@ -35,10 +35,14 @@ import java.util.List;
  * @since 4.2
  */
 public class DefaultFilePredicates implements FilePredicates {
+
+  private final File baseDir;
+
   /**
    * Client code should use {@link org.sonar.api.batch.fs.FileSystem#predicates()} to get an instance
    */
-  DefaultFilePredicates() {
+  DefaultFilePredicates(File baseDir) {
+    this.baseDir = baseDir;
   }
 
   /**
@@ -55,15 +59,13 @@ public class DefaultFilePredicates implements FilePredicates {
     return FalsePredicate.FALSE;
   }
 
-  /**
-   * Warning - not efficient because absolute path is not indexed yet.
-   */
+  @Override
   public FilePredicate hasAbsolutePath(String s) {
-    return new AbsolutePathPredicate(s);
+    return new AbsolutePathPredicate(s, baseDir);
   }
 
   /**
-   * TODO document that non-normalized path and Windows-style path are supported
+   * non-normalized path and Windows-style path are supported
    */
   public FilePredicate hasRelativePath(String s) {
     return new RelativePathPredicate(s);
@@ -143,26 +145,26 @@ public class DefaultFilePredicates implements FilePredicates {
   }
 
   public FilePredicate or(Collection<FilePredicate> or) {
-    return new OrPredicate(or);
+    return OrPredicate.create(or);
   }
 
   public FilePredicate or(FilePredicate... or) {
-    return new OrPredicate(Arrays.asList(or));
+    return OrPredicate.create(Arrays.asList(or));
   }
 
   public FilePredicate or(FilePredicate first, FilePredicate second) {
-    return new OrPredicate(Arrays.asList(first, second));
+    return OrPredicate.create(Arrays.asList(first, second));
   }
 
   public FilePredicate and(Collection<FilePredicate> and) {
-    return new AndPredicate(and);
+    return AndPredicate.create(and);
   }
 
   public FilePredicate and(FilePredicate... and) {
-    return new AndPredicate(Arrays.asList(and));
+    return AndPredicate.create(Arrays.asList(and));
   }
 
   public FilePredicate and(FilePredicate first, FilePredicate second) {
-    return new AndPredicate(Arrays.asList(first, second));
+    return AndPredicate.create(Arrays.asList(first, second));
   }
 }
index 55c5303cbaeea07cd9a6ef345f7b3d0c5289dcc6..a45fc16a0089fa7b9dbfb966c01c95bd91ec2193 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.api.batch.fs.internal;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.FilePredicates;
 import org.sonar.api.batch.fs.FileSystem;
@@ -34,7 +36,6 @@ import javax.annotation.Nullable;
 import java.io.File;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -49,25 +50,23 @@ public class DefaultFileSystem implements FileSystem {
 
   private final Cache cache;
   private final SortedSet<String> languages = new TreeSet<String>();
-  private File baseDir, workDir;
+  private File baseDir;
+  private File workDir;
   private Charset encoding;
-  private final FilePredicates predicates = new DefaultFilePredicates();
+  private final FilePredicates predicates;
 
   /**
    * Only for testing
    */
-  public DefaultFileSystem() {
-    this.cache = new MapCache();
+  public DefaultFileSystem(File baseDir) {
+    this(baseDir, new MapCache());
   }
 
-  protected DefaultFileSystem(Cache cache) {
+  protected DefaultFileSystem(File baseDir, Cache cache) {
+    Preconditions.checkNotNull(baseDir, "Base directory can't be null");
+    this.baseDir = baseDir.getAbsoluteFile();
     this.cache = cache;
-  }
-
-  public DefaultFileSystem setBaseDir(File d) {
-    Preconditions.checkNotNull(d, "Base directory can't be null");
-    this.baseDir = d.getAbsoluteFile();
-    return this;
+    this.predicates = new DefaultFilePredicates(baseDir);
   }
 
   @Override
@@ -75,6 +74,10 @@ public class DefaultFileSystem implements FileSystem {
     return baseDir;
   }
 
+  public void setBaseDir(File baseDir) {
+    this.baseDir = baseDir;
+  }
+
   public DefaultFileSystem setEncoding(@Nullable Charset e) {
     this.encoding = e;
     return this;
@@ -101,10 +104,6 @@ public class DefaultFileSystem implements FileSystem {
 
   @Override
   public InputFile inputFile(FilePredicate predicate) {
-    doPreloadFiles();
-    if (predicate instanceof RelativePathPredicate) {
-      return cache.inputFile((RelativePathPredicate) predicate);
-    }
     Iterable<InputFile> files = inputFiles(predicate);
     Iterator<InputFile> iterator = files.iterator();
     if (!iterator.hasNext()) {
@@ -132,30 +131,23 @@ public class DefaultFileSystem implements FileSystem {
   @Override
   public Iterable<InputFile> inputFiles(FilePredicate predicate) {
     doPreloadFiles();
-    return filter(cache.inputFiles(), predicate);
+    return OptimizedFilePredicateAdapter.create(predicate).get(cache);
   }
 
   @Override
   public boolean hasFiles(FilePredicate predicate) {
-    doPreloadFiles();
-    for (InputFile element : cache.inputFiles()) {
-      if (predicate.apply(element)) {
-        return true;
-      }
-    }
-    return false;
+    return inputFiles(predicate).iterator().hasNext();
   }
 
   @Override
   public Iterable<File> files(FilePredicate predicate) {
     doPreloadFiles();
-    Collection<File> result = new ArrayList<File>();
-    for (InputFile element : inputFiles(predicate)) {
-      if (predicate.apply(element)) {
-        result.add(element.file());
+    return Iterables.transform(inputFiles(predicate), new Function<InputFile, File>() {
+      @Override
+      public File apply(InputFile input) {
+        return input.file();
       }
-    }
-    return result;
+    });
   }
 
   @Override
@@ -168,16 +160,6 @@ public class DefaultFileSystem implements FileSystem {
     return cache.inputDir(relativePath);
   }
 
-  public static Collection<InputFile> filter(Iterable<InputFile> target, FilePredicate predicate) {
-    Collection<InputFile> result = new ArrayList<InputFile>();
-    for (InputFile element : target) {
-      if (predicate.apply(element)) {
-        result.add(element);
-      }
-    }
-    return result;
-  }
-
   /**
    * Adds InputFile to the list and registers its language, if present.
    */
@@ -225,14 +207,17 @@ public class DefaultFileSystem implements FileSystem {
     // nothing to do by default
   }
 
-  public abstract static class Cache {
-    protected abstract Iterable<InputFile> inputFiles();
+  public abstract static class Cache implements Index {
+    @Override
+    public abstract Iterable<InputFile> inputFiles();
 
+    @Override
     @CheckForNull
-    protected abstract InputFile inputFile(RelativePathPredicate predicate);
+    public abstract InputFile inputFile(String relativePath);
 
+    @Override
     @CheckForNull
-    protected abstract InputDir inputDir(String relativePath);
+    public abstract InputDir inputDir(String relativePath);
 
     protected abstract void doAdd(InputFile inputFile);
 
@@ -261,12 +246,12 @@ public class DefaultFileSystem implements FileSystem {
     }
 
     @Override
-    public InputFile inputFile(RelativePathPredicate predicate) {
-      return fileMap.get(predicate.path());
+    public InputFile inputFile(String relativePath) {
+      return fileMap.get(relativePath);
     }
 
     @Override
-    protected InputDir inputDir(String relativePath) {
+    public InputDir inputDir(String relativePath) {
       return dirMap.get(relativePath);
     }
 
index b092f848946d41ee36aced4164a22fb89e04b9bb..bab5060a9b8b696209c2becca15bae32fe7672c6 100644 (file)
 package org.sonar.api.batch.fs.internal;
 
 import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
 import org.sonar.api.batch.fs.InputFile;
 
-class FalsePredicate implements FilePredicate {
+import java.util.Collections;
+
+class FalsePredicate extends AbstractFilePredicate {
 
   static final FilePredicate FALSE = new FalsePredicate();
 
@@ -30,4 +33,14 @@ class FalsePredicate implements FilePredicate {
   public boolean apply(InputFile inputFile) {
     return false;
   }
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return Collections.emptyList();
+  }
 }
index 77d2f74887a64add394650eec696a5774f436b60..0418629235b2db25a8978622c1828e8f14f3359a 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
 
 /**
  * @since 4.2
  */
-class LanguagePredicate implements FilePredicate {
+class LanguagePredicate extends AbstractFilePredicate {
   private final String language;
 
   LanguagePredicate(String language) {
index 5f2359ee166c4789ed21353b37ef66bdbd4229c0..15b6aa3238fcf04b38a4c208c0f815efea2eb0c3 100644 (file)
@@ -25,7 +25,7 @@ import org.sonar.api.batch.fs.InputFile;
 /**
  * @since 4.2
  */
-class NotPredicate implements FilePredicate {
+class NotPredicate extends AbstractFilePredicate {
 
   private final FilePredicate predicate;
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicate.java
new file mode 100644 (file)
index 0000000..99ba698
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * Optimized version of FilePredicate allowing to speed up query by looking at InputFile by index.
+ */
+public interface OptimizedFilePredicate extends FilePredicate, Comparable<OptimizedFilePredicate> {
+
+  /**
+   * Filter provided files to keep only the ones that are valid for this predicate
+   */
+  Iterable<InputFile> filter(Iterable<InputFile> inputFiles);
+
+  /**
+   * Get all files that are valid for this predicate.
+   */
+  Iterable<InputFile> get(FileSystem.Index index);
+
+  /**
+   * For optimization. FilePredicates will be applied in priority order. For example when doing
+   * p.and(p1, p2, p3) then p1, p2 and p3 will be applied according to their priority value. Higher priority value
+   * are applied first.
+   * Assign a high priority when the predicate will likely highly reduce the set of InputFiles to filter. Also
+   * {@link RelativePathPredicate} and AbsolutePathPredicate have a high priority since they are using cache index.
+   */
+  int priority();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/OptimizedFilePredicateAdapter.java
new file mode 100644 (file)
index 0000000..81eec77
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.InputFile;
+
+class OptimizedFilePredicateAdapter extends AbstractFilePredicate {
+
+  private FilePredicate unoptimizedPredicate;
+
+  private OptimizedFilePredicateAdapter(FilePredicate unoptimizedPredicate) {
+    this.unoptimizedPredicate = unoptimizedPredicate;
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return unoptimizedPredicate.apply(inputFile);
+  }
+
+  public static OptimizedFilePredicate create(FilePredicate predicate) {
+    if (predicate instanceof OptimizedFilePredicate) {
+      return (OptimizedFilePredicate) predicate;
+    } else {
+      return new OptimizedFilePredicateAdapter(predicate);
+    }
+  }
+
+}
index a887c631cf2aeb7078c106daa14859447a71df5f..b960fc2cec4bce3b0ba2dadab587f47fad4f0415 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collection;
 
 /**
  * @since 4.2
  */
-class OrPredicate implements FilePredicate {
+class OrPredicate extends AbstractFilePredicate {
 
-  private final Collection<FilePredicate> predicates;
+  private final Collection<FilePredicate> predicates = new ArrayList<FilePredicate>();
 
-  OrPredicate(Collection<FilePredicate> predicates) {
+  private OrPredicate() {
+  }
+
+  public static FilePredicate create(Collection<FilePredicate> predicates) {
     if (predicates.isEmpty()) {
-      this.predicates = Arrays.asList(TruePredicate.TRUE);
-    } else {
-      this.predicates = predicates;
+      return TruePredicate.TRUE;
+    }
+    OrPredicate result = new OrPredicate();
+    for (FilePredicate filePredicate : predicates) {
+      if (filePredicate == TruePredicate.TRUE) {
+        return TruePredicate.TRUE;
+      } else if (filePredicate == FalsePredicate.FALSE) {
+        continue;
+      } else if (filePredicate instanceof OrPredicate) {
+        result.predicates.addAll(((OrPredicate) filePredicate).predicates);
+      } else {
+        result.predicates.add(filePredicate);
+      }
     }
+    return result;
   }
 
   @Override
@@ -50,4 +65,9 @@ class OrPredicate implements FilePredicate {
     return false;
   }
 
+  @VisibleForTesting
+  Collection<FilePredicate> predicates() {
+    return predicates;
+  }
+
 }
index 0cf614bcb09875a1fdd055c1f6c233f9f20e6449..9ccf48bffa52660a959b7886aed7d20af48fff85 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
 
 /**
  * @since 4.2
  */
-class PathPatternPredicate implements FilePredicate {
+class PathPatternPredicate extends AbstractFilePredicate {
 
   private final PathPattern pattern;
 
index 3763bcdd223acc113241ae57cbcf1cb2f73c5cf5..66c25246111d20269686b57d1f8556b191dab8e8 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.utils.PathUtils;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 /**
  * @since 4.2
  */
-public class RelativePathPredicate implements FilePredicate {
+public class RelativePathPredicate extends AbstractFilePredicate {
 
   private final String path;
 
@@ -43,4 +46,15 @@ public class RelativePathPredicate implements FilePredicate {
     return path.equals(f.relativePath());
   }
 
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    InputFile f = index.inputFile(this.path);
+    return f != null ? Arrays.asList(f) : Collections.<InputFile>emptyList();
+  }
+
+  @Override
+  public int priority() {
+    return USE_INDEX;
+  }
+
 }
index fe7c934071ecf92d7a7393d4e4420d0838f0bf3a..449df0418d6f3bc619c2764a0e8c0237e1888ee8 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
 
 /**
  * @since 4.2
  */
-class StatusPredicate implements FilePredicate {
+class StatusPredicate extends AbstractFilePredicate {
 
   private final InputFile.Status status;
 
index ec2aebd624d28e324f55b0c12171dba8a5668c25..6457aee090ff29e5d3c8e31c57fddb166802f6aa 100644 (file)
 package org.sonar.api.batch.fs.internal;
 
 import org.sonar.api.batch.fs.FilePredicate;
+import org.sonar.api.batch.fs.FileSystem.Index;
 import org.sonar.api.batch.fs.InputFile;
 
-class TruePredicate implements FilePredicate {
+class TruePredicate extends AbstractFilePredicate {
 
   static final FilePredicate TRUE = new TruePredicate();
 
@@ -30,4 +31,14 @@ class TruePredicate implements FilePredicate {
   public boolean apply(InputFile inputFile) {
     return true;
   }
+
+  @Override
+  public Iterable<InputFile> get(Index index) {
+    return index.inputFiles();
+  }
+
+  @Override
+  public Iterable<InputFile> filter(Iterable<InputFile> target) {
+    return target;
+  }
 }
index ac8b6d5fe8d94a19e8df00624e1fdb358ef70e0d..e5e266d7d233235dfaa195d2054c759fa724e0cf 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
-import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.InputFile;
 
 /**
  * @since 4.2
  */
-class TypePredicate implements FilePredicate {
+class TypePredicate extends AbstractFilePredicate {
 
   private final InputFile.Type type;
 
@@ -39,4 +38,3 @@ class TypePredicate implements FilePredicate {
   }
 
 }
-
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/AndPredicateTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/AndPredicateTest.java
new file mode 100644 (file)
index 0000000..12a1529
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.junit.Test;
+import org.sonar.api.batch.fs.FilePredicate;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class AndPredicateTest {
+
+  @Test
+  public void flattenNestedAnd() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    PathPatternPredicate pathPatternPredicate3 = new PathPatternPredicate(PathPattern.create("foo3/**"));
+    FilePredicate andPredicate = AndPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      AndPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate2, pathPatternPredicate3))));
+    assertThat(((AndPredicate) andPredicate).predicates()).containsOnly(pathPatternPredicate1, pathPatternPredicate2, pathPatternPredicate3);
+  }
+
+  @Test
+  public void sortPredicatesByPriority() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    RelativePathPredicate relativePathPredicate = new RelativePathPredicate("foo");
+    FilePredicate andPredicate = AndPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      relativePathPredicate, pathPatternPredicate2));
+    assertThat(((AndPredicate) andPredicate).predicates()).containsOnly(relativePathPredicate, pathPatternPredicate1, pathPatternPredicate2);
+  }
+
+  @Test
+  public void simplifyAndExpressionsWhenEmpty() {
+    FilePredicate andPredicate = AndPredicate.create(Arrays.<FilePredicate>asList());
+    assertThat(andPredicate).isEqualTo(TruePredicate.TRUE);
+  }
+
+  @Test
+  public void simplifyAndExpressionsWhenTrue() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    FilePredicate andPredicate = AndPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      TruePredicate.TRUE, pathPatternPredicate2));
+    assertThat(((AndPredicate) andPredicate).predicates()).containsOnly(pathPatternPredicate1, pathPatternPredicate2);
+  }
+
+  @Test
+  public void simplifyAndExpressionsWhenFalse() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    FilePredicate andPredicate = AndPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      FalsePredicate.FALSE, pathPatternPredicate2));
+    assertThat(andPredicate).isEqualTo(FalsePredicate.FALSE);
+  }
+
+}
index 95888fecfe99e76f2f0dcdec6b057cbbef4296e7..9c78f46024fdefa90d585f55ac6bbcafe459e0ff 100644 (file)
@@ -41,10 +41,11 @@ public class DefaultFilePredicatesTest {
   public TemporaryFolder temp = new TemporaryFolder();
 
   DefaultInputFile javaFile;
-  FilePredicates predicates = new DefaultFilePredicates();
+  FilePredicates predicates;
 
   @Before
   public void before() throws IOException {
+    predicates = new DefaultFilePredicates(temp.newFolder());
     javaFile = new DefaultInputFile("src/main/java/struts/Action.java")
       .setFile(temp.newFile("Action.java"))
       .setLanguage("java")
index 678fdc5687b25f8a6df179aec386d20ee4686947..ea810f8e46340682539d49a39db181f6330706f2 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.batch.fs.internal;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -37,12 +38,18 @@ public class DefaultFileSystemTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  private DefaultFileSystem fs;
+
+  private File basedir;
+
+  @Before
+  public void prepare() throws Exception {
+    basedir = temp.newFolder();
+    fs = new DefaultFileSystem(basedir);
+  }
+
   @Test
   public void test_directories() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
-
-    File basedir = temp.newFolder();
-    fs.setBaseDir(basedir);
     assertThat(fs.baseDir()).isAbsolute().isDirectory().exists();
     assertThat(fs.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath());
 
@@ -54,8 +61,6 @@ public class DefaultFileSystemTest {
 
   @Test
   public void test_encoding() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
-
     assertThat(fs.isDefaultJvmEncoding()).isTrue();
     assertThat(fs.encoding()).isEqualTo(Charset.defaultCharset());
 
@@ -66,8 +71,6 @@ public class DefaultFileSystemTest {
 
   @Test
   public void add_languages() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
-
     assertThat(fs.languages()).isEmpty();
 
     fs.addLanguages("java", "php", "cobol");
@@ -76,8 +79,6 @@ public class DefaultFileSystemTest {
 
   @Test
   public void files() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
-
     assertThat(fs.inputFiles(fs.predicates().all())).isEmpty();
 
     fs.add(new DefaultInputFile("src/Foo.php").setLanguage("php").setFile(temp.newFile()));
@@ -108,7 +109,6 @@ public class DefaultFileSystemTest {
 
   @Test
   public void input_file_returns_null_if_file_not_found() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
     assertThat(fs.inputFile(fs.predicates().hasRelativePath("src/Bar.java"))).isNull();
     assertThat(fs.inputFile(fs.predicates().hasLanguage("cobol"))).isNull();
   }
@@ -118,7 +118,6 @@ public class DefaultFileSystemTest {
     thrown.expect(IllegalArgumentException.class);
     thrown.expectMessage("expected one element");
 
-    DefaultFileSystem fs = new DefaultFileSystem();
     fs.add(new DefaultInputFile("src/Bar.java").setLanguage("java").setFile(temp.newFile()));
     fs.add(new DefaultInputFile("src/Baz.java").setLanguage("java").setFile(temp.newFile()));
 
@@ -127,7 +126,6 @@ public class DefaultFileSystemTest {
 
   @Test
   public void input_file_supports_non_indexed_predicates() throws Exception {
-    DefaultFileSystem fs = new DefaultFileSystem();
     fs.add(new DefaultInputFile("src/Bar.java").setLanguage("java").setFile(temp.newFile()));
 
     // it would fail if more than one java file
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/OrPredicateTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/OrPredicateTest.java
new file mode 100644 (file)
index 0000000..ef72585
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.junit.Test;
+import org.sonar.api.batch.fs.FilePredicate;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class OrPredicateTest {
+
+  @Test
+  public void flattenNestedOr() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    PathPatternPredicate pathPatternPredicate3 = new PathPatternPredicate(PathPattern.create("foo3/**"));
+    FilePredicate orPredicate = OrPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      OrPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate2, pathPatternPredicate3))));
+    assertThat(((OrPredicate) orPredicate).predicates()).containsOnly(pathPatternPredicate1, pathPatternPredicate2, pathPatternPredicate3);
+  }
+
+  @Test
+  public void simplifyOrExpressionsWhenEmpty() {
+    FilePredicate orPredicate = OrPredicate.create(Arrays.<FilePredicate>asList());
+    assertThat(orPredicate).isEqualTo(TruePredicate.TRUE);
+  }
+
+  @Test
+  public void simplifyOrExpressionsWhenFalse() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    FilePredicate andPredicate = OrPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      FalsePredicate.FALSE, pathPatternPredicate2));
+    assertThat(((OrPredicate) andPredicate).predicates()).containsOnly(pathPatternPredicate1, pathPatternPredicate2);
+  }
+
+  @Test
+  public void simplifyAndExpressionsWhenTrue() {
+    PathPatternPredicate pathPatternPredicate1 = new PathPatternPredicate(PathPattern.create("foo1/**"));
+    PathPatternPredicate pathPatternPredicate2 = new PathPatternPredicate(PathPattern.create("foo2/**"));
+    FilePredicate andPredicate = OrPredicate.create(Arrays.<FilePredicate>asList(pathPatternPredicate1,
+      TruePredicate.TRUE, pathPatternPredicate2));
+    assertThat(andPredicate).isEqualTo(TruePredicate.TRUE);
+  }
+
+}