]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8832 Support project-level sensors (#1720)
authorJanos Gyerik <janos.gyerik@sonarsource.com>
Thu, 2 Mar 2017 16:32:20 +0000 (17:32 +0100)
committerGitHub <noreply@github.com>
Thu, 2 Mar 2017 16:32:20 +0000 (17:32 +0100)
26 files changed:
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/TestInputFileBuilder.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFileSystemTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/deprecated/DeprecatedCpdBlockIndexerSensor.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorStrategy.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorWrapper.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/source/CodeColorizerSensor.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnaryTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/postjob/DefaultPostJobContextTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/SourcePublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/QProfileVerifierTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ModuleIndexerTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputComponentStoreTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java

index b3f160872d454d7e297562f1749e6f2c644f77e5..dda12ece0fdb46a78c178ed3384ce6b24be397c9 100644 (file)
@@ -28,7 +28,6 @@ import java.nio.charset.Charset;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -53,7 +52,6 @@ import org.sonar.api.utils.PathUtils;
 public class DefaultFileSystem implements FileSystem {
 
   private final Cache cache;
-  private final SortedSet<String> languages = new TreeSet<>();
   private final Path baseDir;
   private Path workDir;
   private Charset encoding;
@@ -192,10 +190,6 @@ public class DefaultFileSystem implements FileSystem {
 
   public DefaultFileSystem add(InputFile inputFile) {
     cache.add(inputFile);
-    String language = inputFile.language();
-    if (language != null) {
-      languages.add(language);
-    }
     return this;
   }
 
@@ -204,20 +198,10 @@ public class DefaultFileSystem implements FileSystem {
     return this;
   }
 
-  /**
-   * Adds a language to the list. To be used only for unit tests that need to use {@link #languages()} without
-   * using {@link #add(InputFile)}.
-   */
-  public DefaultFileSystem addLanguages(String language, String... others) {
-    languages.add(language);
-    Collections.addAll(languages, others);
-    return this;
-  }
-
   @Override
   public SortedSet<String> languages() {
     doPreloadFiles();
-    return languages;
+    return cache.languages();
   }
 
   @Override
@@ -245,6 +229,8 @@ public class DefaultFileSystem implements FileSystem {
     public void add(InputDir inputDir) {
       doAdd(inputDir);
     }
+
+    protected abstract SortedSet<String> languages();
   }
 
   /**
@@ -255,6 +241,7 @@ public class DefaultFileSystem implements FileSystem {
     private final Map<String, InputDir> dirMap = new HashMap<>();
     private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
     private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
+    private SortedSet<String> languages = new TreeSet<>();
 
     @Override
     public Iterable<InputFile> inputFiles() {
@@ -283,6 +270,9 @@ public class DefaultFileSystem implements FileSystem {
 
     @Override
     protected void doAdd(InputFile inputFile) {
+      if (inputFile.language() != null) {
+        languages.add(inputFile.language());
+      }
       fileMap.put(inputFile.relativePath(), inputFile);
       filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
       filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
@@ -292,6 +282,11 @@ public class DefaultFileSystem implements FileSystem {
     protected void doAdd(InputDir inputDir) {
       dirMap.put(inputDir.relativePath(), inputDir);
     }
+
+    @Override
+    protected SortedSet<String> languages() {
+      return languages;
+    }
   }
 
   @Override
index 48f0f871853057ed1968a6065f282f6992997765..6f803e1a8f03ef28a4d05480b8a8577f9221838f 100644 (file)
  */
 package org.sonar.api.batch.fs.internal;
 
+import java.io.File;
 import java.io.StringReader;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import javax.annotation.Nullable;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.utils.PathUtils;
 
@@ -143,4 +145,10 @@ public class TestInputFileBuilder {
     inputFile.setPublish(publish);
     return inputFile;
   }
+
+  public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) {
+    ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey);
+    definition.setBaseDir(baseDir);
+    return new DefaultInputModule(definition, TestInputFileBuilder.nextBatchId());
+  }
 }
index 373d40aaf1cf1ce0a0029a1673a26a2461f0e376..5239c783363033bec8036b04d2c79d83f27b1e3a 100644 (file)
@@ -79,4 +79,9 @@ public interface SensorDescriptor {
    */
   SensorDescriptor requireProperties(String... propertyKeys);
 
+  /**
+   * This sensor should be executed at the project level, instead of per-module.
+   * @since 6.4
+   */
+  SensorDescriptor global();
 }
index 56dc26847fa51e3e452c22360166c6f59a0a31e5..59cb196c2c04f4cfbe405beba5d194f678450411 100644 (file)
@@ -32,6 +32,7 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
   private InputFile.Type type = null;
   private String[] ruleRepositories = new String[0];
   private String[] properties = new String[0];
+  private boolean global = false;
 
   public String name() {
     return name;
@@ -54,6 +55,10 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
     return Arrays.asList(properties);
   }
 
+  public boolean isGlobal() {
+    return global;
+  }
+
   @Override
   public DefaultSensorDescriptor name(String name) {
     this.name = name;
@@ -99,4 +104,10 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
     return this;
   }
 
+  @Override
+  public SensorDescriptor global() {
+    this.global = true;
+    return this;
+  }
+
 }
index 8e7a286e529163c02cb498c954f3a00ac5bcdd50..1fcc28e032222e046568efd8ab4bd704ab67c178 100644 (file)
@@ -74,8 +74,10 @@ public class DefaultFileSystemTest {
   public void add_languages() {
     assertThat(fs.languages()).isEmpty();
 
-    fs.addLanguages("java", "php", "cobol");
-    assertThat(fs.languages()).containsOnly("cobol", "java", "php");
+    fs.add(new TestInputFileBuilder("foo", "src/Foo.php").setLanguage("php").build());
+    fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build());
+
+    assertThat(fs.languages()).containsOnly("java", "php");
   }
 
   @Test
index b68cedda7ef89e92e0566edab0e21e008b1f5947..705b3cc4560bd35582673556f200d48675f15910 100644 (file)
@@ -26,6 +26,7 @@ import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -78,6 +79,25 @@ public class ScannerExtensionDictionnary {
     return result;
   }
 
+  public Collection<org.sonar.api.batch.Sensor> selectSensors(@Nullable DefaultInputModule module, boolean global) {
+    List<org.sonar.api.batch.Sensor> result = getFilteredExtensions(org.sonar.api.batch.Sensor.class, module, null);
+
+    Iterator<org.sonar.api.batch.Sensor> iterator = result.iterator();
+    while (iterator.hasNext()) {
+      org.sonar.api.batch.Sensor sensor = iterator.next();
+      if (sensor instanceof SensorWrapper) {
+        if (global != ((SensorWrapper) sensor).isGlobal()) {
+          iterator.remove();
+        }
+      } else if (global) {
+        // only old sensors are not wrapped, and old sensors are never global -> exclude
+        iterator.remove();
+      }
+    }
+
+    return sort(result);
+  }
+
   private static Phase.Name evaluatePhase(Object extension) {
     Object extensionToEvaluate;
     if (extension instanceof SensorWrapper) {
index 3b0261853023ba5c269c09a81bb5698027009556..b8721d3616744353998ee5ef364365a3dc1a4963 100644 (file)
@@ -52,7 +52,8 @@ public class DeprecatedCpdBlockIndexerSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("CPD Block Indexer");
+    descriptor.name("CPD Block Indexer")
+      .global();
   }
 
   @VisibleForTesting
@@ -65,7 +66,6 @@ public class DeprecatedCpdBlockIndexerSensor implements Sensor {
 
   @Override
   public void execute(SensorContext context) {
-
     for (String language : fs.languages()) {
       CpdBlockIndexer blockIndexer = getBlockIndexer(language);
       if (!blockIndexer.isLanguageSupported(language)) {
index 73cc0c2072afe4aa46d233bf6708b715f058f4ec..c4b1805d3baad299218a81c722b382837e870d1b 100644 (file)
@@ -20,6 +20,9 @@
 package org.sonar.scanner.phases;
 
 import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SensorContext;
@@ -27,29 +30,56 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.resources.Project;
 import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
 import org.sonar.scanner.events.EventBus;
-import java.util.Collection;
+import org.sonar.scanner.sensor.SensorStrategy;
 
 @ScannerSide
 public class SensorsExecutor {
-  private final EventBus eventBus;
-  private final DefaultInputModule module;
   private final ScannerExtensionDictionnary selector;
+  private final DefaultInputModule module;
+  private final EventBus eventBus;
+  private final SensorStrategy strategy;
+  private final boolean isRoot;
 
-  public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus) {
+  public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus, SensorStrategy strategy) {
     this.selector = selector;
-    this.eventBus = eventBus;
     this.module = module;
+    this.eventBus = eventBus;
+    this.strategy = strategy;
+    this.isRoot = module.definition().getParent() == null;
   }
 
   public void execute(SensorContext context) {
-    Collection<Sensor> sensors = selector.select(Sensor.class, module, true, null);
-    eventBus.fireEvent(new SensorsPhaseEvent(Lists.newArrayList(sensors), true));
+    Collection<Sensor> perModuleSensors = selector.selectSensors(module, false);
+    Collection<Sensor> globalSensors;
+    if (isRoot) {
+      boolean orig = strategy.isGlobal();
+      strategy.setGlobal(true);
+      globalSensors = selector.selectSensors(module, true);
+      strategy.setGlobal(orig);
+    } else {
+      globalSensors = Collections.emptyList();
+    }
+
+    Collection<Sensor> allSensors = new ArrayList<>(perModuleSensors);
+    allSensors.addAll(globalSensors);
+    eventBus.fireEvent(new SensorsPhaseEvent(Lists.newArrayList(allSensors), true));
+
+    execute(context, perModuleSensors);
+
+    if (isRoot) {
+      boolean orig = strategy.isGlobal();
+      strategy.setGlobal(true);
+      execute(context, globalSensors);
+      strategy.setGlobal(orig);
+    }
+
+    eventBus.fireEvent(new SensorsPhaseEvent(Lists.newArrayList(allSensors), false));
+  }
 
+  private void execute(SensorContext context, Collection<Sensor> sensors) {
     for (Sensor sensor : sensors) {
       executeSensor(context, sensor);
     }
-
-    eventBus.fireEvent(new SensorsPhaseEvent(Lists.newArrayList(sensors), false));
   }
 
   private void executeSensor(SensorContext context, Sensor sensor) {
index 3f46db556973f8f652c06486195206674f749538..9053ad07f89aea2294ff060a4289ecbb84bdbc54 100644 (file)
@@ -70,6 +70,7 @@ import org.sonar.scanner.scan.filesystem.StatusDetectionFactory;
 import org.sonar.scanner.scan.report.IssuesReports;
 import org.sonar.scanner.sensor.DefaultSensorStorage;
 import org.sonar.scanner.sensor.SensorOptimizer;
+import org.sonar.scanner.sensor.SensorStrategy;
 import org.sonar.scanner.sensor.coverage.CoverageExclusions;
 import org.sonar.scanner.source.HighlightableBuilder;
 import org.sonar.scanner.source.SymbolizableBuilder;
@@ -137,6 +138,8 @@ public class ModuleScanContainer extends ComponentContainer {
       IssueFilters.class,
       CoverageExclusions.class,
 
+      SensorStrategy.class,
+
       // rules
       new RulesProfileProvider(),
       CheckFactory.class,
index 332a976d76cf18ea105b5beedd853c3ca2c24c7f..e905184a0ed6e2d8ee738d2156b3a604f080aafa 100644 (file)
@@ -245,6 +245,7 @@ public class ProjectScanContainer extends ComponentContainer {
 
     LOG.debug("Start recursive analysis of project modules");
     scanRecursively(tree, tree.root());
+
     if (analysisMode.isMediumTest()) {
       getComponentByType(ScanTaskObservers.class).notifyEndOfScanTask();
     }
index 2b7d318815c4d4d226207e4e3299cf6f86424105..52c0167d100b2fc414ca7a661726d4c091c62e17 100644 (file)
  */
 package org.sonar.scanner.scan.filesystem;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Table;
+import com.google.common.collect.TreeBasedTable;
+import java.nio.file.Path;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-
+import java.util.SortedSet;
+import java.util.TreeSet;
 import javax.annotation.CheckForNull;
-
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.InputDir;
@@ -34,11 +39,10 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-
-import com.google.common.collect.Table;
-import com.google.common.collect.TreeBasedTable;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.FileExtensionPredicate;
 import org.sonar.api.batch.fs.internal.FilenamePredicate;
+import org.sonar.api.scan.filesystem.PathResolver;
 
 /**
  * Store of all files and dirs. This cache is shared amongst all project modules. Inclusion and
@@ -47,7 +51,12 @@ import org.sonar.api.batch.fs.internal.FilenamePredicate;
 @ScannerSide
 public class InputComponentStore {
 
+  private final PathResolver pathResolver;
+  private final SortedSet<String> globalLanguagesCache = new TreeSet<>();
+  private final Map<String, SortedSet<String>> languagesCache = new HashMap<>();
+  private final Map<String, InputFile> globalInputFileCache = new HashMap<>();
   private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create();
+  private final Map<String, InputDir> globalInputDirCache = new HashMap<>();
   private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create();
   private final Map<String, InputModule> inputModuleCache = new HashMap<>();
   private final Map<String, InputComponent> inputComponents = new HashMap<>();
@@ -55,6 +64,10 @@ public class InputComponentStore {
   private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
   private InputModule root;
 
+  public InputComponentStore(PathResolver pathResolver) {
+    this.pathResolver = pathResolver;
+  }
+
   public Collection<InputComponent> all() {
     return inputComponents.values();
   }
@@ -77,10 +90,6 @@ public class InputComponentStore {
     return inputComponents.get(key);
   }
 
-  public void setRoot(InputModule root) {
-    this.root = root;
-  }
-
   @CheckForNull
   public InputModule root() {
     return root;
@@ -114,38 +123,79 @@ public class InputComponentStore {
 
   public InputComponentStore put(InputFile inputFile) {
     DefaultInputFile file = (DefaultInputFile) inputFile;
+    addToLanguageCache(file);
     inputFileCache.put(file.moduleKey(), inputFile.relativePath(), inputFile);
+    globalInputFileCache.put(getProjectRelativePath(file), inputFile);
     inputComponents.put(inputFile.key(), inputFile);
     filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
     filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
     return this;
   }
 
+  private void addToLanguageCache(DefaultInputFile inputFile) {
+    String language = inputFile.language();
+    if (language != null) {
+      globalLanguagesCache.add(language);
+      languagesCache.computeIfAbsent(inputFile.moduleKey(), k -> new TreeSet<>()).add(language);
+    }
+  }
+
   public InputComponentStore put(InputDir inputDir) {
     DefaultInputDir dir = (DefaultInputDir) inputDir;
     inputDirCache.put(dir.moduleKey(), inputDir.relativePath(), inputDir);
+    globalInputDirCache.put(getProjectRelativePath(dir), inputDir);
     inputComponents.put(inputDir.key(), inputDir);
     return this;
   }
 
+  private String getProjectRelativePath(DefaultInputFile file) {
+    return pathResolver.relativePath(getProjectBaseDir(), file.path());
+  }
+
+  private String getProjectRelativePath(DefaultInputDir dir) {
+    return pathResolver.relativePath(getProjectBaseDir(), dir.path());
+  }
+
+  private Path getProjectBaseDir() {
+    return ((DefaultInputModule) root).definition().getBaseDir().toPath();
+  }
+
   @CheckForNull
   public InputFile getFile(String moduleKey, String relativePath) {
     return inputFileCache.get(moduleKey, relativePath);
   }
 
+  @CheckForNull
+  public InputFile getFile(String relativePath) {
+    return globalInputFileCache.get(relativePath);
+  }
+
   @CheckForNull
   public InputDir getDir(String moduleKey, String relativePath) {
     return inputDirCache.get(moduleKey, relativePath);
   }
 
+  @CheckForNull
+  public InputDir getDir(String relativePath) {
+    return globalInputDirCache.get(relativePath);
+  }
+
   @CheckForNull
   public InputModule getModule(String moduleKey) {
     return inputModuleCache.get(moduleKey);
   }
 
-  public void put(InputModule inputModule) {
+  public void put(DefaultInputModule inputModule) {
+    Preconditions.checkState(!inputComponents.containsKey(inputModule.key()), "Module '%s' already indexed", inputModule.key());
+    Preconditions.checkState(!inputModuleCache.containsKey(inputModule.key()), "Module '%s' already indexed", inputModule.key());
     inputComponents.put(inputModule.key(), inputModule);
     inputModuleCache.put(inputModule.key(), inputModule);
+    if (inputModule.definition().getParent() == null) {
+      if (root != null) {
+        throw new IllegalStateException("Root module already indexed: '" + root.key() + "', '" + inputModule.key() + "'");
+      }
+      root = inputModule;
+    }
   }
 
   public Iterable<InputFile> getFilesByName(String filename) {
@@ -155,4 +205,12 @@ public class InputComponentStore {
   public Iterable<InputFile> getFilesByExtension(String extension) {
     return filesByExtensionCache.get(extension);
   }
+
+  public SortedSet<String> getLanguages() {
+    return globalLanguagesCache;
+  }
+
+  public SortedSet<String> getLanguages(String moduleKey) {
+    return languagesCache.getOrDefault(moduleKey, Collections.emptySortedSet());
+  }
 }
index efc97addbf5052fe65f6a7c313ba08d725234906..cca6b49fb14a65e3ee460e4cdadb7651067b54db 100644 (file)
  */
 package org.sonar.scanner.scan.filesystem;
 
+import java.util.SortedSet;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.scanner.sensor.SensorStrategy;
 
 @ScannerSide
 public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
 
   private final String moduleKey;
   private final InputComponentStore inputComponentStore;
+  private final SensorStrategy strategy;
 
-  public ModuleInputComponentStore(InputModule module, InputComponentStore inputComponentStore) {
+  public ModuleInputComponentStore(InputModule module, InputComponentStore inputComponentStore, SensorStrategy strategy) {
     this.moduleKey = module.key();
     this.inputComponentStore = inputComponentStore;
+    this.strategy = strategy;
   }
 
   @Override
   public Iterable<InputFile> inputFiles() {
-    return inputComponentStore.filesByModule(moduleKey);
+    if (strategy.isGlobal()) {
+      return inputComponentStore.allFiles();
+    } else {
+      return inputComponentStore.filesByModule(moduleKey);
+    }
   }
 
   @Override
   public InputFile inputFile(String relativePath) {
-    return inputComponentStore.getFile(moduleKey, relativePath);
+    if (strategy.isGlobal()) {
+      return inputComponentStore.getFile(relativePath);
+    } else {
+      return inputComponentStore.getFile(moduleKey, relativePath);
+    }
   }
 
   @Override
   public InputDir inputDir(String relativePath) {
-    return inputComponentStore.getDir(moduleKey, relativePath);
+    if (strategy.isGlobal()) {
+      return inputComponentStore.getDir(relativePath);
+    } else {
+      return inputComponentStore.getDir(moduleKey, relativePath);
+    }
+  }
+
+  @Override
+  public SortedSet<String> languages() {
+    if (strategy.isGlobal()) {
+      return inputComponentStore.getLanguages();
+    } else {
+      return inputComponentStore.getLanguages(moduleKey);
+    }
   }
 
   @Override
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorStrategy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/SensorStrategy.java
new file mode 100644 (file)
index 0000000..ebdba34
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.sensor;
+
+/**
+ * A shared, mutable object in the module container.
+ * It's used during the execution of sensors to decide whether
+ * sensors should be executed once for the entire project, or per-module.
+ */
+public class SensorStrategy {
+
+  private boolean global = false;
+
+  public boolean isGlobal() {
+    return global;
+  }
+
+  public void setGlobal(boolean global) {
+    this.global = global;
+  }
+}
index ef3114a12462165e1481baaf2bda052cecff2214..6c71a8dc05d418dde0d7a21e8728dc5589cc12e0 100644 (file)
@@ -61,4 +61,8 @@ public class SensorWrapper implements org.sonar.api.batch.Sensor {
       return wrappedSensor.getClass().getName();
     }
   }
+
+  public boolean isGlobal() {
+    return descriptor.isGlobal();
+  }
 }
index b735d817214ed9a3f845b55812022c484dad322d..97a9f5f30d1ad8a43bfe53d15b5cc47186891493 100644 (file)
@@ -42,7 +42,8 @@ public final class CodeColorizerSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("Code Colorizer Sensor");
+    descriptor.name("Code Colorizer Sensor")
+      .global();
   }
 
   @Override
index 64663ba44538aae6542d8d3009779ffe9ed8d283..da5482356f487064aa5a9bfb017e89d0a2fc9224 100644 (file)
@@ -70,7 +70,8 @@ public final class ZeroCoverageSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("Zero Coverage Sensor");
+    descriptor.name("Zero Coverage Sensor")
+      .global();
   }
 
   @Override
index 0d3bc4db5c7965097ee8f0fb9af0b7c3e051a919..e805f490e2e8d8db3bb03a61f7121e4639249b1d 100644 (file)
@@ -36,13 +36,13 @@ import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.postjob.PostJobContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.api.resources.Project;
 import org.sonar.core.platform.ComponentContainer;
-import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
-import org.sonar.scanner.bootstrap.ExtensionMatcher;
 import org.sonar.scanner.postjob.PostJobOptimizer;
 import org.sonar.scanner.sensor.DefaultSensorContext;
 import org.sonar.scanner.sensor.SensorOptimizer;
+import org.sonar.scanner.sensor.SensorWrapper;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -301,10 +301,7 @@ public class ScannerExtensionDictionnaryTest {
     ScannerExtensionDictionnary selector = newSelector(analyze, post, pre);
     List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null));
 
-    assertThat(extensions).hasSize(3);
-    assertThat(extensions.get(0)).isEqualTo(pre);
-    assertThat(extensions.get(1)).isEqualTo(analyze);
-    assertThat(extensions.get(2)).isEqualTo(post);
+    assertThat(extensions).containsExactly(pre, analyze, post);
   }
 
   @Test
@@ -321,6 +318,30 @@ public class ScannerExtensionDictionnaryTest {
     assertThat(extensions.get(2)).isEqualTo(checker);
   }
 
+  @Test
+  public void querySensors() {
+    BatchExtension pre = new PreSensorSubclass();
+    BatchExtension analyze = new GeneratesSomething("something");
+    BatchExtension post = new PostSensorSubclass();
+
+    FakeSensor fakeSensor = new FakeSensor();
+    FakeNewSensor fakeNewSensor = new FakeNewSensor();
+    FakeNewGlobalSensor fakeNewGlobalSensor = new FakeNewGlobalSensor();
+    ScannerExtensionDictionnary selector = newSelector(fakeSensor, fakeNewSensor, fakeNewGlobalSensor);
+    List extensions = Lists.newArrayList(selector.selectSensors(null, false));
+
+    assertThat(extensions).hasSize(2);
+    assertThat(extensions).contains(fakeSensor);
+    extensions.remove(fakeSensor);
+    assertThat(extensions.get(0)).isInstanceOf(SensorWrapper.class);
+    assertThat(((SensorWrapper) extensions.get(0)).wrappedSensor()).isEqualTo(fakeNewSensor);
+
+    extensions = Lists.newArrayList(selector.selectSensors(null, true));
+
+    assertThat(extensions.get(0)).isInstanceOf(SensorWrapper.class);
+    assertThat(((SensorWrapper) extensions.get(0)).wrappedSensor()).isEqualTo(fakeNewGlobalSensor);
+  }
+
   class FakeSensor implements Sensor {
 
     public void analyse(Project project, SensorContext context) {
@@ -332,6 +353,31 @@ public class ScannerExtensionDictionnaryTest {
     }
   }
 
+  class FakeNewSensor implements org.sonar.api.batch.sensor.Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+    }
+
+    @Override
+    public void execute(org.sonar.api.batch.sensor.SensorContext context) {
+    }
+
+  }
+
+  class FakeNewGlobalSensor implements org.sonar.api.batch.sensor.Sensor {
+
+    @Override
+    public void describe(SensorDescriptor descriptor) {
+      descriptor.global();
+    }
+
+    @Override
+    public void execute(org.sonar.api.batch.sensor.SensorContext context) {
+    }
+
+  }
+
   class MethodDependentOf implements BatchExtension {
     private Object dep;
 
index 9259a79c87c58e88ad432b0fc7e51e6809856b6b..708f5ac97bc8fbba091a3954d341582570f478a3 100644 (file)
@@ -32,10 +32,10 @@ import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.config.MapSettings;
 import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.core.util.CloseableIterator;
@@ -86,11 +86,11 @@ public class CpdExecutorTest {
     publisher = mock(ReportPublisher.class);
     when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir));
     index = new SonarCpdBlockIndex(publisher, settings);
-    componentStore = new InputComponentStore();
+    componentStore = new InputComponentStore(new PathResolver());
     executor = new CpdExecutor(settings, index, publisher, componentStore);
     reader = new ScannerReportReader(outputDir);
 
-    componentStore.put(new DefaultInputModule("foo"));
+    componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", baseDir));
 
     batchComponent1 = createComponent("src/Foo.php", 5);
     batchComponent2 = createComponent("src/Foo2.php", 5);
index c29e5307b5484dac897f81b917202ceb6a570eb9..64a87b2295d508fa3215156c13c9354fc04bf4ac 100644 (file)
  */
 package org.sonar.scanner.postjob;
 
+import java.io.IOException;
 import java.util.Arrays;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.postjob.issue.PostJobIssue;
 import org.sonar.api.batch.rule.Severity;
-import org.sonar.api.config.Settings;
 import org.sonar.api.config.MapSettings;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.scanner.issue.IssueCache;
 import org.sonar.scanner.issue.tracking.TrackedIssue;
 import org.sonar.scanner.scan.filesystem.InputComponentStore;
@@ -38,6 +42,9 @@ import static org.mockito.Mockito.when;
 
 public class DefaultPostJobContextTest {
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
   private IssueCache issueCache;
   private InputComponentStore componentStore;
   private DefaultPostJobContext context;
@@ -47,14 +54,14 @@ public class DefaultPostJobContextTest {
   @Before
   public void prepare() {
     issueCache = mock(IssueCache.class);
-    componentStore = new InputComponentStore();
+    componentStore = new InputComponentStore(new PathResolver());
     settings = new MapSettings();
     analysisMode = mock(AnalysisMode.class);
     context = new DefaultPostJobContext(settings, issueCache, componentStore, analysisMode);
   }
 
   @Test
-  public void testIssues() {
+  public void testIssues() throws IOException {
     when(analysisMode.isIssues()).thenReturn(true);
 
     assertThat(context.settings()).isSameAs(settings);
@@ -78,7 +85,10 @@ public class DefaultPostJobContextTest {
     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
     assertThat(issue.inputComponent()).isNull();
 
-    componentStore.put(new TestInputFileBuilder("foo", "src/Foo.php").build());
+    String moduleKey = "foo";
+    componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", temp.newFolder()));
+
+    componentStore.put(new TestInputFileBuilder(moduleKey, "src/Foo.php").build());
     assertThat(issue.inputComponent()).isNotNull();
 
   }
index aabacd097998162d05ad1528075228f437f46e9a..5551ca8f67902657191e0eefa5550e5dca4b3414 100644 (file)
 package org.sonar.scanner.report;
 
 import java.io.File;
+import java.io.IOException;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage;
 import org.sonar.scanner.protocol.output.ScannerReportReader;
@@ -49,14 +50,14 @@ public class CoveragePublisherTest {
   private MeasureCache measureCache;
   private CoveragePublisher publisher;
 
-  private InputComponentStore componentCache;
   private DefaultInputFile inputFile;
 
   @Before
-  public void prepare() {
-    inputFile = new TestInputFileBuilder("foo", "src/Foo.php").setLines(5).build();
-    componentCache = new InputComponentStore();
-    componentCache.put(new DefaultInputModule("foo"));
+  public void prepare() throws IOException {
+    String moduleKey = "foo";
+    inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setLines(5).build();
+    InputComponentStore componentCache = new InputComponentStore(new PathResolver());
+    componentCache.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()));
     componentCache.put(inputFile);
 
     measureCache = mock(MeasureCache.class);
index 1efd6c69f43ce85cc58d91cdc52b398e9d390ab6..d3dfe4cc3e9274ba098a4b7db33e8a9232c39d5a 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.scanner.deprecated.test.TestPlanBuilder;
 import org.sonar.scanner.protocol.output.ScannerReport;
@@ -58,18 +59,18 @@ public class MeasuresPublisherTest {
   private MeasureCache measureCache;
   private MeasuresPublisher publisher;
 
-  private InputComponentStore componentCache;
   private File outputDir;
   private ScannerReportWriter writer;
   private DefaultInputFile inputFile;
-  private DefaultInputModule InputModule;
+  private DefaultInputModule inputModule;
 
   @Before
   public void prepare() throws IOException {
-    InputModule = new DefaultInputModule("foo");
-    inputFile = new TestInputFileBuilder("foo", "src/Foo.php").setPublish(true).build();
-    componentCache = new InputComponentStore();
-    componentCache.put(InputModule);
+    String moduleKey = "foo";
+    inputModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder());
+    inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setPublish(true).build();
+    InputComponentStore componentCache = new InputComponentStore(new PathResolver());
+    componentCache.put(inputModule);
     componentCache.put(inputFile);
     measureCache = mock(MeasureCache.class);
     when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList());
@@ -90,7 +91,7 @@ public class MeasuresPublisherTest {
     publisher.publish(writer);
     ScannerReportReader reader = new ScannerReportReader(outputDir);
 
-    assertThat(reader.readComponentMeasures(InputModule.batchId())).hasSize(0);
+    assertThat(reader.readComponentMeasures(inputModule.batchId())).hasSize(0);
     try (CloseableIterator<ScannerReport.Measure> componentMeasures = reader.readComponentMeasures(inputFile.batchId())) {
       assertThat(componentMeasures).hasSize(2);
     }
index 86c8fd74bbe11e0a2b213c83fe41e6fdb7ed2273..3624dcabf6503a6ea9017b81ffa1fa12e873cc6f 100644 (file)
@@ -28,10 +28,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.scanner.protocol.output.ScannerReportWriter;
-import org.sonar.scanner.report.SourcePublisher;
 import org.sonar.scanner.scan.filesystem.InputComponentStore;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -44,20 +43,22 @@ public class SourcePublisherTest {
   private File sourceFile;
   private ScannerReportWriter writer;
   private DefaultInputFile inputFile;
-  private InputComponentStore componentStore;
 
   @Before
   public void prepare() throws IOException {
     File baseDir = temp.newFolder();
     sourceFile = new File(baseDir, "src/Foo.php");
-    inputFile = new TestInputFileBuilder("foo", "src/Foo.php")
+    String moduleKey = "foo";
+    inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php")
       .setLines(5)
       .setModuleBaseDir(baseDir.toPath())
       .setCharset(StandardCharsets.ISO_8859_1)
       .build();
-    componentStore = new InputComponentStore();
-    componentStore.put(new DefaultInputModule("foo"));
+
+    InputComponentStore componentStore = new InputComponentStore(new PathResolver());
+    componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, baseDir));
     componentStore.put(inputFile);
+
     publisher = new SourcePublisher(componentStore);
     File outputDir = temp.newFolder();
     writer = new ScannerReportWriter(outputDir);
index 58dac067d4833bf94cecdde23a55bcf5333a9a7c..bfb438c3fb6abb51bf8063fcaec76f2623a9914f 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.slf4j.Logger;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.config.Settings;
 import org.sonar.api.config.MapSettings;
 import org.sonar.api.utils.MessageException;
@@ -58,7 +59,9 @@ public class QProfileVerifierTest {
 
   @Test
   public void should_log_all_used_profiles() {
-    fs.addLanguages("java", "cobol");
+    fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build());
+    fs.add(new TestInputFileBuilder("foo", "src/Baz.cbl").setLanguage("cobol").build());
+
     QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
     Logger logger = mock(Logger.class);
     profileLogger.execute(logger);
@@ -69,7 +72,8 @@ public class QProfileVerifierTest {
 
   @Test
   public void should_fail_if_default_profile_not_used() {
-    fs.addLanguages("java", "cobol");
+    fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build());
+
     settings.setProperty("sonar.profile", "Unknown");
 
     QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
@@ -92,7 +96,8 @@ public class QProfileVerifierTest {
 
   @Test
   public void should_not_fail_if_default_profile_used_at_least_once() {
-    fs.addLanguages("java", "cobol");
+    fs.add(new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build());
+
     settings.setProperty("sonar.profile", "My Java profile");
 
     QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
index 3ce99df7e753857bab1e03b66f162a06ba23670b..74faa55f153b85114a6430a1fc8173ed27a333fb 100644 (file)
  */
 package org.sonar.scanner.scan;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
 import org.sonar.scanner.scan.filesystem.InputComponentStore;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 public class ModuleIndexerTest {
   private ModuleIndexer indexer;
   private DefaultComponentTree tree;
@@ -40,7 +41,7 @@ public class ModuleIndexerTest {
   @Before
   public void setUp() {
     reactor = mock(ImmutableProjectReactor.class);
-    componentStore = new InputComponentStore();
+    componentStore = new InputComponentStore(new PathResolver());
     tree = new DefaultComponentTree();
     moduleHierarchy = new DefaultInputModuleHierarchy();
     indexer = new ModuleIndexer(reactor, tree, componentStore, new BatchIdGenerator(), moduleHierarchy);
index 9ec32b91523b2750e557433ae91463e4efd4ee7e..6b5a8b1ace12bbc4fbd6a3bbd94776219608a2bf 100644 (file)
  */
 package org.sonar.scanner.scan.filesystem;
 
-import static org.assertj.core.api.Assertions.assertThat;
-
+import java.io.File;
 import java.nio.charset.StandardCharsets;
 import java.util.LinkedList;
 import java.util.List;
-
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -33,7 +31,11 @@ import org.sonar.api.batch.fs.InputFile.Status;
 import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.scan.filesystem.PathResolver;
+
+import static org.assertj.core.api.Assertions.assertThat;
 
 public class InputComponentStoreTest {
   @Rule
@@ -41,13 +43,24 @@ public class InputComponentStoreTest {
 
   @Test
   public void should_add_input_file() throws Exception {
-    InputComponentStore cache = new InputComponentStore();
-    DefaultInputFile fooFile = new TestInputFileBuilder("struts", "src/main/java/Foo.java")
-      .setModuleBaseDir(temp.newFolder().toPath())
+    InputComponentStore cache = new InputComponentStore(new PathResolver());
+
+    String rootModuleKey = "struts";
+    File rootBaseDir = temp.newFolder();
+    DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootModuleKey, rootBaseDir);
+    cache.put(rootModule);
+
+    String subModuleKey = "struts-core";
+    DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(subModuleKey, temp.newFolder());
+    rootModule.definition().addSubProject(subModule.definition());
+    cache.put(subModule);
+
+    DefaultInputFile fooFile = new TestInputFileBuilder(rootModuleKey, "src/main/java/Foo.java")
+      .setModuleBaseDir(rootBaseDir.toPath())
       .setPublish(true)
       .build();
     cache.put(fooFile);
-    cache.put(new TestInputFileBuilder("struts-core", "src/main/java/Bar.java")
+    cache.put(new TestInputFileBuilder(subModuleKey, "src/main/java/Bar.java")
       .setLanguage("bla")
       .setPublish(false)
       .setType(Type.MAIN)
@@ -57,12 +70,12 @@ public class InputComponentStoreTest {
       .setModuleBaseDir(temp.newFolder().toPath())
       .build());
 
-    DefaultInputFile loadedFile = (DefaultInputFile) cache.getFile("struts-core", "src/main/java/Bar.java");
+    DefaultInputFile loadedFile = (DefaultInputFile) cache.getFile(subModuleKey, "src/main/java/Bar.java");
     assertThat(loadedFile.relativePath()).isEqualTo("src/main/java/Bar.java");
     assertThat(loadedFile.charset()).isEqualTo(StandardCharsets.UTF_8);
 
-    assertThat(cache.filesByModule("struts")).hasSize(1);
-    assertThat(cache.filesByModule("struts-core")).hasSize(1);
+    assertThat(cache.filesByModule(rootModuleKey)).hasSize(1);
+    assertThat(cache.filesByModule(subModuleKey)).hasSize(1);
     assertThat(cache.allFiles()).hasSize(2);
     for (InputPath inputPath : cache.allFiles()) {
       assertThat(inputPath.relativePath()).startsWith("src/main/java/");
@@ -75,9 +88,9 @@ public class InputComponentStoreTest {
     cache.remove(fooFile);
     assertThat(cache.allFiles()).hasSize(1);
 
-    cache.removeModule("struts");
-    assertThat(cache.filesByModule("struts")).hasSize(0);
-    assertThat(cache.filesByModule("struts-core")).hasSize(1);
+    cache.removeModule(rootModuleKey);
+    assertThat(cache.filesByModule(rootModuleKey)).hasSize(0);
+    assertThat(cache.filesByModule(subModuleKey)).hasSize(1);
     assertThat(cache.allFiles()).hasSize(1);
   }
 
index 228bf779a18b87c2ea6296f24f2c9e4bb621e852..eeb3b951fb14d1e3f29fa404ed098442da87696f 100644 (file)
 package org.sonar.scanner.scan.filesystem;
 
 import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.scanner.sensor.SensorStrategy;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class ModuleInputComponentStoreTest {
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  private InputComponentStore componentStore;
+
+  private final String moduleKey = "dummy key";
+
+  @Before
+  public void setUp() throws IOException {
+    componentStore = new InputComponentStore(new PathResolver());
+    componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()));
+  }
+
   @Test
   public void should_cache_files_by_filename() throws IOException {
-    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+    ModuleInputComponentStore store = newModuleInputComponentStore();
 
     String filename = "some name";
-    String moduleKey = "dummy key";
     InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/" + filename).build();
     store.doAdd(inputFile1);
 
@@ -49,9 +66,8 @@ public class ModuleInputComponentStoreTest {
 
   @Test
   public void should_cache_files_by_extension() throws IOException {
-    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+    ModuleInputComponentStore store = newModuleInputComponentStore();
 
-    String moduleKey = "dummy key";
     InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/Program.java").build();
     store.doAdd(inputFile1);
 
@@ -66,11 +82,11 @@ public class ModuleInputComponentStoreTest {
 
   @Test
   public void should_not_cache_duplicates() throws IOException {
-    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+    ModuleInputComponentStore store = newModuleInputComponentStore();
 
     String ext = "java";
     String filename = "Program." + ext;
-    InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+    InputFile inputFile = new TestInputFileBuilder(moduleKey, "some/path/" + filename).build();
     store.doAdd(inputFile);
     store.doAdd(inputFile);
     store.doAdd(inputFile);
@@ -81,14 +97,18 @@ public class ModuleInputComponentStoreTest {
 
   @Test
   public void should_get_empty_iterable_on_cache_miss() {
-    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+    ModuleInputComponentStore store = newModuleInputComponentStore();
 
     String ext = "java";
     String filename = "Program." + ext;
-    InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+    InputFile inputFile = new TestInputFileBuilder(moduleKey, "some/path/" + filename).build();
     store.doAdd(inputFile);
 
     assertThat(store.getFilesByName("nonexistent")).isEmpty();
     assertThat(store.getFilesByExtension("nonexistent")).isEmpty();
   }
+
+  private ModuleInputComponentStore newModuleInputComponentStore() {
+    return new ModuleInputComponentStore(mock(InputModule.class), componentStore, mock(SensorStrategy.class));
+  }
 }