diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2024-09-04 11:46:59 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-12 20:02:54 +0000 |
commit | 1d44e3b465b8df3ad3a0c63d17b2b24ef529fd79 (patch) | |
tree | 3b86b40a8d1d656b934dd284f7eadc007c61661f /sonar-scanner-engine/src/testFixtures/java/org | |
parent | e56fc5a6aa170161d32e171cf3b499a691924bd2 (diff) | |
download | sonarqube-1d44e3b465b8df3ad3a0c63d17b2b24ef529fd79.tar.gz sonarqube-1d44e3b465b8df3ad3a0c63d17b2b24ef529fd79.zip |
SONAR-22914 Move the ScannerMediumTester to testFixtures
Diffstat (limited to 'sonar-scanner-engine/src/testFixtures/java/org')
6 files changed, 1076 insertions, 0 deletions
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<String, InputFile> 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<ScannerReport.Issue> issuesFor(InputComponent inputComponent) { + return readFromReport(inputComponent, ScannerReportReader::readComponentIssues); + } + + public List<ScannerReport.ExternalIssue> externalIssuesFor(InputComponent inputComponent) { + return readFromReport(inputComponent, ScannerReportReader::readComponentExternalIssues); + } + + public List<ScannerReport.Issue> issuesFor(Component reportComponent) { + return readFromReport(reportComponent, ScannerReportReader::readComponentIssues); + } + + public InputProject project() { + return project; + } + + public Collection<InputFile> inputFiles() { + return inputFilesByKeys.values(); + } + + @CheckForNull + public InputFile inputFile(String relativePath) { + return inputFilesByKeys.get(relativePath); + } + + public Map<String, List<ScannerReport.Measure>> allMeasures() { + Map<String, List<ScannerReport.Measure>> 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<TypeOfText> 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<TypeOfText> result = new ArrayList<>(); + try (CloseableIterator<ScannerReport.SyntaxHighlightingRule> 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<ScannerReport.TextRange> symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) { + int ref = ((DefaultInputComponent) file).scannerId(); + try (CloseableIterator<Symbol> 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<ScannerReport.Duplication> duplicationsFor(InputFile file) { + return readFromReport(file, ScannerReportReader::readComponentDuplications); + } + + public List<ScannerReport.CpdTextBlock> duplicationBlocksFor(InputFile file) { + return readFromReport(file, ScannerReportReader::readCpdTextBlocks); + } + + @CheckForNull + public ScannerReport.LineCoverage coverageFor(InputFile file, int line) { + int ref = ((DefaultInputComponent) file).scannerId(); + try (CloseableIterator<ScannerReport.LineCoverage> 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<ScannerReport.AdHocRule> adHocRules() { + return readFromReport(ScannerReportReader::readAdHocRules); + } + + @NotNull + private <G> List<G> readFromReport(InputComponent component, BiFunction<ScannerReportReader, Integer, CloseableIterator<G>> readerMethod) { + int ref = ((DefaultInputComponent) component).scannerId(); + return readFromReport(r -> readerMethod.apply(r, ref)); + } + + @NotNull + private <G> List<G> readFromReport(Component component, BiFunction<ScannerReportReader, Integer, CloseableIterator<G>> readerMethod) { + int ref = component.getRef(); + return readFromReport(r -> readerMethod.apply(r, ref)); + } + + @NotNull + private <G> List<G> readFromReport(Function<ScannerReportReader, CloseableIterator<G>> readerMethod) { + List<G> result = new ArrayList<>(); + try (CloseableIterator<G> 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<String, Language> 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<String, Language> 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<String, ScannerPlugin> pluginsByKeys = new HashMap<>(); + private final List<LocalPlugin> mediumTestPlugins = new ArrayList<>(); + private final List<LocalPlugin> 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<String> requiredForLanguages, Plugin instance) { + optionalMediumTestPlugins.add(new LocalPlugin(pluginKey, instance, requiredForLanguages)); + return this; + } + + @Override + public Map<String, ScannerPlugin> installAllPlugins() { + return pluginsByKeys; + } + + @Override + public Map<String, ScannerPlugin> installRequiredPlugins() { + return pluginsByKeys; + } + + @Override + public Map<String, ScannerPlugin> installPluginsForLanguages(Set<String> languageKeys) { + return pluginsByKeys; + } + + @Override + public List<LocalPlugin> installLocals() { + return mediumTestPlugins; + } + + @Override + public List<LocalPlugin> installOptionalLocals(Set<String> 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<String, String> 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<String> 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<Repository> 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<String, String> 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<Object, Object> 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<String, String> taskProperties = new HashMap<>(); + private final ScannerMediumTester tester; + + public AnalysisBuilder(ScannerMediumTester tester) { + this.tester = tester; + } + + public AnalysisResult execute() { + AnalysisResult result = new AnalysisResult(); + Map<String, String> 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<String, String> 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<Rule> rules = new LinkedList<>(); + + public FakeRulesLoader addRule(Rule rule) { + rules.add(rule); + return this; + } + + @Override + public List<Rule> load() { + return rules; + } + } + + @Priority(1) + private static class FakeActiveRulesLoader implements ActiveRulesLoader { + private List<LoadedActiveRule> activeRules = new LinkedList<>(); + + public void addActiveRule(LoadedActiveRule activeRule) { + this.activeRules.add(activeRule); + } + + @Override + public List<LoadedActiveRule> load(String qualityProfileKey) { + return activeRules; + } + } + + @Priority(1) + private static class FakeMetricsRepositoryLoader implements MetricsRepositoryLoader { + + private int metricId = 1; + + private List<Metric> 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<String, FileData> 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<String, String> projectSettings, ProjectBranches branches) { + return branchConfiguration; + } + } + + @Priority(1) + private static class FakeQualityProfileLoader implements QualityProfileLoader { + + private List<QualityProfile> 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<QualityProfile> load(String projectKey) { + return qualityProfiles; + } + } + + @Priority(1) + private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader { + @Override + public Optional<SensorCacheData> load() { + return Optional.empty(); + } + } + + @Priority(1) + private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { + + private Map<String, String> globalSettings = new HashMap<>(); + + public Map<String, String> getGlobalSettings() { + return globalSettings; + } + + @Override + public Map<String, String> 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<String, String> projectSettings = new HashMap<>(); + + public Map<String, String> getProjectSettings() { + return projectSettings; + } + + @Override + public Map<String, String> 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; |