From: Julien HENRY Date: Wed, 4 Sep 2024 09:46:59 +0000 (+0200) Subject: SONAR-22914 Move the ScannerMediumTester to testFixtures X-Git-Tag: 10.7.0.96327~110 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1d44e3b465b8df3ad3a0c63d17b2b24ef529fd79;p=sonarqube.git SONAR-22914 Move the ScannerMediumTester to testFixtures --- diff --git a/sonar-scanner-engine/build.gradle b/sonar-scanner-engine/build.gradle index 121d7eec295..4a7d8bc12ea 100644 --- a/sonar-scanner-engine/build.gradle +++ b/sonar-scanner-engine/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'java-test-fixtures' +} + sonar { properties { property 'sonar.projectName', "${projectTitle} :: Scanner Engine" @@ -50,6 +54,8 @@ dependencies { compileOnlyApi 'com.github.spotbugs:spotbugs-annotations' + testFixturesImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'com.squareup.okhttp3:mockwebserver' testImplementation 'com.squareup.okhttp3:okhttp' testImplementation 'com.squareup.okio:okio' diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java deleted file mode 100644 index 59eb6290e79..00000000000 --- a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java +++ /dev/null @@ -1,616 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import javax.annotation.Priority; -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.extension.AfterTestExecutionCallback; -import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.rules.ExternalResource; -import org.sonar.api.Plugin; -import org.sonar.api.SonarEdition; -import org.sonar.api.SonarProduct; -import org.sonar.api.SonarQubeSide; -import org.sonar.api.SonarRuntime; -import org.sonar.api.batch.rule.LoadedActiveRule; -import org.sonar.api.impl.server.RulesDefinitionContext; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Metric; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.server.rule.RulesDefinition.Repository; -import org.sonar.api.utils.DateUtils; -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.scanner.bootstrap.GlobalAnalysisMode; -import org.sonar.scanner.cache.AnalysisCacheLoader; -import org.sonar.scanner.protocol.internal.SensorCacheData; -import org.sonar.scanner.report.CeTaskReportDataHolder; -import org.sonar.scanner.repository.FileData; -import org.sonar.scanner.repository.MetricsRepository; -import org.sonar.scanner.repository.MetricsRepositoryLoader; -import org.sonar.scanner.repository.NewCodePeriodLoader; -import org.sonar.scanner.repository.ProjectRepositories; -import org.sonar.scanner.repository.ProjectRepositoriesLoader; -import org.sonar.scanner.repository.QualityProfileLoader; -import org.sonar.scanner.repository.SingleProjectRepository; -import org.sonar.scanner.repository.settings.GlobalSettingsLoader; -import org.sonar.scanner.repository.settings.ProjectSettingsLoader; -import org.sonar.scanner.rule.ActiveRulesLoader; -import org.sonar.scanner.rule.RulesLoader; -import org.sonar.scanner.scan.ScanProperties; -import org.sonar.scanner.scan.branch.BranchConfiguration; -import org.sonar.scanner.scan.branch.BranchConfigurationLoader; -import org.sonar.scanner.scan.branch.BranchType; -import org.sonar.scanner.scan.branch.ProjectBranches; -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. - */ -public class ScannerMediumTester extends ExternalResource implements BeforeTestExecutionCallback, AfterTestExecutionCallback { - - private static Path userHome = null; - private final Map globalProperties = new HashMap<>(); - private final FakeMetricsRepositoryLoader globalRefProvider = new FakeMetricsRepositoryLoader(); - private final FakeBranchConfigurationLoader branchConfigurationLoader = new FakeBranchConfigurationLoader(); - private final FakeBranchConfiguration branchConfiguration = new FakeBranchConfiguration(); - private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader(); - private final FakePluginInstaller pluginInstaller = new FakePluginInstaller(); - private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); - private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); - private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); - private final FakeAnalysisCacheLoader analysisCacheLoader = new FakeAnalysisCacheLoader(); - private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); - private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); - private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); - private final FakeSonarRuntime sonarRuntime = new FakeSonarRuntime(); - private final CeTaskReportDataHolder reportMetadataHolder = new CeTaskReportDataHolderExt(); - private final FakeLanguagesLoader languagesLoader = new FakeLanguagesLoader(); - private final FakeLanguagesProvider languagesProvider = new FakeLanguagesProvider(); - private LogOutput logOutput = null; - - private static void createWorkingDirs() throws IOException { - destroyWorkingDirs(); - - userHome = java.nio.file.Files.createTempDirectory("mediumtest-userHome"); - } - - private static void destroyWorkingDirs() throws IOException { - if (userHome != null) { - FileUtils.deleteDirectory(userHome.toFile()); - userHome = null; - } - } - - public ScannerMediumTester setLogOutput(LogOutput logOutput) { - this.logOutput = logOutput; - return this; - } - - public ScannerMediumTester registerPlugin(String pluginKey, File location) { - return registerPlugin(pluginKey, location, 1L); - } - - public ScannerMediumTester registerPlugin(String pluginKey, File location, long lastUpdatedAt) { - pluginInstaller.add(pluginKey, location, lastUpdatedAt); - return this; - } - - public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance) { - pluginInstaller.add(pluginKey, instance); - return this; - } - - public ScannerMediumTester registerOptionalPlugin(String pluginKey, Set requiredForLanguages, Plugin instance) { - pluginInstaller.addOptional(pluginKey, requiredForLanguages, instance); - return this; - } - - public ScannerMediumTester registerCoreMetrics() { - for (Metric m : CoreMetrics.getMetrics()) { - registerMetric(m); - } - return this; - } - - public ScannerMediumTester registerMetric(Metric metric) { - globalRefProvider.add(metric); - return this; - } - - public ScannerMediumTester addQProfile(String language, String name) { - qualityProfiles.add(language, name); - return this; - } - - public ScannerMediumTester addRule(Rule rule) { - rulesLoader.addRule(rule); - return this; - } - - public ScannerMediumTester addRule(String key, String repoKey, String internalKey, String name) { - Rule.Builder builder = Rule.newBuilder(); - builder.setKey(key); - builder.setRepo(repoKey); - if (internalKey != null) { - builder.setInternalKey(internalKey); - } - builder.setName(name); - - rulesLoader.addRule(builder.build()); - return this; - } - - public ScannerMediumTester addRules(RulesDefinition rulesDefinition) { - RulesDefinition.Context context = new RulesDefinitionContext(); - rulesDefinition.define(context); - List repositories = context.repositories(); - for (Repository repo : repositories) { - for (RulesDefinition.Rule rule : repo.rules()) { - this.addRule(rule.key(), rule.repository().key(), rule.internalKey(), rule.name()); - } - } - return this; - } - - public ScannerMediumTester addDefaultQProfile(String language, String name) { - addQProfile(language, name); - return this; - } - - public ScannerMediumTester bootstrapProperties(Map props) { - globalProperties.putAll(props); - return this; - } - - public ScannerMediumTester activateRule(LoadedActiveRule activeRule) { - activeRules.addActiveRule(activeRule); - return this; - } - - public ScannerMediumTester addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity, - @Nullable String internalKey, @Nullable String language) { - LoadedActiveRule r = new LoadedActiveRule(); - - r.setInternalKey(internalKey); - r.setRuleKey(RuleKey.of(repositoryKey, ruleKey)); - r.setName(name); - r.setTemplateRuleKey(templateRuleKey); - r.setLanguage(language); - r.setSeverity(severity); - r.setDeprecatedKeys(emptySet()); - - activeRules.addActiveRule(r); - return this; - } - - public ScannerMediumTester addFileData(String path, FileData fileData) { - projectRefProvider.addFileData(path, fileData); - return this; - } - - public ScannerMediumTester addGlobalServerSettings(String key, String value) { - globalSettingsLoader.getGlobalSettings().put(key, value); - return this; - } - - public ScannerMediumTester addProjectServerSettings(String key, String value) { - projectSettingsLoader.getProjectSettings().put(key, value); - return this; - } - - public ScannerMediumTester setNewCodePeriod(NewCodePeriods.NewCodePeriodType type, String value) { - newCodePeriodLoader.set(NewCodePeriods.ShowWSResponse.newBuilder().setType(type).setValue(value).build()); - return this; - } - - @Override - public void afterTestExecution(ExtensionContext extensionContext) { - after(); - } - - @Override - public void beforeTestExecution(ExtensionContext extensionContext) { - before(); - } - - @Override - protected void before() { - try { - createWorkingDirs(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - registerCoreMetrics(); - globalProperties.put(GlobalAnalysisMode.MEDIUM_TEST_ENABLED, "true"); - globalProperties.put(ScanProperties.KEEP_REPORT_PROP_KEY, "true"); - globalProperties.put("sonar.userHome", userHome.toString()); - } - - @Override - protected void after() { - try { - destroyWorkingDirs(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - - public AnalysisBuilder newAnalysis() { - return new AnalysisBuilder(this); - } - - public AnalysisBuilder newAnalysis(File sonarProps) { - Properties prop = new Properties(); - try (Reader reader = new InputStreamReader(new FileInputStream(sonarProps), StandardCharsets.UTF_8)) { - prop.load(reader); - } catch (Exception e) { - throw new IllegalStateException("Unable to read configuration file", e); - } - AnalysisBuilder builder = new AnalysisBuilder(this); - builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath()); - for (Map.Entry entry : prop.entrySet()) { - builder.property(entry.getKey().toString(), entry.getValue().toString()); - } - return builder; - } - - public void addLanguage(String key, String name, String... suffixes) { - languagesLoader.addLanguage(key, name, suffixes, new String[0]); - languagesProvider.addLanguage(key, name, true); - } - - public void addLanguage(String key, String name, boolean publishAllFiles, String... suffixes) { - languagesLoader.addLanguage(key, name, suffixes, new String[0]); - languagesProvider.addLanguage(key, name, publishAllFiles); - } - - public static class AnalysisBuilder { - private final Map taskProperties = new HashMap<>(); - private final ScannerMediumTester tester; - - public AnalysisBuilder(ScannerMediumTester tester) { - this.tester = tester; - } - - public AnalysisResult execute() { - AnalysisResult result = new AnalysisResult(); - Map props = new HashMap<>(); - props.putAll(tester.globalProperties); - props.putAll(taskProperties); - - Batch.Builder builder = Batch.builder() - .setGlobalProperties(props) - .setEnableLoggingConfiguration(true) - .addComponents(new EnvironmentInformation("mediumTest", "1.0"), - tester.pluginInstaller, - tester.globalRefProvider, - tester.qualityProfiles, - tester.rulesLoader, - tester.branchConfigurationLoader, - tester.projectRefProvider, - tester.activeRules, - tester.globalSettingsLoader, - tester.projectSettingsLoader, - tester.newCodePeriodLoader, - tester.analysisCacheLoader, - tester.sonarRuntime, - tester.reportMetadataHolder, - tester.languagesLoader, - tester.languagesProvider, - result); - if (tester.logOutput != null) { - builder.setLogOutput(tester.logOutput); - } else { - builder.setEnableLoggingConfiguration(false); - } - builder.build().execute(); - - return result; - } - - public AnalysisBuilder properties(Map props) { - taskProperties.putAll(props); - return this; - } - - public AnalysisBuilder property(String key, String value) { - taskProperties.put(key, value); - return this; - } - - } - - @Priority(1) - private static class FakeRulesLoader implements RulesLoader { - private List rules = new LinkedList<>(); - - public FakeRulesLoader addRule(Rule rule) { - rules.add(rule); - return this; - } - - @Override - public List load() { - return rules; - } - } - - @Priority(1) - private static class FakeActiveRulesLoader implements ActiveRulesLoader { - private List activeRules = new LinkedList<>(); - - public void addActiveRule(LoadedActiveRule activeRule) { - this.activeRules.add(activeRule); - } - - @Override - public List load(String qualityProfileKey) { - return activeRules; - } - } - - @Priority(1) - private static class FakeMetricsRepositoryLoader implements MetricsRepositoryLoader { - - private int metricId = 1; - - private List metrics = new ArrayList<>(); - - @Override - public MetricsRepository load() { - return new MetricsRepository(metrics); - } - - public FakeMetricsRepositoryLoader add(Metric metric) { - metric.setUuid("metric" + metricId++); - metrics.add(metric); - metricId++; - return this; - } - - } - - @Priority(1) - private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader { - private Map fileDataMap = new HashMap<>(); - - @Override - public ProjectRepositories load(String projectKey, @Nullable String branchBase) { - return new SingleProjectRepository(fileDataMap); - } - - public FakeProjectRepositoriesLoader addFileData(String path, FileData fileData) { - fileDataMap.put(path, fileData); - return this; - } - - } - - @Priority(1) - private static class FakeBranchConfiguration implements BranchConfiguration { - - private BranchType branchType = BranchType.BRANCH; - private String branchName = null; - private String branchTarget = null; - private String referenceBranchName = null; - - @Override - public BranchType branchType() { - return branchType; - } - - @CheckForNull - @Override - public String branchName() { - return branchName; - } - - @CheckForNull - @Override - public String targetBranchName() { - return branchTarget; - } - - @CheckForNull - @Override - public String referenceBranchName() { - return referenceBranchName; - } - - @Override - public String pullRequestKey() { - return "1'"; - } - } - - @Priority(1) - private static class FakeSonarRuntime implements SonarRuntime { - - private SonarEdition edition; - - FakeSonarRuntime() { - this.edition = SonarEdition.COMMUNITY; - } - - @Override - public Version getApiVersion() { - return Version.create(7, 8); - } - - @Override - public SonarProduct getProduct() { - return SonarProduct.SONARQUBE; - } - - @Override - public SonarQubeSide getSonarQubeSide() { - return SonarQubeSide.SCANNER; - } - - @Override - public SonarEdition getEdition() { - return edition; - } - - public void setEdition(SonarEdition edition) { - this.edition = edition; - } - } - - public ScannerMediumTester setBranchType(BranchType branchType) { - branchConfiguration.branchType = branchType; - return this; - } - - public ScannerMediumTester setBranchName(String branchName) { - this.branchConfiguration.branchName = branchName; - return this; - } - - public ScannerMediumTester setBranchTarget(String branchTarget) { - this.branchConfiguration.branchTarget = branchTarget; - return this; - } - - public ScannerMediumTester setReferenceBranchName(String referenceBranchNam) { - this.branchConfiguration.referenceBranchName = referenceBranchNam; - return this; - } - - public ScannerMediumTester setEdition(SonarEdition edition) { - this.sonarRuntime.setEdition(edition); - return this; - } - - @Priority(1) - private class FakeBranchConfigurationLoader implements BranchConfigurationLoader { - @Override - public BranchConfiguration load(Map projectSettings, ProjectBranches branches) { - return branchConfiguration; - } - } - - @Priority(1) - private static class FakeQualityProfileLoader implements QualityProfileLoader { - - private List qualityProfiles = new LinkedList<>(); - - public void add(String language, String name) { - qualityProfiles.add(QualityProfile.newBuilder() - .setLanguage(language) - .setKey(name) - .setName(name) - .setRulesUpdatedAt(DateUtils.formatDateTime(new Date(1234567891212L))) - .build()); - } - - @Override - public List load(String projectKey) { - return qualityProfiles; - } - } - - @Priority(1) - private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader { - @Override - public Optional load() { - return Optional.empty(); - } - } - - @Priority(1) - private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { - - private Map globalSettings = new HashMap<>(); - - public Map getGlobalSettings() { - return globalSettings; - } - - @Override - public Map loadGlobalSettings() { - return Collections.unmodifiableMap(globalSettings); - } - } - - @Priority(1) - private static class FakeNewCodePeriodLoader implements NewCodePeriodLoader { - private NewCodePeriods.ShowWSResponse response; - - @Override - public NewCodePeriods.ShowWSResponse load(String projectKey, String branchName) { - return response; - } - - public void set(NewCodePeriods.ShowWSResponse response) { - this.response = response; - } - } - - @Priority(1) - private static class FakeProjectSettingsLoader implements ProjectSettingsLoader { - - private Map projectSettings = new HashMap<>(); - - public Map getProjectSettings() { - return projectSettings; - } - - @Override - public Map loadProjectSettings() { - return Collections.unmodifiableMap(projectSettings); - } - } - - @Priority(1) - private static class CeTaskReportDataHolderExt extends CeTaskReportDataHolder { - - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/AnalysisResult.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/AnalysisResult.java deleted file mode 100644 index c4c8d099225..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/AnalysisResult.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.annotation.CheckForNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.TextPointer; -import org.sonar.api.batch.fs.TextRange; -import org.sonar.api.batch.fs.internal.DefaultInputComponent; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.api.scanner.fs.InputProject; -import org.sonar.core.util.CloseableIterator; -import org.sonar.scanner.protocol.output.FileStructure; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.Component; -import org.sonar.scanner.protocol.output.ScannerReport.Symbol; -import org.sonar.scanner.protocol.output.ScannerReportReader; -import org.sonar.scanner.report.ScannerReportUtils; -import org.sonar.scanner.scan.SpringProjectScanContainer; -import org.sonar.scanner.scan.filesystem.InputComponentStore; - -public class AnalysisResult implements AnalysisObserver { - - private static final Logger LOG = LoggerFactory.getLogger(AnalysisResult.class); - - private Map inputFilesByKeys = new HashMap<>(); - private InputProject project; - private ScannerReportReader reader; - - @Override - public void analysisCompleted(SpringProjectScanContainer container) { - LOG.info("Store analysis results in memory for later assertions in medium test"); - FileStructure fileStructure = container.getComponentByType(FileStructure.class); - reader = new ScannerReportReader(fileStructure); - project = container.getComponentByType(InputProject.class); - - storeFs(container); - - } - - public ScannerReportReader getReportReader() { - return reader; - } - - private void storeFs(SpringProjectScanContainer container) { - InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class); - for (InputFile inputPath : inputFileCache.inputFiles()) { - inputFilesByKeys.put(((DefaultInputFile) inputPath).getProjectRelativePath(), inputPath); - } - } - - public Component getReportComponent(InputComponent inputComponent) { - return getReportReader().readComponent(((DefaultInputComponent) inputComponent).scannerId()); - } - - public Component getReportComponent(int scannerId) { - return getReportReader().readComponent(scannerId); - } - - public List issuesFor(InputComponent inputComponent) { - return issuesFor(((DefaultInputComponent) inputComponent).scannerId()); - } - - public List externalIssuesFor(InputComponent inputComponent) { - return externalIssuesFor(((DefaultInputComponent) inputComponent).scannerId()); - } - - public List issuesFor(Component reportComponent) { - int ref = reportComponent.getRef(); - return issuesFor(ref); - } - - private List issuesFor(int ref) { - List result = new ArrayList<>(); - try (CloseableIterator it = reader.readComponentIssues(ref)) { - while (it.hasNext()) { - result.add(it.next()); - } - } - return result; - } - - private List externalIssuesFor(int ref) { - List result = new ArrayList<>(); - try (CloseableIterator it = reader.readComponentExternalIssues(ref)) { - while (it.hasNext()) { - result.add(it.next()); - } - } - return result; - } - - public InputProject project() { - return project; - } - - public Collection inputFiles() { - return inputFilesByKeys.values(); - } - - @CheckForNull - public InputFile inputFile(String relativePath) { - return inputFilesByKeys.get(relativePath); - } - - public Map> allMeasures() { - Map> result = new HashMap<>(); - List projectMeasures = new ArrayList<>(); - try (CloseableIterator it = reader.readComponentMeasures(((DefaultInputComponent) project).scannerId())) { - while (it.hasNext()) { - projectMeasures.add(it.next()); - } - } - result.put(project.key(), projectMeasures); - for (InputFile inputFile : inputFilesByKeys.values()) { - List measures = new ArrayList<>(); - try (CloseableIterator it = reader.readComponentMeasures(((DefaultInputComponent) inputFile).scannerId())) { - while (it.hasNext()) { - measures.add(it.next()); - } - } - result.put(inputFile.key(), measures); - } - return result; - } - - /** - * Get highlighting types at a given position in an inputfile - * - * @param lineOffset 0-based offset in file - */ - public List highlightingTypeFor(InputFile file, int line, int lineOffset) { - int ref = ((DefaultInputComponent) file).scannerId(); - if (!reader.hasSyntaxHighlighting(ref)) { - return Collections.emptyList(); - } - TextPointer pointer = file.newPointer(line, lineOffset); - List result = new ArrayList<>(); - try (CloseableIterator it = reader.readComponentSyntaxHighlighting(ref)) { - while (it.hasNext()) { - ScannerReport.SyntaxHighlightingRule rule = it.next(); - TextRange ruleRange = toRange(file, rule.getRange()); - if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) { - result.add(ScannerReportUtils.toBatchType(rule.getType())); - } - } - } catch (Exception e) { - throw new IllegalStateException("Can't read syntax highlighting for " + file, e); - } - return result; - } - - private static TextRange toRange(InputFile file, ScannerReport.TextRange reportRange) { - return file.newRange(file.newPointer(reportRange.getStartLine(), reportRange.getStartOffset()), file.newPointer(reportRange.getEndLine(), reportRange.getEndOffset())); - } - - /** - * Get list of all start positions of a symbol in an inputfile - * - * @param symbolStartLine 0-based start offset for the symbol in file - * @param symbolStartLineOffset 0-based end offset for the symbol in file - */ - @CheckForNull - public List symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) { - int ref = ((DefaultInputComponent) file).scannerId(); - try (CloseableIterator symbols = getReportReader().readComponentSymbols(ref)) { - while (symbols.hasNext()) { - Symbol symbol = symbols.next(); - if (symbol.getDeclaration().getStartLine() == symbolStartLine && symbol.getDeclaration().getStartOffset() == symbolStartLineOffset) { - return symbol.getReferenceList(); - } - } - } - return Collections.emptyList(); - } - - public List duplicationsFor(InputFile file) { - List result = new ArrayList<>(); - int ref = ((DefaultInputComponent) file).scannerId(); - try (CloseableIterator it = getReportReader().readComponentDuplications(ref)) { - while (it.hasNext()) { - result.add(it.next()); - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - return result; - } - - public List duplicationBlocksFor(InputFile file) { - List result = new ArrayList<>(); - int ref = ((DefaultInputComponent) file).scannerId(); - try (CloseableIterator it = getReportReader().readCpdTextBlocks(ref)) { - while (it.hasNext()) { - result.add(it.next()); - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - return result; - } - - @CheckForNull - public ScannerReport.LineCoverage coverageFor(InputFile file, int line) { - int ref = ((DefaultInputComponent) file).scannerId(); - try (CloseableIterator it = getReportReader().readComponentCoverage(ref)) { - while (it.hasNext()) { - ScannerReport.LineCoverage coverage = it.next(); - if (coverage.getLine() == line) { - return coverage; - } - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - return null; - } - - public List adHocRules() { - List result = new ArrayList<>(); - try (CloseableIterator it = getReportReader().readAdHocRules()) { - while (it.hasNext()) { - result.add(it.next()); - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - return result; - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java deleted file mode 100644 index f2e2f48d556..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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; - -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Priority; -import org.sonar.api.resources.Languages; -import org.sonar.scanner.repository.language.Language; -import org.sonar.scanner.repository.language.LanguagesLoader; -import org.sonar.scanner.repository.language.SupportedLanguageDto; - -@Priority(1) -public class FakeLanguagesLoader implements LanguagesLoader { - - private final Map languageMap = new HashMap<>(); - - public FakeLanguagesLoader() { - languageMap.put("xoo", new Language(new SupportedLanguageDto("xoo", "xoo", new String[] { ".xoo" }, new String[0]))); - } - - public FakeLanguagesLoader(Languages languages) { - for (org.sonar.api.resources.Language language : languages.all()) { - languageMap.put(language.getKey(), new Language(new SupportedLanguageDto(language.getKey(), language.getName(), language.getFileSuffixes(), language.filenamePatterns()))); - } - } - @Override - public Map load() { - return languageMap; - } - - public void addLanguage(String key, String name, String[] suffixes, String[] filenamePatterns) { - languageMap.put(key, new Language(new SupportedLanguageDto(key, name, suffixes, filenamePatterns))); - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java deleted file mode 100644 index 60097466670..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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; - -import javax.annotation.Priority; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.springframework.context.annotation.Bean; - -@Priority(1) -public class FakeLanguagesProvider { - - private Languages languages = new Languages(); - - @Bean("Languages") - public Languages provide() { - return this.languages; - } - - public void addLanguage(String key, String name, boolean publishAllFiles) { - this.languages.add(new FakeLanguage(key, name, publishAllFiles)); - } - - private static class FakeLanguage implements Language { - - private final String name; - private final String key; - private final boolean publishAllFiles; - - public FakeLanguage(String key, String name, boolean publishAllFiles) { - this.name = name; - this.key = key; - this.publishAllFiles = publishAllFiles; - } - - @Override - public String getKey() { - return this.key; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String[] getFileSuffixes() { - return new String[0]; - } - - @Override - public boolean publishAllFiles() { - return this.publishAllFiles; - } - } - - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java deleted file mode 100644 index a735827e65d..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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; - -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 javax.annotation.Priority; -import org.sonar.api.Plugin; -import org.sonar.core.platform.PluginInfo; -import org.sonar.core.plugin.PluginType; -import org.sonar.scanner.bootstrap.PluginInstaller; -import org.sonar.scanner.bootstrap.ScannerPlugin; - -@Priority(1) -public class FakePluginInstaller implements PluginInstaller { - - private final Map pluginsByKeys = new HashMap<>(); - private final List mediumTestPlugins = new ArrayList<>(); - private final List optionalMediumTestPlugins = new ArrayList<>(); - - public FakePluginInstaller add(String pluginKey, File jarFile, long lastUpdatedAt) { - pluginsByKeys.put(pluginKey, new ScannerPlugin(pluginKey, lastUpdatedAt, PluginType.BUNDLED, PluginInfo.create(jarFile))); - return this; - } - - public FakePluginInstaller add(String pluginKey, Plugin instance) { - mediumTestPlugins.add(new LocalPlugin(pluginKey, instance, Set.of())); - return this; - } - - public FakePluginInstaller addOptional(String pluginKey, Set requiredForLanguages, Plugin instance) { - optionalMediumTestPlugins.add(new LocalPlugin(pluginKey, instance, requiredForLanguages)); - return this; - } - - @Override - public Map installAllPlugins() { - return pluginsByKeys; - } - - @Override - public Map installRequiredPlugins() { - return pluginsByKeys; - } - - @Override - public Map installPluginsForLanguages(Set languageKeys) { - return pluginsByKeys; - } - - @Override - public List installLocals() { - return mediumTestPlugins; - } - - @Override - public List installOptionalLocals(Set languageKeys) { - return optionalMediumTestPlugins.stream() - .filter(plugin -> languageKeys.stream().anyMatch(lang -> plugin.requiredForLanguages().contains(lang))) - .toList(); - } -} 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 new file mode 100644 index 00000000000..e3f282977fa --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/AnalysisResult.java @@ -0,0 +1,226 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import javax.annotation.CheckForNull; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.scanner.fs.InputProject; +import org.sonar.core.util.CloseableIterator; +import org.sonar.scanner.protocol.output.FileStructure; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.Component; +import org.sonar.scanner.protocol.output.ScannerReport.Symbol; +import org.sonar.scanner.protocol.output.ScannerReportReader; +import org.sonar.scanner.report.ScannerReportUtils; +import org.sonar.scanner.scan.SpringProjectScanContainer; +import org.sonar.scanner.scan.filesystem.InputComponentStore; + +public class AnalysisResult implements AnalysisObserver { + + private static final Logger LOG = LoggerFactory.getLogger(AnalysisResult.class); + + private final Map inputFilesByKeys = new HashMap<>(); + private InputProject project; + private ScannerReportReader reader; + + @Override + public void analysisCompleted(SpringProjectScanContainer container) { + LOG.info("Store analysis results in memory for later assertions in medium test"); + FileStructure fileStructure = container.getComponentByType(FileStructure.class); + reader = new ScannerReportReader(fileStructure); + project = container.getComponentByType(InputProject.class); + + storeFs(container); + + } + + public ScannerReportReader getReportReader() { + return reader; + } + + private void storeFs(SpringProjectScanContainer container) { + InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class); + for (InputFile inputPath : inputFileCache.inputFiles()) { + inputFilesByKeys.put(((DefaultInputFile) inputPath).getProjectRelativePath(), inputPath); + } + } + + public Component getReportComponent(InputComponent inputComponent) { + return getReportReader().readComponent(((DefaultInputComponent) inputComponent).scannerId()); + } + + public Component getReportComponent(int scannerId) { + return getReportReader().readComponent(scannerId); + } + + public List issuesFor(InputComponent inputComponent) { + return readFromReport(inputComponent, ScannerReportReader::readComponentIssues); + } + + public List externalIssuesFor(InputComponent inputComponent) { + return readFromReport(inputComponent, ScannerReportReader::readComponentExternalIssues); + } + + public List issuesFor(Component reportComponent) { + return readFromReport(reportComponent, ScannerReportReader::readComponentIssues); + } + + public InputProject project() { + return project; + } + + public Collection inputFiles() { + return inputFilesByKeys.values(); + } + + @CheckForNull + public InputFile inputFile(String relativePath) { + return inputFilesByKeys.get(relativePath); + } + + public Map> allMeasures() { + Map> result = new HashMap<>(); + result.put(project.key(), readFromReport(project, ScannerReportReader::readComponentMeasures)); + for (InputFile inputFile : inputFilesByKeys.values()) { + result.put(inputFile.key(), readFromReport(inputFile, ScannerReportReader::readComponentMeasures)); + } + return result; + } + + /** + * Get highlighting types at a given position in an inputfile + * + * @param lineOffset 0-based offset in file + */ + public List highlightingTypeFor(InputFile file, int line, int lineOffset) { + int ref = ((DefaultInputComponent) file).scannerId(); + if (!reader.hasSyntaxHighlighting(ref)) { + return Collections.emptyList(); + } + TextPointer pointer = file.newPointer(line, lineOffset); + List result = new ArrayList<>(); + try (CloseableIterator it = reader.readComponentSyntaxHighlighting(ref)) { + while (it.hasNext()) { + ScannerReport.SyntaxHighlightingRule rule = it.next(); + TextRange ruleRange = toRange(file, rule.getRange()); + if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) { + result.add(ScannerReportUtils.toBatchType(rule.getType())); + } + } + } catch (Exception e) { + throw new IllegalStateException("Can't read syntax highlighting for " + file, e); + } + return result; + } + + private static TextRange toRange(InputFile file, ScannerReport.TextRange reportRange) { + return file.newRange(file.newPointer(reportRange.getStartLine(), reportRange.getStartOffset()), file.newPointer(reportRange.getEndLine(), reportRange.getEndOffset())); + } + + /** + * Get list of all start positions of a symbol in an inputfile + * + * @param symbolStartLine 0-based start offset for the symbol in file + * @param symbolStartLineOffset 0-based end offset for the symbol in file + */ + @CheckForNull + public List symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) { + int ref = ((DefaultInputComponent) file).scannerId(); + try (CloseableIterator symbols = getReportReader().readComponentSymbols(ref)) { + while (symbols.hasNext()) { + Symbol symbol = symbols.next(); + if (symbol.getDeclaration().getStartLine() == symbolStartLine && symbol.getDeclaration().getStartOffset() == symbolStartLineOffset) { + return symbol.getReferenceList(); + } + } + } + return Collections.emptyList(); + } + + public List duplicationsFor(InputFile file) { + return readFromReport(file, ScannerReportReader::readComponentDuplications); + } + + public List duplicationBlocksFor(InputFile file) { + return readFromReport(file, ScannerReportReader::readCpdTextBlocks); + } + + @CheckForNull + public ScannerReport.LineCoverage coverageFor(InputFile file, int line) { + int ref = ((DefaultInputComponent) file).scannerId(); + try (CloseableIterator it = getReportReader().readComponentCoverage(ref)) { + while (it.hasNext()) { + ScannerReport.LineCoverage coverage = it.next(); + if (coverage.getLine() == line) { + return coverage; + } + } + } catch (Exception e) { + throw new IllegalStateException(e); + } + return null; + } + + public List adHocRules() { + return readFromReport(ScannerReportReader::readAdHocRules); + } + + @NotNull + private List readFromReport(InputComponent component, BiFunction> readerMethod) { + int ref = ((DefaultInputComponent) component).scannerId(); + return readFromReport(r -> readerMethod.apply(r, ref)); + } + + @NotNull + private List readFromReport(Component component, BiFunction> readerMethod) { + int ref = component.getRef(); + return readFromReport(r -> readerMethod.apply(r, ref)); + } + + @NotNull + private List readFromReport(Function> readerMethod) { + List result = new ArrayList<>(); + try (CloseableIterator it = readerMethod.apply(getReportReader())) { + while (it.hasNext()) { + result.add(it.next()); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } + return result; + } +} diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java new file mode 100644 index 00000000000..f2e2f48d556 --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesLoader.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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; + +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Priority; +import org.sonar.api.resources.Languages; +import org.sonar.scanner.repository.language.Language; +import org.sonar.scanner.repository.language.LanguagesLoader; +import org.sonar.scanner.repository.language.SupportedLanguageDto; + +@Priority(1) +public class FakeLanguagesLoader implements LanguagesLoader { + + private final Map languageMap = new HashMap<>(); + + public FakeLanguagesLoader() { + languageMap.put("xoo", new Language(new SupportedLanguageDto("xoo", "xoo", new String[] { ".xoo" }, new String[0]))); + } + + public FakeLanguagesLoader(Languages languages) { + for (org.sonar.api.resources.Language language : languages.all()) { + languageMap.put(language.getKey(), new Language(new SupportedLanguageDto(language.getKey(), language.getName(), language.getFileSuffixes(), language.filenamePatterns()))); + } + } + @Override + public Map load() { + return languageMap; + } + + public void addLanguage(String key, String name, String[] suffixes, String[] filenamePatterns) { + languageMap.put(key, new Language(new SupportedLanguageDto(key, name, suffixes, filenamePatterns))); + } + +} diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java new file mode 100644 index 00000000000..60097466670 --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakeLanguagesProvider.java @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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; + +import javax.annotation.Priority; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; +import org.springframework.context.annotation.Bean; + +@Priority(1) +public class FakeLanguagesProvider { + + private Languages languages = new Languages(); + + @Bean("Languages") + public Languages provide() { + return this.languages; + } + + public void addLanguage(String key, String name, boolean publishAllFiles) { + this.languages.add(new FakeLanguage(key, name, publishAllFiles)); + } + + private static class FakeLanguage implements Language { + + private final String name; + private final String key; + private final boolean publishAllFiles; + + public FakeLanguage(String key, String name, boolean publishAllFiles) { + this.name = name; + this.key = key; + this.publishAllFiles = publishAllFiles; + } + + @Override + public String getKey() { + return this.key; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String[] getFileSuffixes() { + return new String[0]; + } + + @Override + public boolean publishAllFiles() { + return this.publishAllFiles; + } + } + + +} 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 new file mode 100644 index 00000000000..a735827e65d --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/FakePluginInstaller.java @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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; + +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 javax.annotation.Priority; +import org.sonar.api.Plugin; +import org.sonar.core.platform.PluginInfo; +import org.sonar.core.plugin.PluginType; +import org.sonar.scanner.bootstrap.PluginInstaller; +import org.sonar.scanner.bootstrap.ScannerPlugin; + +@Priority(1) +public class FakePluginInstaller implements PluginInstaller { + + private final Map pluginsByKeys = new HashMap<>(); + private final List mediumTestPlugins = new ArrayList<>(); + private final List optionalMediumTestPlugins = new ArrayList<>(); + + public FakePluginInstaller add(String pluginKey, File jarFile, long lastUpdatedAt) { + pluginsByKeys.put(pluginKey, new ScannerPlugin(pluginKey, lastUpdatedAt, PluginType.BUNDLED, PluginInfo.create(jarFile))); + return this; + } + + public FakePluginInstaller add(String pluginKey, Plugin instance) { + mediumTestPlugins.add(new LocalPlugin(pluginKey, instance, Set.of())); + return this; + } + + public FakePluginInstaller addOptional(String pluginKey, Set requiredForLanguages, Plugin instance) { + optionalMediumTestPlugins.add(new LocalPlugin(pluginKey, instance, requiredForLanguages)); + return this; + } + + @Override + public Map installAllPlugins() { + return pluginsByKeys; + } + + @Override + public Map installRequiredPlugins() { + return pluginsByKeys; + } + + @Override + public Map installPluginsForLanguages(Set languageKeys) { + return pluginsByKeys; + } + + @Override + public List installLocals() { + return mediumTestPlugins; + } + + @Override + public List installOptionalLocals(Set languageKeys) { + return optionalMediumTestPlugins.stream() + .filter(plugin -> languageKeys.stream().anyMatch(lang -> plugin.requiredForLanguages().contains(lang))) + .toList(); + } +} 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 new file mode 100644 index 00000000000..59eb6290e79 --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java @@ -0,0 +1,616 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.Priority; +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.rules.ExternalResource; +import org.sonar.api.Plugin; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarProduct; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.rule.LoadedActiveRule; +import org.sonar.api.impl.server.RulesDefinitionContext; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.server.rule.RulesDefinition.Repository; +import org.sonar.api.utils.DateUtils; +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.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.cache.AnalysisCacheLoader; +import org.sonar.scanner.protocol.internal.SensorCacheData; +import org.sonar.scanner.report.CeTaskReportDataHolder; +import org.sonar.scanner.repository.FileData; +import org.sonar.scanner.repository.MetricsRepository; +import org.sonar.scanner.repository.MetricsRepositoryLoader; +import org.sonar.scanner.repository.NewCodePeriodLoader; +import org.sonar.scanner.repository.ProjectRepositories; +import org.sonar.scanner.repository.ProjectRepositoriesLoader; +import org.sonar.scanner.repository.QualityProfileLoader; +import org.sonar.scanner.repository.SingleProjectRepository; +import org.sonar.scanner.repository.settings.GlobalSettingsLoader; +import org.sonar.scanner.repository.settings.ProjectSettingsLoader; +import org.sonar.scanner.rule.ActiveRulesLoader; +import org.sonar.scanner.rule.RulesLoader; +import org.sonar.scanner.scan.ScanProperties; +import org.sonar.scanner.scan.branch.BranchConfiguration; +import org.sonar.scanner.scan.branch.BranchConfigurationLoader; +import org.sonar.scanner.scan.branch.BranchType; +import org.sonar.scanner.scan.branch.ProjectBranches; +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. + */ +public class ScannerMediumTester extends ExternalResource implements BeforeTestExecutionCallback, AfterTestExecutionCallback { + + private static Path userHome = null; + private final Map globalProperties = new HashMap<>(); + private final FakeMetricsRepositoryLoader globalRefProvider = new FakeMetricsRepositoryLoader(); + private final FakeBranchConfigurationLoader branchConfigurationLoader = new FakeBranchConfigurationLoader(); + private final FakeBranchConfiguration branchConfiguration = new FakeBranchConfiguration(); + private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader(); + private final FakePluginInstaller pluginInstaller = new FakePluginInstaller(); + private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); + private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); + private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); + private final FakeAnalysisCacheLoader analysisCacheLoader = new FakeAnalysisCacheLoader(); + private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); + private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); + private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); + private final FakeSonarRuntime sonarRuntime = new FakeSonarRuntime(); + private final CeTaskReportDataHolder reportMetadataHolder = new CeTaskReportDataHolderExt(); + private final FakeLanguagesLoader languagesLoader = new FakeLanguagesLoader(); + private final FakeLanguagesProvider languagesProvider = new FakeLanguagesProvider(); + private LogOutput logOutput = null; + + private static void createWorkingDirs() throws IOException { + destroyWorkingDirs(); + + userHome = java.nio.file.Files.createTempDirectory("mediumtest-userHome"); + } + + private static void destroyWorkingDirs() throws IOException { + if (userHome != null) { + FileUtils.deleteDirectory(userHome.toFile()); + userHome = null; + } + } + + public ScannerMediumTester setLogOutput(LogOutput logOutput) { + this.logOutput = logOutput; + return this; + } + + public ScannerMediumTester registerPlugin(String pluginKey, File location) { + return registerPlugin(pluginKey, location, 1L); + } + + public ScannerMediumTester registerPlugin(String pluginKey, File location, long lastUpdatedAt) { + pluginInstaller.add(pluginKey, location, lastUpdatedAt); + return this; + } + + public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance) { + pluginInstaller.add(pluginKey, instance); + return this; + } + + public ScannerMediumTester registerOptionalPlugin(String pluginKey, Set requiredForLanguages, Plugin instance) { + pluginInstaller.addOptional(pluginKey, requiredForLanguages, instance); + return this; + } + + public ScannerMediumTester registerCoreMetrics() { + for (Metric m : CoreMetrics.getMetrics()) { + registerMetric(m); + } + return this; + } + + public ScannerMediumTester registerMetric(Metric metric) { + globalRefProvider.add(metric); + return this; + } + + public ScannerMediumTester addQProfile(String language, String name) { + qualityProfiles.add(language, name); + return this; + } + + public ScannerMediumTester addRule(Rule rule) { + rulesLoader.addRule(rule); + return this; + } + + public ScannerMediumTester addRule(String key, String repoKey, String internalKey, String name) { + Rule.Builder builder = Rule.newBuilder(); + builder.setKey(key); + builder.setRepo(repoKey); + if (internalKey != null) { + builder.setInternalKey(internalKey); + } + builder.setName(name); + + rulesLoader.addRule(builder.build()); + return this; + } + + public ScannerMediumTester addRules(RulesDefinition rulesDefinition) { + RulesDefinition.Context context = new RulesDefinitionContext(); + rulesDefinition.define(context); + List repositories = context.repositories(); + for (Repository repo : repositories) { + for (RulesDefinition.Rule rule : repo.rules()) { + this.addRule(rule.key(), rule.repository().key(), rule.internalKey(), rule.name()); + } + } + return this; + } + + public ScannerMediumTester addDefaultQProfile(String language, String name) { + addQProfile(language, name); + return this; + } + + public ScannerMediumTester bootstrapProperties(Map props) { + globalProperties.putAll(props); + return this; + } + + public ScannerMediumTester activateRule(LoadedActiveRule activeRule) { + activeRules.addActiveRule(activeRule); + return this; + } + + public ScannerMediumTester addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity, + @Nullable String internalKey, @Nullable String language) { + LoadedActiveRule r = new LoadedActiveRule(); + + r.setInternalKey(internalKey); + r.setRuleKey(RuleKey.of(repositoryKey, ruleKey)); + r.setName(name); + r.setTemplateRuleKey(templateRuleKey); + r.setLanguage(language); + r.setSeverity(severity); + r.setDeprecatedKeys(emptySet()); + + activeRules.addActiveRule(r); + return this; + } + + public ScannerMediumTester addFileData(String path, FileData fileData) { + projectRefProvider.addFileData(path, fileData); + return this; + } + + public ScannerMediumTester addGlobalServerSettings(String key, String value) { + globalSettingsLoader.getGlobalSettings().put(key, value); + return this; + } + + public ScannerMediumTester addProjectServerSettings(String key, String value) { + projectSettingsLoader.getProjectSettings().put(key, value); + return this; + } + + public ScannerMediumTester setNewCodePeriod(NewCodePeriods.NewCodePeriodType type, String value) { + newCodePeriodLoader.set(NewCodePeriods.ShowWSResponse.newBuilder().setType(type).setValue(value).build()); + return this; + } + + @Override + public void afterTestExecution(ExtensionContext extensionContext) { + after(); + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) { + before(); + } + + @Override + protected void before() { + try { + createWorkingDirs(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + registerCoreMetrics(); + globalProperties.put(GlobalAnalysisMode.MEDIUM_TEST_ENABLED, "true"); + globalProperties.put(ScanProperties.KEEP_REPORT_PROP_KEY, "true"); + globalProperties.put("sonar.userHome", userHome.toString()); + } + + @Override + protected void after() { + try { + destroyWorkingDirs(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + + public AnalysisBuilder newAnalysis() { + return new AnalysisBuilder(this); + } + + public AnalysisBuilder newAnalysis(File sonarProps) { + Properties prop = new Properties(); + try (Reader reader = new InputStreamReader(new FileInputStream(sonarProps), StandardCharsets.UTF_8)) { + prop.load(reader); + } catch (Exception e) { + throw new IllegalStateException("Unable to read configuration file", e); + } + AnalysisBuilder builder = new AnalysisBuilder(this); + builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath()); + for (Map.Entry entry : prop.entrySet()) { + builder.property(entry.getKey().toString(), entry.getValue().toString()); + } + return builder; + } + + public void addLanguage(String key, String name, String... suffixes) { + languagesLoader.addLanguage(key, name, suffixes, new String[0]); + languagesProvider.addLanguage(key, name, true); + } + + public void addLanguage(String key, String name, boolean publishAllFiles, String... suffixes) { + languagesLoader.addLanguage(key, name, suffixes, new String[0]); + languagesProvider.addLanguage(key, name, publishAllFiles); + } + + public static class AnalysisBuilder { + private final Map taskProperties = new HashMap<>(); + private final ScannerMediumTester tester; + + public AnalysisBuilder(ScannerMediumTester tester) { + this.tester = tester; + } + + public AnalysisResult execute() { + AnalysisResult result = new AnalysisResult(); + Map props = new HashMap<>(); + props.putAll(tester.globalProperties); + props.putAll(taskProperties); + + Batch.Builder builder = Batch.builder() + .setGlobalProperties(props) + .setEnableLoggingConfiguration(true) + .addComponents(new EnvironmentInformation("mediumTest", "1.0"), + tester.pluginInstaller, + tester.globalRefProvider, + tester.qualityProfiles, + tester.rulesLoader, + tester.branchConfigurationLoader, + tester.projectRefProvider, + tester.activeRules, + tester.globalSettingsLoader, + tester.projectSettingsLoader, + tester.newCodePeriodLoader, + tester.analysisCacheLoader, + tester.sonarRuntime, + tester.reportMetadataHolder, + tester.languagesLoader, + tester.languagesProvider, + result); + if (tester.logOutput != null) { + builder.setLogOutput(tester.logOutput); + } else { + builder.setEnableLoggingConfiguration(false); + } + builder.build().execute(); + + return result; + } + + public AnalysisBuilder properties(Map props) { + taskProperties.putAll(props); + return this; + } + + public AnalysisBuilder property(String key, String value) { + taskProperties.put(key, value); + return this; + } + + } + + @Priority(1) + private static class FakeRulesLoader implements RulesLoader { + private List rules = new LinkedList<>(); + + public FakeRulesLoader addRule(Rule rule) { + rules.add(rule); + return this; + } + + @Override + public List load() { + return rules; + } + } + + @Priority(1) + private static class FakeActiveRulesLoader implements ActiveRulesLoader { + private List activeRules = new LinkedList<>(); + + public void addActiveRule(LoadedActiveRule activeRule) { + this.activeRules.add(activeRule); + } + + @Override + public List load(String qualityProfileKey) { + return activeRules; + } + } + + @Priority(1) + private static class FakeMetricsRepositoryLoader implements MetricsRepositoryLoader { + + private int metricId = 1; + + private List metrics = new ArrayList<>(); + + @Override + public MetricsRepository load() { + return new MetricsRepository(metrics); + } + + public FakeMetricsRepositoryLoader add(Metric metric) { + metric.setUuid("metric" + metricId++); + metrics.add(metric); + metricId++; + return this; + } + + } + + @Priority(1) + private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader { + private Map fileDataMap = new HashMap<>(); + + @Override + public ProjectRepositories load(String projectKey, @Nullable String branchBase) { + return new SingleProjectRepository(fileDataMap); + } + + public FakeProjectRepositoriesLoader addFileData(String path, FileData fileData) { + fileDataMap.put(path, fileData); + return this; + } + + } + + @Priority(1) + private static class FakeBranchConfiguration implements BranchConfiguration { + + private BranchType branchType = BranchType.BRANCH; + private String branchName = null; + private String branchTarget = null; + private String referenceBranchName = null; + + @Override + public BranchType branchType() { + return branchType; + } + + @CheckForNull + @Override + public String branchName() { + return branchName; + } + + @CheckForNull + @Override + public String targetBranchName() { + return branchTarget; + } + + @CheckForNull + @Override + public String referenceBranchName() { + return referenceBranchName; + } + + @Override + public String pullRequestKey() { + return "1'"; + } + } + + @Priority(1) + private static class FakeSonarRuntime implements SonarRuntime { + + private SonarEdition edition; + + FakeSonarRuntime() { + this.edition = SonarEdition.COMMUNITY; + } + + @Override + public Version getApiVersion() { + return Version.create(7, 8); + } + + @Override + public SonarProduct getProduct() { + return SonarProduct.SONARQUBE; + } + + @Override + public SonarQubeSide getSonarQubeSide() { + return SonarQubeSide.SCANNER; + } + + @Override + public SonarEdition getEdition() { + return edition; + } + + public void setEdition(SonarEdition edition) { + this.edition = edition; + } + } + + public ScannerMediumTester setBranchType(BranchType branchType) { + branchConfiguration.branchType = branchType; + return this; + } + + public ScannerMediumTester setBranchName(String branchName) { + this.branchConfiguration.branchName = branchName; + return this; + } + + public ScannerMediumTester setBranchTarget(String branchTarget) { + this.branchConfiguration.branchTarget = branchTarget; + return this; + } + + public ScannerMediumTester setReferenceBranchName(String referenceBranchNam) { + this.branchConfiguration.referenceBranchName = referenceBranchNam; + return this; + } + + public ScannerMediumTester setEdition(SonarEdition edition) { + this.sonarRuntime.setEdition(edition); + return this; + } + + @Priority(1) + private class FakeBranchConfigurationLoader implements BranchConfigurationLoader { + @Override + public BranchConfiguration load(Map projectSettings, ProjectBranches branches) { + return branchConfiguration; + } + } + + @Priority(1) + private static class FakeQualityProfileLoader implements QualityProfileLoader { + + private List qualityProfiles = new LinkedList<>(); + + public void add(String language, String name) { + qualityProfiles.add(QualityProfile.newBuilder() + .setLanguage(language) + .setKey(name) + .setName(name) + .setRulesUpdatedAt(DateUtils.formatDateTime(new Date(1234567891212L))) + .build()); + } + + @Override + public List load(String projectKey) { + return qualityProfiles; + } + } + + @Priority(1) + private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader { + @Override + public Optional load() { + return Optional.empty(); + } + } + + @Priority(1) + private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { + + private Map globalSettings = new HashMap<>(); + + public Map getGlobalSettings() { + return globalSettings; + } + + @Override + public Map loadGlobalSettings() { + return Collections.unmodifiableMap(globalSettings); + } + } + + @Priority(1) + private static class FakeNewCodePeriodLoader implements NewCodePeriodLoader { + private NewCodePeriods.ShowWSResponse response; + + @Override + public NewCodePeriods.ShowWSResponse load(String projectKey, String branchName) { + return response; + } + + public void set(NewCodePeriods.ShowWSResponse response) { + this.response = response; + } + } + + @Priority(1) + private static class FakeProjectSettingsLoader implements ProjectSettingsLoader { + + private Map projectSettings = new HashMap<>(); + + public Map getProjectSettings() { + return projectSettings; + } + + @Override + public Map loadProjectSettings() { + return Collections.unmodifiableMap(projectSettings); + } + } + + @Priority(1) + private static class CeTaskReportDataHolderExt extends CeTaskReportDataHolder { + + } + +} diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/package-info.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/package-info.java new file mode 100644 index 00000000000..ec4d264d1e0 --- /dev/null +++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.mediumtest; + +import javax.annotation.ParametersAreNonnullByDefault;