aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine
diff options
context:
space:
mode:
authorSteve Marion <steve.marion@sonarsource.com>2025-02-28 16:38:04 +0100
committerMatteo Mara <matteo.mara@sonarsource.com>2025-03-17 22:23:55 +0100
commita087eb237d62b3280f1fc5d3540b1b57afccd4bd (patch)
treee7cbc5dfa9581be63b0cdb9d8e1a2657a0f187f5 /sonar-scanner-engine
parente496fb60dd1fbf1fd90b6ca08b9499ba8035a5c0 (diff)
downloadsonarqube-a087eb237d62b3280f1fc5d3540b1b57afccd4bd.tar.gz
sonarqube-a087eb237d62b3280f1fc5d3540b1b57afccd4bd.zip
SONAR-24521 Implements sonar plugin API addAnalysisData method in the scanner-engine.
Add Xoo plugin sensor that utilizes the analysisData method.
Diffstat (limited to 'sonar-scanner-engine')
-rw-r--r--sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/analysisdata/AnalysisDataIT.java68
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java12
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/LocalPlugin.java8
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java36
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java10
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java25
-rw-r--r--sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java4
-rw-r--r--sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java7
-rw-r--r--sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java10
9 files changed, 151 insertions, 29 deletions
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/analysisdata/AnalysisDataIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/analysisdata/AnalysisDataIT.java
new file mode 100644
index 00000000000..732d759264c
--- /dev/null
+++ b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/analysisdata/AnalysisDataIT.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2025 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.mediumtest.analysisdata;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.testfixtures.log.LogTester;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.scanner.mediumtest.AnalysisResult;
+import org.sonar.scanner.mediumtest.ScannerMediumTester;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooPlugin;
+
+public class AnalysisDataIT {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public ScannerMediumTester tester = new ScannerMediumTester()
+ .registerPlugin(new PluginInfo("xoo").setOrganizationName("SonarSource"), new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way");
+
+ @Test
+ public void whenScanningWithXoo_thenArchitectureGraphIsInReport() throws Exception {
+ // given
+ File projectDir = new File("test-resources/mediumtest/xoo/sample");
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ // when
+ AnalysisResult result = tester
+ .newAnalysis(new File(tmpDir, "sonar-project.properties"))
+ .execute();
+
+ // then
+ List<ScannerReport.AnalysisData> analysisData = result.analysisData();
+ assertThat(analysisData)
+ .anyMatch(data -> data.getKey().equals(Xoo.NAME + ".file_graph"));
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java
index 3187c2e9aa3..194c1e17ffa 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java
@@ -19,6 +19,10 @@
*/
package org.sonar.scanner.bootstrap;
+import static java.util.stream.Collectors.toMap;
+import static org.sonar.api.utils.Preconditions.checkState;
+import static org.sonar.core.config.ScannerProperties.PLUGIN_LOADING_OPTIMIZATION_KEY;
+
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -38,10 +42,6 @@ import org.sonar.core.platform.PluginRepository;
import org.sonar.core.plugin.PluginType;
import org.sonar.scanner.mediumtest.LocalPlugin;
-import static java.util.stream.Collectors.toMap;
-import static org.sonar.api.utils.Preconditions.checkState;
-import static org.sonar.core.config.ScannerProperties.PLUGIN_LOADING_OPTIMIZATION_KEY;
-
/**
* Orchestrates the installation and loading of plugins
*/
@@ -83,7 +83,7 @@ public class ScannerPluginRepository implements PluginRepository, Startable {
// this part is only used by medium tests
for (LocalPlugin localPlugin : installer.installLocals()) {
ScannerPlugin scannerPlugin = localPlugin.toScannerPlugin();
- String pluginKey = localPlugin.pluginKey();
+ String pluginKey = localPlugin.pluginInfo().getKey();
pluginsByKeys.put(pluginKey, scannerPlugin);
pluginInstancesByKeys.put(pluginKey, localPlugin.pluginInstance());
}
@@ -112,7 +112,7 @@ public class ScannerPluginRepository implements PluginRepository, Startable {
// this part is only used by medium tests
for (LocalPlugin localPlugin : installer.installOptionalLocals(languageKeys)) {
ScannerPlugin scannerPlugin = localPlugin.toScannerPlugin();
- String pluginKey = localPlugin.pluginKey();
+ String pluginKey = localPlugin.pluginInfo().getKey();
languagePluginsByKeys.put(pluginKey, scannerPlugin);
pluginsByKeys.put(pluginKey, scannerPlugin);
pluginInstancesByKeys.put(pluginKey, localPlugin.pluginInstance());
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/LocalPlugin.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/LocalPlugin.java
index 0a3d42979f7..70df99a4f3d 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/LocalPlugin.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/LocalPlugin.java
@@ -25,9 +25,13 @@ import org.sonar.core.platform.PluginInfo;
import org.sonar.core.plugin.PluginType;
import org.sonar.scanner.bootstrap.ScannerPlugin;
-public record LocalPlugin(String pluginKey, Plugin pluginInstance, Set<String> requiredForLanguages) {
+public record LocalPlugin(PluginInfo pluginInfo, Plugin pluginInstance, Set<String> requiredForLanguages) {
+
+ public LocalPlugin(String pluginKey, Plugin pluginInstance, Set<String> requiredForLanguages) {
+ this(new PluginInfo(pluginKey).setOrganizationName("SonarSource"), pluginInstance, requiredForLanguages);
+ }
public ScannerPlugin toScannerPlugin() {
- return new ScannerPlugin(pluginKey, 1L, PluginType.BUNDLED, new PluginInfo(pluginKey));
+ return new ScannerPlugin(pluginInfo.getKey(), 1L, PluginType.BUNDLED, pluginInfo);
}
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
index 5fa6e33aac6..dfc6df5050c 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
@@ -19,6 +19,16 @@
*/
package org.sonar.scanner.sensor;
+import static java.lang.Math.max;
+import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
+import static org.sonar.api.measures.CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY;
+import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY;
+import static org.sonar.api.utils.Preconditions.checkArgument;
+
+import com.google.protobuf.ByteString;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
@@ -28,6 +38,7 @@ import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputComponent;
@@ -80,12 +91,6 @@ import org.sonar.scanner.repository.ContextPropertiesCache;
import org.sonar.scanner.repository.TelemetryCache;
import org.sonar.scanner.scan.branch.BranchConfiguration;
-import static java.lang.Math.max;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DATA_KEY;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY;
-import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY;
-
public class DefaultSensorStorage implements SensorStorage {
private static final Logger LOG = LoggerFactory.getLogger(DefaultSensorStorage.class);
@@ -122,6 +127,7 @@ public class DefaultSensorStorage implements SensorStorage {
private final ScannerMetrics scannerMetrics;
private final BranchConfiguration branchConfiguration;
private final Set<String> alreadyLogged = new HashSet<>();
+ private final Set<String> alreadyAddedData = new HashSet<>();
public DefaultSensorStorage(MetricFinder metricFinder, IssuePublisher moduleIssues, Configuration settings, ReportPublisher reportPublisher, SonarCpdBlockIndex index,
ContextPropertiesCache contextPropertiesCache, TelemetryCache telemetryCache, ScannerMetrics scannerMetrics, BranchConfiguration branchConfiguration) {
@@ -472,4 +478,22 @@ public class DefaultSensorStorage implements SensorStorage {
writer.writeComponentSignificantCode(componentRef, protobuf);
}
+
+ public void storeAnalysisData(String key, String mimeType, InputStream data) {
+ checkArgument(!StringUtils.isBlank(key), "Key must not be null");
+ checkArgument(!alreadyAddedData.contains(key), "A data with this key already exists");
+ checkArgument(!StringUtils.isBlank(mimeType), "MimeType must not be null");
+ checkArgument(data != null, "Data must not be null");
+ alreadyAddedData.add(key);
+ try (data) {
+ ScannerReport.AnalysisData analysisData = ScannerReport.AnalysisData.newBuilder()
+ .setKey(key)
+ .setData(ByteString.readFrom(data))
+ .build();
+ ScannerReportWriter writer = reportPublisher.getWriter();
+ writer.appendAnalysisData(analysisData);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Failed to read data InputStream", e);
+ }
+ }
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
index bac06a38645..855fd945109 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
@@ -19,6 +19,7 @@
*/
package org.sonar.scanner.sensor;
+import java.io.InputStream;
import java.io.Serializable;
import javax.annotation.concurrent.ThreadSafe;
import org.sonar.api.SonarRuntime;
@@ -233,6 +234,15 @@ public class ProjectSensorContext implements SensorContext {
}
@Override
+ public void addAnalysisData(String key, String mimeType, InputStream data) {
+ if (isSonarSourcePlugin()) {
+ this.sensorStorage.storeAnalysisData(key, mimeType, data);
+ } else {
+ throw new IllegalStateException("Analysis data can only be added by SonarSource plugins");
+ }
+ }
+
+ @Override
public NewSignificantCode newSignificantCode() {
return new DefaultSignificantCode(sensorStorage);
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
index a6cabe242fb..e769e0d6451 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
@@ -19,6 +19,16 @@
*/
package org.sonar.scanner.sensor;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.data.MapEntry.entry;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -67,16 +77,6 @@ import org.sonar.scanner.repository.ContextPropertiesCache;
import org.sonar.scanner.repository.TelemetryCache;
import org.sonar.scanner.scan.branch.BranchConfiguration;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.assertj.core.data.MapEntry.entry;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
class DefaultSensorStorageTest {
@TempDir
@@ -112,8 +112,9 @@ class DefaultSensorStorageTest {
branchConfiguration = mock(BranchConfiguration.class);
- underTest = new DefaultSensorStorage(metricFinder,
- moduleIssues, settings.asConfig(), reportPublisher, mock(SonarCpdBlockIndex.class), contextPropertiesCache, telemetryCache, new ScannerMetrics(), branchConfiguration);
+ underTest = new DefaultSensorStorage(
+ metricFinder, moduleIssues, settings.asConfig(), reportPublisher, mock(SonarCpdBlockIndex.class), contextPropertiesCache, telemetryCache, new ScannerMetrics(),
+ branchConfiguration);
project = new DefaultInputProject(ProjectDefinition.create()
.setKey("foo")
diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java
index 25b27dbb5ae..57843c8abd7 100644
--- a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java
+++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java
@@ -199,6 +199,10 @@ public class AnalysisResult implements AnalysisObserver {
return readFromReport(ScannerReportReader::readAdHocRules);
}
+ public List<ScannerReport.AnalysisData> analysisData() {
+ return readFromReport(ScannerReportReader::readAnalysisData);
+ }
+
@NotNull
private <G> List<G> readFromReport(InputComponent component, BiFunction<ScannerReportReader, Integer, CloseableIterator<G>> readerMethod) {
int ref = ((DefaultInputComponent) component).scannerId();
diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java
index 60a8348f778..5934c9da0bc 100644
--- a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java
+++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java
@@ -19,13 +19,13 @@
*/
package org.sonar.scanner.mediumtest;
+import jakarta.annotation.Priority;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import jakarta.annotation.Priority;
import org.sonar.api.Plugin;
import org.sonar.core.platform.PluginInfo;
import org.sonar.core.plugin.PluginType;
@@ -49,6 +49,11 @@ public class FakePluginInstaller implements PluginInstaller {
return this;
}
+ public FakePluginInstaller add(PluginInfo pluginInfo, Plugin instance) {
+ mediumTestPlugins.add(new LocalPlugin(pluginInfo, instance, Set.of()));
+ return this;
+ }
+
public FakePluginInstaller addOptional(String pluginKey, Set<String> requiredForLanguages, Plugin instance) {
optionalMediumTestPlugins.add(new LocalPlugin(pluginKey, instance, requiredForLanguages));
return this;
diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
index 1f6738ec99c..f2edfe609d4 100644
--- a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
+++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
@@ -19,6 +19,8 @@
*/
package org.sonar.scanner.mediumtest;
+import static java.util.Collections.emptySet;
+
import jakarta.annotation.Priority;
import java.io.File;
import java.io.FileInputStream;
@@ -60,6 +62,7 @@ import org.sonar.api.utils.Version;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.bootstrapper.LogOutput;
+import org.sonar.core.platform.PluginInfo;
import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
import org.sonar.scanner.cache.AnalysisCacheLoader;
import org.sonar.scanner.protocol.internal.SensorCacheData;
@@ -86,8 +89,6 @@ import org.sonarqube.ws.NewCodePeriods;
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
import org.sonarqube.ws.Rules.Rule;
-import static java.util.Collections.emptySet;
-
/**
* Main utility class for writing scanner medium tests.
*/
@@ -146,6 +147,11 @@ public class ScannerMediumTester extends ExternalResource implements BeforeTestE
return this;
}
+ public ScannerMediumTester registerPlugin(PluginInfo pluginInfo, Plugin instance) {
+ pluginInstaller.add(pluginInfo, instance);
+ return this;
+ }
+
public ScannerMediumTester registerOptionalPlugin(String pluginKey, Set<String> requiredForLanguages, Plugin instance) {
pluginInstaller.addOptional(pluginKey, requiredForLanguages, instance);
return this;