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.

LoadPeriodsStepIT.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.ce.task.projectanalysis.step;
  21. import com.tngtech.java.junit.dataprovider.DataProvider;
  22. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  23. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  24. import java.time.ZoneId;
  25. import java.time.ZonedDateTime;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.List;
  29. import java.util.Optional;
  30. import java.util.Random;
  31. import java.util.stream.Stream;
  32. import javax.annotation.Nullable;
  33. import org.junit.Before;
  34. import org.junit.Rule;
  35. import org.junit.Test;
  36. import org.junit.runner.RunWith;
  37. import org.slf4j.event.Level;
  38. import org.sonar.api.testfixtures.log.LogTester;
  39. import org.sonar.api.utils.MessageException;
  40. import org.sonar.api.utils.System2;
  41. import org.sonar.ce.task.log.CeTaskMessages;
  42. import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
  43. import org.sonar.ce.task.projectanalysis.component.Component;
  44. import org.sonar.ce.task.projectanalysis.component.ReportComponent;
  45. import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
  46. import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver;
  47. import org.sonar.ce.task.projectanalysis.period.Period;
  48. import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl;
  49. import org.sonar.ce.task.step.ComputationStep;
  50. import org.sonar.ce.task.step.TestComputationStepContext;
  51. import org.sonar.core.util.SequenceUuidFactory;
  52. import org.sonar.db.DbTester;
  53. import org.sonar.db.component.ComponentDto;
  54. import org.sonar.db.component.SnapshotDto;
  55. import org.sonar.db.event.EventTesting;
  56. import org.sonar.db.newcodeperiod.NewCodePeriodDao;
  57. import org.sonar.db.newcodeperiod.NewCodePeriodType;
  58. import org.sonar.server.project.Project;
  59. import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
  60. import static org.assertj.core.api.Assertions.assertThat;
  61. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  62. import static org.assertj.core.api.Assertions.fail;
  63. import static org.mockito.ArgumentMatchers.any;
  64. import static org.mockito.Mockito.mock;
  65. import static org.mockito.Mockito.verify;
  66. import static org.mockito.Mockito.verifyNoInteractions;
  67. import static org.mockito.Mockito.verifyNoMoreInteractions;
  68. import static org.mockito.Mockito.when;
  69. import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
  70. import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED;
  71. import static org.sonar.db.event.EventDto.CATEGORY_VERSION;
  72. import static org.sonar.db.event.EventTesting.newEvent;
  73. @RunWith(DataProviderRunner.class)
  74. public class LoadPeriodsStepIT extends BaseStepTest {
  75. @Rule
  76. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  77. @Rule
  78. public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
  79. @Rule
  80. public LogTester logTester = new LogTester();
  81. private final AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
  82. private final PeriodHolderImpl periodsHolder = new PeriodHolderImpl();
  83. private final System2 system2Mock = mock(System2.class);
  84. private final NewCodePeriodDao dao = new NewCodePeriodDao(system2Mock, new SequenceUuidFactory());
  85. private final NewCodePeriodResolver newCodePeriodResolver = new NewCodePeriodResolver(dbTester.getDbClient(), analysisMetadataHolder);
  86. private final ZonedDateTime analysisDate = ZonedDateTime.of(2019, 3, 20, 5, 30, 40, 0, ZoneId.systemDefault());
  87. private final CeTaskMessages ceTaskMessages = mock(CeTaskMessages.class);
  88. private final LoadPeriodsStep underTest = new LoadPeriodsStep(analysisMetadataHolder, dao, treeRootHolder, periodsHolder, dbTester.getDbClient(), newCodePeriodResolver,
  89. ceTaskMessages, system2Mock);
  90. private ComponentDto project;
  91. @Override
  92. protected ComputationStep step() {
  93. return underTest;
  94. }
  95. @Before
  96. public void setUp() {
  97. logTester.setLevel(Level.TRACE);
  98. project = dbTester.components().insertPublicProject().getMainBranchComponent();
  99. when(analysisMetadataHolder.isBranch()).thenReturn(true);
  100. when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(false);
  101. when(analysisMetadataHolder.getAnalysisDate()).thenReturn(analysisDate.toInstant().toEpochMilli());
  102. }
  103. @Test
  104. public void no_period_on_first_analysis() {
  105. setupRoot(project);
  106. when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);
  107. underTest.execute(new TestComputationStepContext());
  108. verify(analysisMetadataHolder).isFirstAnalysis();
  109. verify(analysisMetadataHolder).isBranch();
  110. verify(analysisMetadataHolder).getProject();
  111. verify(analysisMetadataHolder).getNewCodeReferenceBranch();
  112. assertThat(periodsHolder.hasPeriod()).isFalse();
  113. verifyNoMoreInteractions(analysisMetadataHolder);
  114. }
  115. @Test
  116. public void no_period_date_if_not_branch() {
  117. when(analysisMetadataHolder.isBranch()).thenReturn(false);
  118. underTest.execute(new TestComputationStepContext());
  119. verify(analysisMetadataHolder).isBranch();
  120. assertThat(periodsHolder.hasPeriod()).isFalse();
  121. verifyNoMoreInteractions(analysisMetadataHolder);
  122. }
  123. @Test
  124. public void load_default_if_nothing_defined() {
  125. setupRoot(project);
  126. SnapshotDto analysis = dbTester.components().insertSnapshot(project,
  127. snapshot -> snapshot.setCreatedAt(milisSinceEpoch(2019, 3, 15, 0)));
  128. underTest.execute(new TestComputationStepContext());
  129. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, null, analysis.getCreatedAt());
  130. verifyDebugLogs("Resolving first analysis as new code period as there is only one existing version");
  131. }
  132. @Test
  133. public void load_number_of_days_global() {
  134. setGlobalPeriod(NewCodePeriodType.NUMBER_OF_DAYS, "10");
  135. testNumberOfDays(project);
  136. }
  137. @Test
  138. public void load_number_of_days_on_project() {
  139. setGlobalPeriod(NewCodePeriodType.PREVIOUS_VERSION, null);
  140. setProjectPeriod(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "10");
  141. testNumberOfDays(project);
  142. }
  143. @Test
  144. public void load_number_of_days_on_branch() {
  145. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  146. setGlobalPeriod(NewCodePeriodType.PREVIOUS_VERSION, null);
  147. setProjectPeriod(project.uuid(), NewCodePeriodType.PREVIOUS_VERSION, null);
  148. setBranchPeriod(project.uuid(), branch.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "10");
  149. testNumberOfDays(branch);
  150. verifyNoInteractions(ceTaskMessages);
  151. }
  152. @Test
  153. public void load_reference_branch() {
  154. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  155. setupRoot(branch);
  156. setProjectPeriod(project.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
  157. underTest.execute(new TestComputationStepContext());
  158. assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, "master", null);
  159. verifyNoInteractions(ceTaskMessages);
  160. }
  161. @Test
  162. public void scanner_overrides_branch_new_code_reference_branch() {
  163. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  164. setupRoot(branch);
  165. setBranchPeriod(project.uuid(), branch.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
  166. String newCodeReferenceBranch = "newCodeReferenceBranch";
  167. when(analysisMetadataHolder.getNewCodeReferenceBranch()).thenReturn(Optional.of(newCodeReferenceBranch));
  168. underTest.execute(new TestComputationStepContext());
  169. assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, newCodeReferenceBranch, null);
  170. verify(ceTaskMessages).add(any(CeTaskMessages.Message.class));
  171. }
  172. @Test
  173. public void scanner_defines_new_code_reference_branch() {
  174. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  175. setupRoot(branch);
  176. String newCodeReferenceBranch = "newCodeReferenceBranch";
  177. when(analysisMetadataHolder.getNewCodeReferenceBranch()).thenReturn(Optional.of(newCodeReferenceBranch));
  178. underTest.execute(new TestComputationStepContext());
  179. assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, newCodeReferenceBranch, null);
  180. verifyNoInteractions(ceTaskMessages);
  181. }
  182. @Test
  183. public void scanner_overrides_project_new_code_reference_branch() {
  184. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  185. setupRoot(branch);
  186. setProjectPeriod(project.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
  187. String newCodeReferenceBranch = "newCodeReferenceBranch";
  188. when(analysisMetadataHolder.getNewCodeReferenceBranch()).thenReturn(Optional.of(newCodeReferenceBranch));
  189. underTest.execute(new TestComputationStepContext());
  190. assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, newCodeReferenceBranch, null);
  191. verifyNoInteractions(ceTaskMessages);
  192. }
  193. @Test
  194. public void load_reference_branch_without_fork_date_in_report() {
  195. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  196. setupRoot(branch);
  197. setProjectPeriod(project.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
  198. underTest.execute(new TestComputationStepContext());
  199. assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, "master", null);
  200. verifyNoInteractions(ceTaskMessages);
  201. }
  202. private void testNumberOfDays(ComponentDto projectOrBranch) {
  203. setupRoot(projectOrBranch);
  204. SnapshotDto analysis = dbTester.components().insertSnapshot(projectOrBranch,
  205. snapshot -> snapshot.setCreatedAt(milisSinceEpoch(2019, 3, 15, 0)));
  206. underTest.execute(new TestComputationStepContext());
  207. assertPeriod(NewCodePeriodType.NUMBER_OF_DAYS, "10", analysis.getCreatedAt());
  208. verifyDebugLogs("Resolving new code period by 10 days: 2019-03-10");
  209. }
  210. @Test
  211. public void load_specific_analysis() {
  212. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  213. SnapshotDto selectedAnalysis = dbTester.components().insertSnapshot(branch);
  214. SnapshotDto aVersionAnalysis = dbTester.components().insertSnapshot(branch, snapshot -> snapshot.setCreatedAt(milisSinceEpoch(2019, 3, 12, 0)).setLast(false));
  215. dbTester.events().insertEvent(EventTesting.newEvent(aVersionAnalysis).setName("a_version").setCategory(CATEGORY_VERSION));
  216. dbTester.components().insertSnapshot(branch, snapshot -> snapshot.setCreatedAt(milisSinceEpoch(2019, 3, 15, 0)).setLast(true));
  217. setBranchPeriod(project.uuid(), branch.uuid(), NewCodePeriodType.SPECIFIC_ANALYSIS, selectedAnalysis.getUuid());
  218. setupRoot(branch);
  219. underTest.execute(new TestComputationStepContext());
  220. assertPeriod(NewCodePeriodType.SPECIFIC_ANALYSIS, selectedAnalysis.getUuid(), selectedAnalysis.getCreatedAt());
  221. verifyDebugLogs("Resolving new code period with a specific analysis");
  222. verifyNoInteractions(ceTaskMessages);
  223. }
  224. @Test
  225. public void throw_ISE_if_no_analysis_found_for_number_of_days() {
  226. setProjectPeriod(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "10");
  227. setupRoot(project);
  228. assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
  229. .isInstanceOf(IllegalStateException.class)
  230. .hasMessageContaining("Attempting to resolve period while no analysis exist");
  231. }
  232. @Test
  233. public void throw_ISE_if_no_analysis_found_with_default() {
  234. setupRoot(project);
  235. assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
  236. .isInstanceOf(IllegalStateException.class)
  237. .hasMessageContaining("Attempting to resolve period while no analysis exist");
  238. }
  239. @Test
  240. public void ignore_unprocessed_snapshots() {
  241. SnapshotDto analysis1 = dbTester.components()
  242. .insertSnapshot(project, snapshot -> snapshot.setStatus(STATUS_UNPROCESSED).setCreatedAt(milisSinceEpoch(2019, 3, 12, 0)).setLast(false));
  243. SnapshotDto analysis2 = dbTester.components().insertSnapshot(project,
  244. snapshot -> snapshot.setStatus(STATUS_PROCESSED).setProjectVersion("not provided").setCreatedAt(milisSinceEpoch(2019, 3, 15, 0)).setLast(false));
  245. dbTester.events().insertEvent(newEvent(analysis1).setName("not provided").setCategory(CATEGORY_VERSION).setDate(analysis1.getCreatedAt()));
  246. dbTester.events().insertEvent(newEvent(analysis2).setName("not provided").setCategory(CATEGORY_VERSION).setDate(analysis2.getCreatedAt()));
  247. setupRoot(project);
  248. setProjectPeriod(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "10");
  249. underTest.execute(new TestComputationStepContext());
  250. assertPeriod(NewCodePeriodType.NUMBER_OF_DAYS, "10", analysis2.getCreatedAt());
  251. verifyDebugLogs("Resolving new code period by 10 days: 2019-03-10");
  252. }
  253. @Test
  254. public void throw_ISE_when_specific_analysis_is_set_but_does_not_exist_in_DB() {
  255. ComponentDto project = dbTester.components().insertPublicProject().getMainBranchComponent();
  256. setProjectPeriod(project.uuid(), NewCodePeriodType.SPECIFIC_ANALYSIS, "nonexistent");
  257. setupRoot(project, project.uuid(), "any-string");
  258. assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
  259. .isInstanceOf(IllegalStateException.class)
  260. .hasMessage("Analysis 'nonexistent' of project '" + project.uuid() + "' defined as the baseline does not exist");
  261. }
  262. @Test
  263. public void throw_ISE_when_specific_analysis_is_set_but_does_not_belong_to_current_project() {
  264. ComponentDto otherProject = dbTester.components().insertPublicProject().getMainBranchComponent();
  265. SnapshotDto otherProjectAnalysis = dbTester.components().insertSnapshot(otherProject);
  266. setBranchPeriod(project.uuid(), project.uuid(), NewCodePeriodType.SPECIFIC_ANALYSIS, otherProjectAnalysis.getUuid());
  267. setupRoot(project);
  268. assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
  269. .isInstanceOf(IllegalStateException.class)
  270. .hasMessage("Analysis '" + otherProjectAnalysis.getUuid() + "' of project '" + project.uuid() + "' defined as the baseline does not exist");
  271. }
  272. @Test
  273. public void throw_ISE_when_specific_analysis_is_set_but_does_not_belong_to_current_branch() {
  274. ComponentDto otherBranch = dbTester.components().insertProjectBranch(project);
  275. SnapshotDto otherBranchAnalysis = dbTester.components().insertSnapshot(otherBranch);
  276. setBranchPeriod(project.uuid(), project.uuid(), NewCodePeriodType.SPECIFIC_ANALYSIS, otherBranchAnalysis.getUuid());
  277. setupRoot(project);
  278. assertThatThrownBy(() -> underTest.execute(new TestComputationStepContext()))
  279. .isInstanceOf(IllegalStateException.class)
  280. .hasMessage("Analysis '" + otherBranchAnalysis.getUuid() + "' of project '" + project.uuid() + "' defined as the baseline does not exist");
  281. }
  282. @Test
  283. public void load_previous_version() {
  284. SnapshotDto analysis1 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setProjectVersion("0.9").setLast(false)); // 2008-11-11
  285. SnapshotDto analysis2 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226494680000L).setProjectVersion("1.0").setLast(false)); // 2008-11-12
  286. SnapshotDto analysis3 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227157200000L).setProjectVersion("1.1").setLast(false)); // 2008-11-20
  287. SnapshotDto analysis4 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227358680000L).setProjectVersion("1.1").setLast(false)); // 2008-11-22
  288. SnapshotDto analysis5 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227934800000L).setProjectVersion("1.1").setLast(true)); // 2008-11-29
  289. dbTester.events().insertEvent(newEvent(analysis1).setName("0.9").setCategory(CATEGORY_VERSION).setDate(analysis1.getCreatedAt()));
  290. dbTester.events().insertEvent(newEvent(analysis2).setName("1.0").setCategory(CATEGORY_VERSION).setDate(analysis2.getCreatedAt()));
  291. dbTester.events().insertEvent(newEvent(analysis5).setName("1.1").setCategory(CATEGORY_VERSION).setDate(analysis5.getCreatedAt()));
  292. setupRoot(project, "1.1");
  293. setProjectPeriod(project.uuid(), NewCodePeriodType.PREVIOUS_VERSION, null);
  294. underTest.execute(new TestComputationStepContext());
  295. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, "1.0", analysis2.getCreatedAt());
  296. verifyDebugLogs("Resolving new code period by previous version: 1.0");
  297. }
  298. @Test
  299. public void load_previous_version_when_version_is_changing() {
  300. SnapshotDto analysis1 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setProjectVersion("0.9").setLast(false)); // 2008-11-11
  301. SnapshotDto analysis2 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226494680000L).setProjectVersion("0.9").setLast(true)); // 2008-11-12
  302. dbTester.events().insertEvent(newEvent(analysis2).setName("0.9").setCategory(CATEGORY_VERSION).setDate(analysis2.getCreatedAt()));
  303. setupRoot(project, "1.0");
  304. setProjectPeriod(project.uuid(), NewCodePeriodType.PREVIOUS_VERSION, null);
  305. underTest.execute(new TestComputationStepContext());
  306. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, "0.9", analysis2.getCreatedAt());
  307. verifyDebugLogs("Resolving new code period by previous version: 0.9");
  308. }
  309. @Test
  310. @UseDataProvider("zeroOrLess")
  311. public void fail_with_MessageException_if_period_is_0_or_less(int zeroOrLess) {
  312. setupRoot(project);
  313. setProjectPeriod(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, String.valueOf(zeroOrLess));
  314. verifyFailWithInvalidValueMessageException(String.valueOf(zeroOrLess),
  315. "Invalid code period '" + zeroOrLess + "': number of days is <= 0");
  316. }
  317. @Test
  318. public void load_previous_version_with_previous_version_deleted() {
  319. SnapshotDto analysis1 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setProjectVersion("0.9").setLast(false)); // 2008-11-11
  320. SnapshotDto analysis2 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226494680000L).setProjectVersion("1.0").setLast(false)); // 2008-11-12
  321. SnapshotDto analysis3 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227157200000L).setProjectVersion("1.1").setLast(true)); // 2008-11-20
  322. dbTester.events().insertEvent(newEvent(analysis1).setName("0.9").setCategory(CATEGORY_VERSION));
  323. // The "1.0" version was deleted from the history
  324. setupRoot(project, "1.1");
  325. underTest.execute(new TestComputationStepContext());
  326. // Analysis form 2008-11-11
  327. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, "0.9", analysis1.getCreatedAt());
  328. }
  329. @Test
  330. public void load_previous_version_with_first_analysis_when_no_previous_version_found() {
  331. SnapshotDto analysis1 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setProjectVersion("1.1").setLast(false)); // 2008-11-11
  332. SnapshotDto analysis2 = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227934800000L).setProjectVersion("1.1").setLast(true)); // 2008-11-29
  333. dbTester.events().insertEvent(newEvent(analysis2).setName("1.1").setCategory(CATEGORY_VERSION));
  334. setupRoot(project, "1.1");
  335. underTest.execute(new TestComputationStepContext());
  336. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, null, analysis1.getCreatedAt());
  337. verifyDebugLogs("Resolving first analysis as new code period as there is only one existing version");
  338. }
  339. @Test
  340. public void load_previous_version_with_first_analysis_when_previous_snapshot_is_the_last_one() {
  341. SnapshotDto analysis = dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setProjectVersion("0.9").setLast(true)); // 2008-11-11
  342. dbTester.events().insertEvent(newEvent(analysis).setName("0.9").setCategory(CATEGORY_VERSION));
  343. setupRoot(project, "1.1");
  344. dbTester.newCodePeriods().insert(NewCodePeriodType.PREVIOUS_VERSION, null);
  345. underTest.execute(new TestComputationStepContext());
  346. assertPeriod(NewCodePeriodType.PREVIOUS_VERSION, "0.9", analysis.getCreatedAt());
  347. verifyDebugLogs("Resolving new code period by previous version: 0.9");
  348. }
  349. @Test
  350. @UseDataProvider("anyValidLeakPeriodSettingValue")
  351. public void leak_period_setting_is_ignored_for_PR(NewCodePeriodType type, @Nullable String value) {
  352. when(analysisMetadataHolder.isBranch()).thenReturn(false);
  353. dbTester.newCodePeriods().insert(type, value);
  354. underTest.execute(new TestComputationStepContext());
  355. assertThat(periodsHolder.hasPeriod()).isFalse();
  356. }
  357. private void verifyFailWithInvalidValueMessageException(String propertyValue, String debugLog, String... otherDebugLogs) {
  358. try {
  359. underTest.execute(new TestComputationStepContext());
  360. fail("a Message Exception should have been thrown");
  361. } catch (MessageException e) {
  362. verifyInvalidValueMessage(e, propertyValue);
  363. verifyDebugLogs(debugLog, otherDebugLogs);
  364. }
  365. }
  366. @DataProvider
  367. public static Object[][] zeroOrLess() {
  368. return new Object[][] {
  369. {0},
  370. {-1 - new Random().nextInt(30)}
  371. };
  372. }
  373. @DataProvider
  374. public static Object[][] stringConsideredAsVersions() {
  375. return new Object[][] {
  376. {randomAlphabetic(5)},
  377. {"1,3"},
  378. {"1.3"},
  379. {"0 1"},
  380. {"1-SNAPSHOT"},
  381. {"01-12-2018"}, // unsupported date format
  382. };
  383. }
  384. @DataProvider
  385. public static Object[][] projectVersionNullOrNot() {
  386. return new Object[][] {
  387. {null},
  388. {randomAlphabetic(15)},
  389. };
  390. }
  391. @DataProvider
  392. public static Object[][] anyValidLeakPeriodSettingValue() {
  393. return new Object[][] {
  394. // days
  395. {NewCodePeriodType.NUMBER_OF_DAYS, "100"},
  396. // previous_version
  397. {NewCodePeriodType.PREVIOUS_VERSION, null}
  398. };
  399. }
  400. private List<SnapshotDto> createSnapshots(ComponentDto project) {
  401. ArrayList<SnapshotDto> list = new ArrayList<>();
  402. list.add(dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226379600000L).setLast(false))); // 2008-11-11
  403. list.add(dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1226494680000L).setLast(false))); // 2008-11-12
  404. list.add(dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227157200000L).setLast(false))); // 2008-11-20
  405. list.add(dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227358680000L).setLast(false))); // 2008-11-22
  406. list.add(dbTester.components().insertSnapshot(project, snapshot -> snapshot.setCreatedAt(1227934800000L).setLast(true))); // 2008-11-29
  407. return list;
  408. }
  409. private long milisSinceEpoch(int year, int month, int day, int hour) {
  410. return ZonedDateTime.of(year, month, day, hour, 0, 0, 0, ZoneId.systemDefault())
  411. .toInstant().toEpochMilli();
  412. }
  413. private void setProjectPeriod(String projectUuid, NewCodePeriodType type, @Nullable String value) {
  414. dbTester.newCodePeriods().insert(projectUuid, type, value);
  415. }
  416. private void setBranchPeriod(String projectUuid, String branchUuid, NewCodePeriodType type, @Nullable String value) {
  417. dbTester.newCodePeriods().insert(projectUuid, branchUuid, type, value);
  418. }
  419. private void setGlobalPeriod(NewCodePeriodType type, @Nullable String value) {
  420. dbTester.newCodePeriods().insert(type, value);
  421. }
  422. private void assertPeriod(NewCodePeriodType type, @Nullable String value, @Nullable Long date) {
  423. Period period = periodsHolder.getPeriod();
  424. assertThat(period).isNotNull();
  425. assertThat(period.getMode()).isEqualTo(type.name());
  426. assertThat(period.getModeParameter()).isEqualTo(value);
  427. assertThat(period.getDate()).isEqualTo(date);
  428. }
  429. private void verifyDebugLogs(String log, String... otherLogs) {
  430. assertThat(logTester.logs(Level.DEBUG))
  431. .contains(Stream.concat(Stream.of(log), Arrays.stream(otherLogs)).toArray(String[]::new));
  432. }
  433. private void setupRoot(ComponentDto branchComponent) {
  434. setupRoot(branchComponent, "any-string");
  435. }
  436. private void setupRoot(ComponentDto branchComponent, String projectUuid, String version) {
  437. treeRootHolder.setRoot(ReportComponent
  438. .builder(Component.Type.PROJECT, 1)
  439. .setUuid(branchComponent.uuid())
  440. .setKey(branchComponent.getKey())
  441. .setProjectVersion(version)
  442. .build());
  443. Project project = mock(Project.class);
  444. when(project.getUuid()).thenReturn(projectUuid);
  445. when(analysisMetadataHolder.getProject()).thenReturn(project);
  446. }
  447. private void setupRoot(ComponentDto branchComponent, String version) {
  448. setupRoot(branchComponent, project.uuid(), version);
  449. }
  450. private static void verifyInvalidValueMessage(MessageException e, String propertyValue) {
  451. assertThat(e).hasMessage("Invalid new code period. '" + propertyValue
  452. + "' is not one of: integer > 0, date before current analysis j, \"previous_version\", or version string that exists in the project' \n" +
  453. "Please contact a project administrator to correct this setting");
  454. }
  455. }