Skip to content

Commit

Permalink
SONAR-5831 Provide the ability to explicitly ask the SCM sensor to re…
Browse files Browse the repository at this point in the history
…load all blame information
  • Loading branch information
henryju committed Jan 30, 2015
1 parent 8d9f68e commit c4ba2c0
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 31 deletions.
Expand Up @@ -51,15 +51,15 @@ public class BatchPluginRepository implements PluginRepository {
private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class); private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class);
private static final String CORE_PLUGIN = "core"; private static final String CORE_PLUGIN = "core";


private PluginsReferential pluginsReferential; private PluginsRepository pluginsReferential;
private Map<String, Plugin> pluginsByKey; private Map<String, Plugin> pluginsByKey;
private Map<String, PluginMetadata> metadataByKey; private Map<String, PluginMetadata> metadataByKey;
private Settings settings; private Settings settings;
private PluginClassloaders classLoaders; private PluginClassloaders classLoaders;
private final DefaultAnalysisMode analysisMode; private final DefaultAnalysisMode analysisMode;
private final BatchPluginJarInstaller pluginInstaller; private final BatchPluginJarInstaller pluginInstaller;


public BatchPluginRepository(PluginsReferential pluginsReferential, Settings settings, DefaultAnalysisMode analysisMode, public BatchPluginRepository(PluginsRepository pluginsReferential, Settings settings, DefaultAnalysisMode analysisMode,
BatchPluginJarInstaller pluginInstaller) { BatchPluginJarInstaller pluginInstaller) {
this.pluginsReferential = pluginsReferential; this.pluginsReferential = pluginsReferential;
this.settings = settings; this.settings = settings;
Expand Down
Expand Up @@ -37,9 +37,9 @@
import java.util.Map; import java.util.Map;


/** /**
* A {@link PluginsReferential} implementation that put downloaded plugins in a FS cache. * A {@link PluginsRepository} implementation that put downloaded plugins in a FS cache.
*/ */
public class DefaultPluginsReferential implements PluginsReferential { public class DefaultPluginsReferential implements PluginsRepository {


private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginsReferential.class); private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginsReferential.class);


Expand Down
Expand Up @@ -107,7 +107,7 @@ private void addBootstrapComponents() {
DefaultI18n.class, DefaultI18n.class,
new GlobalRepositoriesProvider(), new GlobalRepositoriesProvider(),
UserRepository.class); UserRepository.class);
if (getComponentByType(PluginsReferential.class) == null) { if (getComponentByType(PluginsRepository.class) == null) {
add(DefaultPluginsReferential.class); add(DefaultPluginsReferential.class);
} }
if (getComponentByType(GlobalRepositoriesLoader.class) == null) { if (getComponentByType(GlobalRepositoriesLoader.class) == null) {
Expand Down
Expand Up @@ -31,7 +31,7 @@
* Plugin referential. * Plugin referential.
* @since 4.4 * @since 4.4
*/ */
public interface PluginsReferential { public interface PluginsRepository {


/** /**
* Return list of remote plugins to be installed * Return list of remote plugins to be installed
Expand Down
Expand Up @@ -28,11 +28,12 @@
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.api.platform.PluginMetadata; import org.sonar.api.platform.PluginMetadata;
import org.sonar.batch.bootstrap.PluginsReferential; import org.sonar.batch.bootstrap.PluginsRepository;
import org.sonar.batch.bootstrap.TaskProperties; import org.sonar.batch.bootstrap.TaskProperties;
import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.protocol.input.ActiveRule; import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.batch.protocol.input.FileData;
import org.sonar.batch.protocol.input.GlobalRepositories; import org.sonar.batch.protocol.input.GlobalRepositories;
import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.protocol.input.ProjectRepositories;
import org.sonar.batch.protocol.input.issues.PreviousIssue; import org.sonar.batch.protocol.input.issues.PreviousIssue;
Expand Down Expand Up @@ -68,9 +69,9 @@ public static BatchMediumTesterBuilder builder() {
} }


public static class BatchMediumTesterBuilder { public static class BatchMediumTesterBuilder {
private final FakeGlobalReferentialsLoader globalRefProvider = new FakeGlobalReferentialsLoader(); private final FakeGlobalRepositoriesLoader globalRefProvider = new FakeGlobalRepositoriesLoader();
private final FakeProjectReferentialsLoader projectRefProvider = new FakeProjectReferentialsLoader(); private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader();
private final FakePluginsReferential pluginsReferential = new FakePluginsReferential(); private final FakePluginsRepository pluginsReferential = new FakePluginsRepository();
private final FakePreviousIssuesLoader previousIssues = new FakePreviousIssuesLoader(); private final FakePreviousIssuesLoader previousIssues = new FakePreviousIssuesLoader();
private final Map<String, String> bootstrapProperties = new HashMap<String, String>(); private final Map<String, String> bootstrapProperties = new HashMap<String, String>();


Expand Down Expand Up @@ -121,6 +122,11 @@ public BatchMediumTesterBuilder activateRule(ActiveRule activeRule) {
return this; return this;
} }


public BatchMediumTesterBuilder addFileData(String moduleKey, String path, FileData fileData) {
projectRefProvider.addFileData(moduleKey, path, fileData);
return this;
}

public BatchMediumTesterBuilder addPreviousIssue(PreviousIssue issue) { public BatchMediumTesterBuilder addPreviousIssue(PreviousIssue issue) {
previousIssues.getPreviousIssues().add(issue); previousIssues.getPreviousIssues().add(issue);
return this; return this;
Expand Down Expand Up @@ -180,9 +186,9 @@ public TaskBuilder(BatchMediumTester tester) {


public TaskResult start() { public TaskResult start() {
TaskResult result = new TaskResult(); TaskResult result = new TaskResult();
tester.batch.executeTask(taskProperties, Map<String, String> props = new HashMap<>();
result props.putAll(taskProperties);
); tester.batch.executeTask(props, result);
return result; return result;
} }


Expand All @@ -197,7 +203,7 @@ public TaskBuilder property(String key, String value) {
} }
} }


private static class FakeGlobalReferentialsLoader implements GlobalRepositoriesLoader { private static class FakeGlobalRepositoriesLoader implements GlobalRepositoriesLoader {


private int metricId = 1; private int metricId = 1;


Expand All @@ -212,7 +218,7 @@ public Map<String, String> globalSettings() {
return ref.globalSettings(); return ref.globalSettings();
} }


public FakeGlobalReferentialsLoader add(Metric metric) { public FakeGlobalRepositoriesLoader add(Metric metric) {
Boolean optimizedBestValue = metric.isOptimizedBestValue(); Boolean optimizedBestValue = metric.isOptimizedBestValue();
ref.metrics().add(new org.sonar.batch.protocol.input.Metric(metricId, ref.metrics().add(new org.sonar.batch.protocol.input.Metric(metricId,
metric.key(), metric.key(),
Expand All @@ -230,7 +236,7 @@ public FakeGlobalReferentialsLoader add(Metric metric) {
} }
} }


private static class FakeProjectReferentialsLoader implements ProjectRepositoriesLoader { private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader {


private ProjectRepositories ref = new ProjectRepositories(); private ProjectRepositories ref = new ProjectRepositories();


Expand All @@ -239,18 +245,24 @@ public ProjectRepositories load(ProjectReactor reactor, TaskProperties taskPrope
return ref; return ref;
} }


public FakeProjectReferentialsLoader addQProfile(String language, String name) { public FakeProjectRepositoriesLoader addQProfile(String language, String name) {
ref.addQProfile(new org.sonar.batch.protocol.input.QProfile(name, name, language, new Date())); ref.addQProfile(new org.sonar.batch.protocol.input.QProfile(name, name, language, new Date()));
return this; return this;
} }


public FakeProjectReferentialsLoader addActiveRule(ActiveRule activeRule) { public FakeProjectRepositoriesLoader addActiveRule(ActiveRule activeRule) {
ref.addActiveRule(activeRule); ref.addActiveRule(activeRule);
return this; return this;
} }

public FakeProjectRepositoriesLoader addFileData(String moduleKey, String path, FileData fileData) {
ref.addFileData(moduleKey, path, fileData);
return this;
}

} }


private static class FakePluginsReferential implements PluginsReferential { private static class FakePluginsRepository implements PluginsRepository {


private List<RemotePlugin> pluginList = new ArrayList<RemotePlugin>(); private List<RemotePlugin> pluginList = new ArrayList<RemotePlugin>();
private Map<RemotePlugin, File> pluginFiles = new HashMap<RemotePlugin, File>(); private Map<RemotePlugin, File> pluginFiles = new HashMap<RemotePlugin, File>();
Expand All @@ -266,14 +278,14 @@ public File pluginFile(RemotePlugin remote) {
return pluginFiles.get(remote); return pluginFiles.get(remote);
} }


public FakePluginsReferential addPlugin(String pluginKey, File location) { public FakePluginsRepository addPlugin(String pluginKey, File location) {
RemotePlugin plugin = new RemotePlugin(pluginKey, false); RemotePlugin plugin = new RemotePlugin(pluginKey, false);
pluginList.add(plugin); pluginList.add(plugin);
pluginFiles.put(plugin, location); pluginFiles.put(plugin, location);
return this; return this;
} }


public FakePluginsReferential addPlugin(String pluginKey, SonarPlugin pluginInstance) { public FakePluginsRepository addPlugin(String pluginKey, SonarPlugin pluginInstance) {
localPlugins.put(DefaultPluginMetadata.create(pluginKey), pluginInstance); localPlugins.put(DefaultPluginMetadata.create(pluginKey), pluginInstance);
return this; return this;
} }
Expand Down
Expand Up @@ -25,6 +25,9 @@
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent; import org.sonar.api.BatchComponent;
import org.sonar.api.CoreProperties; import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.batch.bootstrap.ProjectReactor;
Expand All @@ -37,10 +40,25 @@
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;


@Properties({
@Property(
key = ScmConfiguration.FORCE_RELOAD_KEY,
defaultValue = "false",
type = PropertyType.BOOLEAN,
name = "Force reloading of SCM information for all files",
description = "By default only files modified since previous analysis are inspected. Set this parameter to true to force the reloading.",
module = false,
project = false,
global = false,
category = CoreProperties.CATEGORY_SCM
)
})
@InstantiationStrategy(InstantiationStrategy.PER_BATCH) @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public final class ScmConfiguration implements BatchComponent, Startable { public final class ScmConfiguration implements BatchComponent, Startable {
private static final Logger LOG = LoggerFactory.getLogger(ScmConfiguration.class); private static final Logger LOG = LoggerFactory.getLogger(ScmConfiguration.class);


public static final String FORCE_RELOAD_KEY = "sonar.scm.forceReloadAll";

private final ProjectReactor projectReactor; private final ProjectReactor projectReactor;
private final Settings settings; private final Settings settings;
private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<String, ScmProvider>(); private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<String, ScmProvider>();
Expand Down Expand Up @@ -140,6 +158,10 @@ public boolean isDisabled() {
return settings.getBoolean(CoreProperties.SCM_DISABLED_KEY); return settings.getBoolean(CoreProperties.SCM_DISABLED_KEY);
} }


public boolean forceReloadAll() {
return settings.getBoolean(FORCE_RELOAD_KEY);
}

@Override @Override
public void stop() { public void stop() {
// Nothing to do // Nothing to do
Expand Down
20 changes: 16 additions & 4 deletions sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java
Expand Up @@ -73,10 +73,7 @@ public void execute(final SensorContext context) {
return; return;
} }


List<InputFile> filesToBlame = new LinkedList<InputFile>(); List<InputFile> filesToBlame = collectFilesToBlame(context);
for (InputFile f : fs.inputFiles(fs.predicates().all())) {
copyPreviousMeasuresForUnmodifiedFiles(context, filesToBlame, f);
}
if (!filesToBlame.isEmpty()) { if (!filesToBlame.isEmpty()) {
LOG.info("SCM provider for this project is: " + configuration.provider().key()); LOG.info("SCM provider for this project is: " + configuration.provider().key());
TimeProfiler profiler = new TimeProfiler().start("Retrieve SCM blame information"); TimeProfiler profiler = new TimeProfiler().start("Retrieve SCM blame information");
Expand All @@ -87,6 +84,21 @@ public void execute(final SensorContext context) {
} }
} }


private List<InputFile> collectFilesToBlame(final SensorContext context) {
if (configuration.forceReloadAll()) {
LOG.warn("Forced reloading of SCM data for all files.");
}
List<InputFile> filesToBlame = new LinkedList<InputFile>();
for (InputFile f : fs.inputFiles(fs.predicates().all())) {
if (!configuration.forceReloadAll()) {
copyPreviousMeasuresForUnmodifiedFiles(context, filesToBlame, f);
} else {
filesToBlame.add(f);
}
}
return filesToBlame;
}

private void copyPreviousMeasuresForUnmodifiedFiles(final SensorContext context, List<InputFile> filesToBlame, InputFile f) { private void copyPreviousMeasuresForUnmodifiedFiles(final SensorContext context, List<InputFile> filesToBlame, InputFile f) {
FileData fileData = projectReferentials.fileData(projectDefinition.getKeyWithBranch(), f.relativePath()); FileData fileData = projectReferentials.fileData(projectDefinition.getKeyWithBranch(), f.relativePath());


Expand Down
Expand Up @@ -20,6 +20,7 @@
package org.sonar.batch.mediumtest.scm; package org.sonar.batch.mediumtest.scm;


import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
Expand All @@ -31,7 +32,9 @@
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;
import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskBuilder;
import org.sonar.batch.mediumtest.TaskResult; import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.FileData;
import org.sonar.xoo.XooPlugin; import org.sonar.xoo.XooPlugin;


import java.io.File; import java.io.File;
Expand All @@ -41,6 +44,8 @@


public class ScmMediumTest { public class ScmMediumTest {


private static final String SAMPLE_XOO_CONTENT = "Sample xoo\ncontent";

@org.junit.Rule @org.junit.Rule
public TemporaryFolder temp = new TemporaryFolder(); public TemporaryFolder temp = new TemporaryFolder();


Expand All @@ -50,6 +55,7 @@ public class ScmMediumTest {
public BatchMediumTester tester = BatchMediumTester.builder() public BatchMediumTester tester = BatchMediumTester.builder()
.registerPlugin("xoo", new XooPlugin()) .registerPlugin("xoo", new XooPlugin())
.addDefaultQProfile("xoo", "Sonar Way") .addDefaultQProfile("xoo", "Sonar Way")
.addFileData("com.foo.project", "src/sample2.xoo", new FileData(DigestUtils.md5Hex(SAMPLE_XOO_CONTENT), false, "1=;2=", "1=;2=", "1=;2="))
.build(); .build();


@Before @Before
Expand Down Expand Up @@ -145,7 +151,57 @@ public void failIfMissingFile() throws IOException {
.put("sonar.scm.provider", "xoo") .put("sonar.scm.provider", "xoo")
.build()) .build())
.start(); .start();
}

@Test
public void copyPreviousMeasuresOrForceReload() throws IOException {

File baseDir = prepareProject();
File xooFileNoScm = new File(baseDir, "src/sample2.xoo");
FileUtils.write(xooFileNoScm, SAMPLE_XOO_CONTENT);

TaskBuilder taskBuilder = 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.scm.provider", "xoo")
.build());

TaskResult result = taskBuilder.start();

assertThat(result.measures()).hasSize(1 + 2 * 4);


assertThat(result.measures()).contains(new DefaultMeasure<Integer>()
.forMetric(CoreMetrics.LINES)
.onFile(new DefaultInputFile("com.foo.project", "src/sample2.xoo"))
.withValue(2));

assertThat(result.measures()).contains(new DefaultMeasure<String>()
.forMetric(CoreMetrics.SCM_AUTHORS_BY_LINE)
.onFile(new DefaultInputFile("com.foo.project", "src/sample2.xoo"))
.withValue("1=;2="));

// Force reload
File xooScmFile = new File(baseDir, "src/sample2.xoo.scm");
FileUtils.write(xooScmFile,
// revision,author,dateTime
"1,foo,2013-01-04\n" +
"1,bar,2013-01-04\n"
);

result = taskBuilder
.property("sonar.scm.forceReloadAll", "true")
.start();

assertThat(result.measures()).contains(new DefaultMeasure<String>()
.forMetric(CoreMetrics.SCM_AUTHORS_BY_LINE)
.onFile(new DefaultInputFile("com.foo.project", "src/sample2.xoo"))
.withValue("1=foo;2=bar"));
} }


@Test @Test
Expand Down Expand Up @@ -215,19 +271,18 @@ private File prepareProject() throws IOException {
File srcDir = new File(baseDir, "src"); File srcDir = new File(baseDir, "src");
srcDir.mkdir(); srcDir.mkdir();


File xooFile = new File(srcDir, "sample.xoo"); File xooFile1 = new File(srcDir, "sample.xoo");
File xooMeasureFile = new File(srcDir, "sample.xoo.measures"); FileUtils.write(xooFile1, "Sample xoo\ncontent\n3\n4\n5");
File xooScmFile = new File(srcDir, "sample.xoo.scm"); File xooScmFile1 = new File(srcDir, "sample.xoo.scm");
FileUtils.write(xooFile, "Sample xoo\ncontent\n3\n4\n5"); FileUtils.write(xooScmFile1,
FileUtils.write(xooMeasureFile, "lines:5");
FileUtils.write(xooScmFile,
// revision,author,dateTime // revision,author,dateTime
"1,,2013-01-04\n" + "1,,2013-01-04\n" +
"1,julien,2013-01-04\n" + "1,julien,2013-01-04\n" +
"2,julien,2013-02-03\n" + "2,julien,2013-02-03\n" +
"2,julien,2013-02-03\n" + "2,julien,2013-02-03\n" +
"3,simon,2013-03-04\n" "3,simon,2013-03-04\n"
); );

return baseDir; return baseDir;
} }


Expand Down

0 comments on commit c4ba2c0

Please sign in to comment.