You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ScannerMediumTester.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.scanner.mediumtest;
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.IOException;
  24. import java.io.InputStreamReader;
  25. import java.io.Reader;
  26. import java.nio.charset.StandardCharsets;
  27. import java.nio.file.Path;
  28. import java.util.ArrayList;
  29. import java.util.Collections;
  30. import java.util.Date;
  31. import java.util.HashMap;
  32. import java.util.LinkedList;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Optional;
  36. import java.util.Properties;
  37. import javax.annotation.CheckForNull;
  38. import javax.annotation.Nullable;
  39. import javax.annotation.Priority;
  40. import org.apache.commons.io.FileUtils;
  41. import org.junit.rules.ExternalResource;
  42. import org.sonar.api.Plugin;
  43. import org.sonar.api.SonarEdition;
  44. import org.sonar.api.SonarProduct;
  45. import org.sonar.api.SonarQubeSide;
  46. import org.sonar.api.SonarRuntime;
  47. import org.sonar.api.batch.rule.LoadedActiveRule;
  48. import org.sonar.api.batch.sensor.cache.ReadCache;
  49. import org.sonar.api.impl.server.RulesDefinitionContext;
  50. import org.sonar.api.measures.CoreMetrics;
  51. import org.sonar.api.measures.Metric;
  52. import org.sonar.api.rule.RuleKey;
  53. import org.sonar.api.server.rule.RulesDefinition;
  54. import org.sonar.api.server.rule.RulesDefinition.Repository;
  55. import org.sonar.api.utils.DateUtils;
  56. import org.sonar.api.utils.Version;
  57. import org.sonar.batch.bootstrapper.Batch;
  58. import org.sonar.batch.bootstrapper.EnvironmentInformation;
  59. import org.sonar.batch.bootstrapper.LogOutput;
  60. import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
  61. import org.sonar.scanner.cache.AnalysisCacheLoader;
  62. import org.sonar.scanner.cache.AnalysisCacheMemoryStorage;
  63. import org.sonar.scanner.cache.ReadCacheImpl;
  64. import org.sonar.scanner.protocol.internal.ScannerInternal;
  65. import org.sonar.scanner.report.CeTaskReportDataHolder;
  66. import org.sonar.scanner.repository.FileData;
  67. import org.sonar.scanner.repository.MetricsRepository;
  68. import org.sonar.scanner.repository.MetricsRepositoryLoader;
  69. import org.sonar.scanner.repository.NewCodePeriodLoader;
  70. import org.sonar.scanner.repository.ProjectRepositories;
  71. import org.sonar.scanner.repository.ProjectRepositoriesLoader;
  72. import org.sonar.scanner.repository.QualityProfileLoader;
  73. import org.sonar.scanner.repository.SingleProjectRepository;
  74. import org.sonar.scanner.repository.settings.GlobalSettingsLoader;
  75. import org.sonar.scanner.repository.settings.ProjectSettingsLoader;
  76. import org.sonar.scanner.rule.ActiveRulesLoader;
  77. import org.sonar.scanner.rule.RulesLoader;
  78. import org.sonar.scanner.scan.ScanProperties;
  79. import org.sonar.scanner.scan.branch.BranchConfiguration;
  80. import org.sonar.scanner.scan.branch.BranchConfigurationLoader;
  81. import org.sonar.scanner.scan.branch.BranchType;
  82. import org.sonar.scanner.scan.branch.ProjectBranches;
  83. import org.sonar.scanner.scan.branch.ProjectPullRequests;
  84. import org.sonarqube.ws.NewCodePeriods;
  85. import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
  86. import org.sonarqube.ws.Rules.ListResponse.Rule;
  87. import org.springframework.context.annotation.Bean;
  88. import static java.util.Collections.emptySet;
  89. /**
  90. * Main utility class for writing scanner medium tests.
  91. */
  92. public class ScannerMediumTester extends ExternalResource {
  93. private static Path userHome = null;
  94. private final Map<String, String> globalProperties = new HashMap<>();
  95. private final FakeMetricsRepositoryLoader globalRefProvider = new FakeMetricsRepositoryLoader();
  96. private final FakeBranchConfigurationLoader branchConfigurationLoader = new FakeBranchConfigurationLoader();
  97. private final FakeBranchConfiguration branchConfiguration = new FakeBranchConfiguration();
  98. private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader();
  99. private final FakePluginInstaller pluginInstaller = new FakePluginInstaller();
  100. private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader();
  101. private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader();
  102. private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader();
  103. private final FakeAnalysisCacheLoader analysisCacheLoader = new FakeAnalysisCacheLoader();
  104. private final FakeRulesLoader rulesLoader = new FakeRulesLoader();
  105. private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader();
  106. private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader();
  107. private final FakeSonarRuntime sonarRuntime = new FakeSonarRuntime();
  108. private final CeTaskReportDataHolder reportMetadataHolder = new CeTaskReportDataHolderExt();
  109. private LogOutput logOutput = null;
  110. private static void createWorkingDirs() throws IOException {
  111. destroyWorkingDirs();
  112. userHome = java.nio.file.Files.createTempDirectory("mediumtest-userHome");
  113. }
  114. private static void destroyWorkingDirs() throws IOException {
  115. if (userHome != null) {
  116. FileUtils.deleteDirectory(userHome.toFile());
  117. userHome = null;
  118. }
  119. }
  120. public ScannerMediumTester setLogOutput(LogOutput logOutput) {
  121. this.logOutput = logOutput;
  122. return this;
  123. }
  124. public ScannerMediumTester registerPlugin(String pluginKey, File location) {
  125. return registerPlugin(pluginKey, location, 1L);
  126. }
  127. public ScannerMediumTester registerPlugin(String pluginKey, File location, long lastUpdatedAt) {
  128. pluginInstaller.add(pluginKey, location, lastUpdatedAt);
  129. return this;
  130. }
  131. public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance) {
  132. return registerPlugin(pluginKey, instance, 1L);
  133. }
  134. public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance, long lastUpdatedAt) {
  135. pluginInstaller.add(pluginKey, instance, lastUpdatedAt);
  136. return this;
  137. }
  138. public ScannerMediumTester registerCoreMetrics() {
  139. for (Metric<?> m : CoreMetrics.getMetrics()) {
  140. registerMetric(m);
  141. }
  142. return this;
  143. }
  144. public ScannerMediumTester registerMetric(Metric<?> metric) {
  145. globalRefProvider.add(metric);
  146. return this;
  147. }
  148. public ScannerMediumTester addQProfile(String language, String name) {
  149. qualityProfiles.add(language, name);
  150. return this;
  151. }
  152. public ScannerMediumTester addRule(Rule rule) {
  153. rulesLoader.addRule(rule);
  154. return this;
  155. }
  156. public ScannerMediumTester addRule(String key, String repoKey, String internalKey, String name) {
  157. Rule.Builder builder = Rule.newBuilder();
  158. builder.setKey(key);
  159. builder.setRepository(repoKey);
  160. if (internalKey != null) {
  161. builder.setInternalKey(internalKey);
  162. }
  163. builder.setName(name);
  164. rulesLoader.addRule(builder.build());
  165. return this;
  166. }
  167. public ScannerMediumTester addRules(RulesDefinition rulesDefinition) {
  168. RulesDefinition.Context context = new RulesDefinitionContext();
  169. rulesDefinition.define(context);
  170. List<Repository> repositories = context.repositories();
  171. for (Repository repo : repositories) {
  172. for (RulesDefinition.Rule rule : repo.rules()) {
  173. this.addRule(rule.key(), rule.repository().key(), rule.internalKey(), rule.name());
  174. }
  175. }
  176. return this;
  177. }
  178. public ScannerMediumTester addDefaultQProfile(String language, String name) {
  179. addQProfile(language, name);
  180. return this;
  181. }
  182. public ScannerMediumTester bootstrapProperties(Map<String, String> props) {
  183. globalProperties.putAll(props);
  184. return this;
  185. }
  186. public ScannerMediumTester activateRule(LoadedActiveRule activeRule) {
  187. activeRules.addActiveRule(activeRule);
  188. return this;
  189. }
  190. public ScannerMediumTester addActiveRule(String repositoryKey, String ruleKey, @Nullable String templateRuleKey, String name, @Nullable String severity,
  191. @Nullable String internalKey, @Nullable String language) {
  192. LoadedActiveRule r = new LoadedActiveRule();
  193. r.setInternalKey(internalKey);
  194. r.setRuleKey(RuleKey.of(repositoryKey, ruleKey));
  195. r.setName(name);
  196. r.setTemplateRuleKey(templateRuleKey);
  197. r.setLanguage(language);
  198. r.setSeverity(severity);
  199. r.setDeprecatedKeys(emptySet());
  200. activeRules.addActiveRule(r);
  201. return this;
  202. }
  203. public ScannerMediumTester addFileData(String path, FileData fileData) {
  204. projectRefProvider.addFileData(path, fileData);
  205. return this;
  206. }
  207. public ScannerMediumTester addGlobalServerSettings(String key, String value) {
  208. globalSettingsLoader.getGlobalSettings().put(key, value);
  209. return this;
  210. }
  211. public ScannerMediumTester addProjectServerSettings(String key, String value) {
  212. projectSettingsLoader.getProjectSettings().put(key, value);
  213. return this;
  214. }
  215. public ScannerMediumTester setNewCodePeriod(NewCodePeriods.NewCodePeriodType type, String value) {
  216. newCodePeriodLoader.set(NewCodePeriods.ShowWSResponse.newBuilder().setType(type).setValue(value).build());
  217. return this;
  218. }
  219. @Override
  220. protected void before() {
  221. try {
  222. createWorkingDirs();
  223. } catch (IOException e) {
  224. throw new IllegalStateException(e);
  225. }
  226. registerCoreMetrics();
  227. globalProperties.put(GlobalAnalysisMode.MEDIUM_TEST_ENABLED, "true");
  228. globalProperties.put(ScanProperties.KEEP_REPORT_PROP_KEY, "true");
  229. globalProperties.put("sonar.userHome", userHome.toString());
  230. }
  231. @Override
  232. protected void after() {
  233. try {
  234. destroyWorkingDirs();
  235. } catch (IOException e) {
  236. throw new IllegalStateException(e);
  237. }
  238. }
  239. public AnalysisBuilder newAnalysis() {
  240. return new AnalysisBuilder(this);
  241. }
  242. public AnalysisBuilder newAnalysis(File sonarProps) {
  243. Properties prop = new Properties();
  244. try (Reader reader = new InputStreamReader(new FileInputStream(sonarProps), StandardCharsets.UTF_8)) {
  245. prop.load(reader);
  246. } catch (Exception e) {
  247. throw new IllegalStateException("Unable to read configuration file", e);
  248. }
  249. AnalysisBuilder builder = new AnalysisBuilder(this);
  250. builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath());
  251. for (Map.Entry<Object, Object> entry : prop.entrySet()) {
  252. builder.property(entry.getKey().toString(), entry.getValue().toString());
  253. }
  254. return builder;
  255. }
  256. public static class AnalysisBuilder {
  257. private final Map<String, String> taskProperties = new HashMap<>();
  258. private final ScannerMediumTester tester;
  259. public AnalysisBuilder(ScannerMediumTester tester) {
  260. this.tester = tester;
  261. }
  262. public AnalysisResult execute() {
  263. AnalysisResult result = new AnalysisResult();
  264. Map<String, String> props = new HashMap<>();
  265. props.putAll(tester.globalProperties);
  266. props.putAll(taskProperties);
  267. Batch.builder()
  268. .setGlobalProperties(props)
  269. .setEnableLoggingConfiguration(true)
  270. .addComponents(new EnvironmentInformation("mediumTest", "1.0"),
  271. tester.pluginInstaller,
  272. tester.globalRefProvider,
  273. tester.qualityProfiles,
  274. tester.rulesLoader,
  275. tester.branchConfigurationLoader,
  276. tester.projectRefProvider,
  277. tester.activeRules,
  278. tester.globalSettingsLoader,
  279. tester.projectSettingsLoader,
  280. tester.newCodePeriodLoader,
  281. tester.analysisCacheLoader,
  282. tester.sonarRuntime,
  283. tester.reportMetadataHolder,
  284. result)
  285. .setLogOutput(tester.logOutput)
  286. .build().execute();
  287. return result;
  288. }
  289. public AnalysisBuilder properties(Map<String, String> props) {
  290. taskProperties.putAll(props);
  291. return this;
  292. }
  293. public AnalysisBuilder property(String key, String value) {
  294. taskProperties.put(key, value);
  295. return this;
  296. }
  297. }
  298. @Priority(1)
  299. private static class FakeRulesLoader implements RulesLoader {
  300. private List<org.sonarqube.ws.Rules.ListResponse.Rule> rules = new LinkedList<>();
  301. public FakeRulesLoader addRule(Rule rule) {
  302. rules.add(rule);
  303. return this;
  304. }
  305. @Override
  306. public List<Rule> load() {
  307. return rules;
  308. }
  309. }
  310. @Priority(1)
  311. private static class FakeActiveRulesLoader implements ActiveRulesLoader {
  312. private List<LoadedActiveRule> activeRules = new LinkedList<>();
  313. public void addActiveRule(LoadedActiveRule activeRule) {
  314. this.activeRules.add(activeRule);
  315. }
  316. @Override
  317. public List<LoadedActiveRule> load(String qualityProfileKey) {
  318. return activeRules;
  319. }
  320. }
  321. @Priority(1)
  322. private static class FakeMetricsRepositoryLoader implements MetricsRepositoryLoader {
  323. private int metricId = 1;
  324. private List<Metric> metrics = new ArrayList<>();
  325. @Override
  326. public MetricsRepository load() {
  327. return new MetricsRepository(metrics);
  328. }
  329. public FakeMetricsRepositoryLoader add(Metric<?> metric) {
  330. metric.setUuid("metric" + metricId++);
  331. metrics.add(metric);
  332. metricId++;
  333. return this;
  334. }
  335. }
  336. @Priority(1)
  337. private static class FakeProjectRepositoriesLoader implements ProjectRepositoriesLoader {
  338. private Map<String, FileData> fileDataMap = new HashMap<>();
  339. @Override
  340. public ProjectRepositories load(String projectKey, @Nullable String branchBase) {
  341. return new SingleProjectRepository(fileDataMap);
  342. }
  343. public FakeProjectRepositoriesLoader addFileData(String path, FileData fileData) {
  344. fileDataMap.put(path, fileData);
  345. return this;
  346. }
  347. }
  348. @Priority(1)
  349. private static class FakeBranchConfiguration implements BranchConfiguration {
  350. private BranchType branchType = BranchType.BRANCH;
  351. private String branchName = null;
  352. private String branchTarget = null;
  353. private String referenceBranchName = null;
  354. @Override
  355. public BranchType branchType() {
  356. return branchType;
  357. }
  358. @CheckForNull
  359. @Override
  360. public String branchName() {
  361. return branchName;
  362. }
  363. @CheckForNull
  364. @Override
  365. public String targetBranchName() {
  366. return branchTarget;
  367. }
  368. @CheckForNull
  369. @Override
  370. public String referenceBranchName() {
  371. return referenceBranchName;
  372. }
  373. @Override
  374. public String pullRequestKey() {
  375. return "1'";
  376. }
  377. }
  378. @Priority(1)
  379. private static class FakeSonarRuntime implements SonarRuntime {
  380. private SonarEdition edition;
  381. FakeSonarRuntime() {
  382. this.edition = SonarEdition.COMMUNITY;
  383. }
  384. @Override
  385. public Version getApiVersion() {
  386. return Version.create(7, 8);
  387. }
  388. @Override
  389. public SonarProduct getProduct() {
  390. return SonarProduct.SONARQUBE;
  391. }
  392. @Override
  393. public SonarQubeSide getSonarQubeSide() {
  394. return SonarQubeSide.SCANNER;
  395. }
  396. @Override
  397. public SonarEdition getEdition() {
  398. return edition;
  399. }
  400. public void setEdition(SonarEdition edition) {
  401. this.edition = edition;
  402. }
  403. }
  404. public ScannerMediumTester setBranchType(BranchType branchType) {
  405. branchConfiguration.branchType = branchType;
  406. return this;
  407. }
  408. public ScannerMediumTester setBranchName(String branchName) {
  409. this.branchConfiguration.branchName = branchName;
  410. return this;
  411. }
  412. public ScannerMediumTester setBranchTarget(String branchTarget) {
  413. this.branchConfiguration.branchTarget = branchTarget;
  414. return this;
  415. }
  416. public ScannerMediumTester setReferenceBranchName(String referenceBranchNam) {
  417. this.branchConfiguration.referenceBranchName = referenceBranchNam;
  418. return this;
  419. }
  420. public ScannerMediumTester setEdition(SonarEdition edition) {
  421. this.sonarRuntime.setEdition(edition);
  422. return this;
  423. }
  424. @Priority(1)
  425. private class FakeBranchConfigurationLoader implements BranchConfigurationLoader {
  426. @Override
  427. public BranchConfiguration load(Map<String, String> projectSettings, ProjectBranches branches, ProjectPullRequests pullRequests) {
  428. return branchConfiguration;
  429. }
  430. }
  431. @Priority(1)
  432. private static class FakeQualityProfileLoader implements QualityProfileLoader {
  433. private List<QualityProfile> qualityProfiles = new LinkedList<>();
  434. public void add(String language, String name) {
  435. qualityProfiles.add(QualityProfile.newBuilder()
  436. .setLanguage(language)
  437. .setKey(name)
  438. .setName(name)
  439. .setRulesUpdatedAt(DateUtils.formatDateTime(new Date(1234567891212L)))
  440. .build());
  441. }
  442. @Override
  443. public List<QualityProfile> load(String projectKey) {
  444. return qualityProfiles;
  445. }
  446. }
  447. @Priority(1)
  448. private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader {
  449. @Override
  450. public Optional<ScannerInternal.AnalysisCacheMsg> load() {
  451. return Optional.empty();
  452. }
  453. }
  454. @Priority(1)
  455. private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader {
  456. private Map<String, String> globalSettings = new HashMap<>();
  457. public Map<String, String> getGlobalSettings() {
  458. return globalSettings;
  459. }
  460. @Override
  461. public Map<String, String> loadGlobalSettings() {
  462. return Collections.unmodifiableMap(globalSettings);
  463. }
  464. }
  465. @Priority(1)
  466. private static class FakeNewCodePeriodLoader implements NewCodePeriodLoader {
  467. private NewCodePeriods.ShowWSResponse response;
  468. @Override
  469. public NewCodePeriods.ShowWSResponse load(String projectKey, String branchName) {
  470. return response;
  471. }
  472. public void set(NewCodePeriods.ShowWSResponse response) {
  473. this.response = response;
  474. }
  475. }
  476. @Priority(1)
  477. private static class FakeProjectSettingsLoader implements ProjectSettingsLoader {
  478. private Map<String, String> projectSettings = new HashMap<>();
  479. public Map<String, String> getProjectSettings() {
  480. return projectSettings;
  481. }
  482. @Override
  483. public Map<String, String> loadProjectSettings() {
  484. return Collections.unmodifiableMap(projectSettings);
  485. }
  486. }
  487. @Priority(1)
  488. private static class CeTaskReportDataHolderExt extends CeTaskReportDataHolder {
  489. }
  490. }