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.

PostProjectAnalysisTasksExecutorTest.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.api.posttask;
  21. import com.google.common.collect.ImmutableMap;
  22. import com.tngtech.java.junit.dataprovider.DataProvider;
  23. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  24. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  25. import java.util.Date;
  26. import java.util.HashMap;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Random;
  30. import javax.annotation.Nullable;
  31. import org.apache.commons.lang.RandomStringUtils;
  32. import org.junit.Before;
  33. import org.junit.Rule;
  34. import org.junit.Test;
  35. import org.junit.runner.RunWith;
  36. import org.mockito.ArgumentCaptor;
  37. import org.mockito.InOrder;
  38. import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
  39. import org.sonar.api.ce.posttask.Project;
  40. import org.sonar.api.utils.System2;
  41. import org.sonar.api.utils.log.LogTester;
  42. import org.sonar.api.utils.log.LoggerLevel;
  43. import org.sonar.ce.task.CeTask;
  44. import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
  45. import org.sonar.ce.task.projectanalysis.analysis.Branch;
  46. import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
  47. import org.sonar.ce.task.projectanalysis.metric.Metric;
  48. import org.sonar.ce.task.projectanalysis.qualitygate.Condition;
  49. import org.sonar.ce.task.projectanalysis.qualitygate.ConditionStatus;
  50. import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateHolderRule;
  51. import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateStatusHolderRule;
  52. import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate;
  53. import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateStatus;
  54. import org.sonar.db.component.BranchType;
  55. import org.sonar.scanner.protocol.output.ScannerReport;
  56. import static com.google.common.collect.ImmutableList.of;
  57. import static java.util.Arrays.asList;
  58. import static java.util.stream.Collectors.toList;
  59. import static org.assertj.core.api.Assertions.assertThat;
  60. import static org.assertj.core.api.ThrowableAssert.catchThrowable;
  61. import static org.assertj.core.data.MapEntry.entry;
  62. import static org.mockito.ArgumentMatchers.any;
  63. import static org.mockito.Mockito.doAnswer;
  64. import static org.mockito.Mockito.doThrow;
  65. import static org.mockito.Mockito.inOrder;
  66. import static org.mockito.Mockito.mock;
  67. import static org.mockito.Mockito.verify;
  68. import static org.mockito.Mockito.when;
  69. @RunWith(DataProviderRunner.class)
  70. public class PostProjectAnalysisTasksExecutorTest {
  71. private static final String QUALITY_GATE_UUID = "98451";
  72. private static final String QUALITY_GATE_NAME = "qualityGate name";
  73. private static final Condition CONDITION_1 = createCondition("metric key 1");
  74. private static final Condition CONDITION_2 = createCondition("metric key 2");
  75. @Rule
  76. public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
  77. @Rule
  78. public MutableQualityGateHolderRule qualityGateHolder = new MutableQualityGateHolderRule();
  79. @Rule
  80. public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule();
  81. @Rule
  82. public BatchReportReaderRule reportReader = new BatchReportReaderRule();
  83. @Rule
  84. public LogTester logTester = new LogTester();
  85. private final System2 system2 = mock(System2.class);
  86. private final ArgumentCaptor<PostProjectAnalysisTask.Context> taskContextCaptor = ArgumentCaptor.forClass(PostProjectAnalysisTask.Context.class);
  87. private final CeTask.Component component = new CeTask.Component("component uuid", "component key", "component name");
  88. private final CeTask ceTask = new CeTask.Builder()
  89. .setType("type")
  90. .setUuid("uuid")
  91. .setComponent(component)
  92. .setMainComponent(component)
  93. .build();
  94. private final PostProjectAnalysisTask postProjectAnalysisTask = newPostProjectAnalysisTask("PT1");
  95. private final PostProjectAnalysisTasksExecutor underTest = new PostProjectAnalysisTasksExecutor(
  96. ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder,
  97. reportReader, system2,
  98. new PostProjectAnalysisTask[] {postProjectAnalysisTask});
  99. @Before
  100. public void setUp() {
  101. qualityGateHolder.setQualityGate(new QualityGate(QUALITY_GATE_UUID, QUALITY_GATE_NAME, of(CONDITION_1, CONDITION_2)));
  102. qualityGateStatusHolder.setStatus(QualityGateStatus.OK, ImmutableMap.of(
  103. CONDITION_1, ConditionStatus.create(ConditionStatus.EvaluationStatus.OK, "value"),
  104. CONDITION_2, ConditionStatus.NO_VALUE_STATUS));
  105. Branch branch = mock(Branch.class);
  106. when(branch.getType()).thenReturn(BranchType.BRANCH);
  107. analysisMetadataHolder
  108. .setBranch(branch);
  109. reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
  110. }
  111. @Test
  112. @UseDataProvider("booleanValues")
  113. public void does_not_fail_when_there_is_no_PostProjectAnalysisTasksExecutor(boolean allStepsExecuted) {
  114. new PostProjectAnalysisTasksExecutor(ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader, system2)
  115. .finished(allStepsExecuted);
  116. }
  117. @Test
  118. @UseDataProvider("booleanValues")
  119. public void finished_calls_all_PostProjectAnalysisTask_in_order_of_the_array_and_passes_the_same_object_to_all(boolean allStepsExecuted) {
  120. PostProjectAnalysisTask postProjectAnalysisTask1 = newPostProjectAnalysisTask("PT1");
  121. PostProjectAnalysisTask postProjectAnalysisTask2 = newPostProjectAnalysisTask("PT2");
  122. InOrder inOrder = inOrder(postProjectAnalysisTask1, postProjectAnalysisTask2);
  123. new PostProjectAnalysisTasksExecutor(
  124. ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader,
  125. system2, new PostProjectAnalysisTask[] {postProjectAnalysisTask1, postProjectAnalysisTask2})
  126. .finished(allStepsExecuted);
  127. inOrder.verify(postProjectAnalysisTask1).finished(taskContextCaptor.capture());
  128. inOrder.verify(postProjectAnalysisTask1).getDescription();
  129. inOrder.verify(postProjectAnalysisTask2).finished(taskContextCaptor.capture());
  130. inOrder.verify(postProjectAnalysisTask2).getDescription();
  131. inOrder.verifyNoMoreInteractions();
  132. ArgumentCaptor<PostProjectAnalysisTask.Context> taskContextCaptor = this.taskContextCaptor;
  133. List<PostProjectAnalysisTask.ProjectAnalysis> allValues = getAllProjectAnalyses(taskContextCaptor);
  134. assertThat(allValues).hasSize(2);
  135. assertThat(allValues.get(0)).isSameAs(allValues.get(1));
  136. assertThat(logTester.logs()).hasSize(2);
  137. List<String> logs = logTester.logs(LoggerLevel.INFO);
  138. assertThat(logs).hasSize(2);
  139. assertThat(logs.get(0)).matches("^PT1 \\| status=SUCCESS \\| time=\\d+ms$");
  140. assertThat(logs.get(1)).matches("^PT2 \\| status=SUCCESS \\| time=\\d+ms$");
  141. }
  142. @Test
  143. @UseDataProvider("booleanValues")
  144. public void organization_is_null(boolean allStepsExecuted) {
  145. underTest.finished(allStepsExecuted);
  146. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  147. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getOrganization()).isEmpty();
  148. }
  149. @Test
  150. @UseDataProvider("booleanValues")
  151. public void CeTask_status_depends_on_finished_method_argument_is_true_or_false(boolean allStepsExecuted) {
  152. underTest.finished(allStepsExecuted);
  153. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  154. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getCeTask().getStatus())
  155. .isEqualTo(
  156. allStepsExecuted ? org.sonar.api.ce.posttask.CeTask.Status.SUCCESS : org.sonar.api.ce.posttask.CeTask.Status.FAILED);
  157. }
  158. @Test
  159. public void ceTask_uuid_is_UUID_of_CeTask() {
  160. underTest.finished(true);
  161. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  162. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getCeTask().getId())
  163. .isEqualTo(ceTask.getUuid());
  164. }
  165. @Test
  166. public void project_uuid_key_and_name_come_from_CeTask() {
  167. underTest.finished(true);
  168. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  169. Project project = taskContextCaptor.getValue().getProjectAnalysis().getProject();
  170. assertThat(project.getUuid()).isEqualTo(ceTask.getComponent().get().getUuid());
  171. assertThat(project.getKey()).isEqualTo(ceTask.getComponent().get().getKey().get());
  172. assertThat(project.getName()).isEqualTo(ceTask.getComponent().get().getName().get());
  173. }
  174. @Test
  175. public void date_comes_from_AnalysisMetadataHolder() {
  176. analysisMetadataHolder.setAnalysisDate(8_465_132_498L);
  177. analysisMetadataHolder.setUuid(RandomStringUtils.randomAlphanumeric(40));
  178. underTest.finished(true);
  179. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  180. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getDate())
  181. .isEqualTo(new Date(analysisMetadataHolder.getAnalysisDate()));
  182. }
  183. @Test
  184. public void date_comes_from_system2_if_not_set_in_AnalysisMetadataHolder() {
  185. long now = 1_999_663L;
  186. when(system2.now()).thenReturn(now);
  187. underTest.finished(false);
  188. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  189. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getDate()).isEqualTo(new Date(now));
  190. }
  191. @Test
  192. public void analysisDate_and_analysisUuid_comes_from_AnalysisMetadataHolder_when_set() {
  193. analysisMetadataHolder.setAnalysisDate(8465132498L);
  194. analysisMetadataHolder.setUuid(RandomStringUtils.randomAlphanumeric(40));
  195. underTest.finished(true);
  196. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  197. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getAnalysis().get().getDate())
  198. .isEqualTo(new Date(analysisMetadataHolder.getAnalysisDate()));
  199. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getAnalysis().get().getAnalysisUuid())
  200. .isEqualTo(analysisMetadataHolder.getUuid());
  201. }
  202. @Test
  203. public void analysis_is_empty_when_not_set_in_AnalysisMetadataHolder() {
  204. underTest.finished(false);
  205. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  206. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getAnalysis()).isEmpty();
  207. }
  208. @Test
  209. public void branch_comes_from_AnalysisMetadataHolder_when_set() {
  210. analysisMetadataHolder.setBranch(new Branch() {
  211. @Override
  212. public BranchType getType() {
  213. return BranchType.BRANCH;
  214. }
  215. @Override
  216. public boolean isMain() {
  217. return false;
  218. }
  219. @Override
  220. public String getReferenceBranchUuid() {
  221. throw new UnsupportedOperationException();
  222. }
  223. @Override
  224. public String getName() {
  225. return "feature/foo";
  226. }
  227. @Override
  228. public boolean supportsCrossProjectCpd() {
  229. throw new UnsupportedOperationException();
  230. }
  231. @Override
  232. public String getPullRequestKey() {
  233. throw new UnsupportedOperationException();
  234. }
  235. @Override
  236. public String getTargetBranchName() {
  237. throw new UnsupportedOperationException();
  238. }
  239. @Override
  240. public String generateKey(String projectKey, @Nullable String fileOrDirPath) {
  241. throw new UnsupportedOperationException();
  242. }
  243. });
  244. underTest.finished(true);
  245. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  246. org.sonar.api.ce.posttask.Branch branch = taskContextCaptor.getValue().getProjectAnalysis().getBranch().get();
  247. assertThat(branch.isMain()).isFalse();
  248. assertThat(branch.getName()).hasValue("feature/foo");
  249. assertThat(branch.getType()).isEqualTo(BranchImpl.Type.BRANCH);
  250. }
  251. @Test
  252. public void qualityGate_is_null_when_finished_method_argument_is_false() {
  253. underTest.finished(false);
  254. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  255. assertThat(taskContextCaptor.getValue().getProjectAnalysis().getQualityGate()).isNull();
  256. }
  257. @Test
  258. public void qualityGate_is_populated_when_finished_method_argument_is_true() {
  259. underTest.finished(true);
  260. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  261. org.sonar.api.ce.posttask.QualityGate qualityGate = taskContextCaptor.getValue().getProjectAnalysis().getQualityGate();
  262. assertThat(qualityGate.getStatus()).isEqualTo(org.sonar.api.ce.posttask.QualityGate.Status.OK);
  263. assertThat(qualityGate.getId()).isEqualTo(QUALITY_GATE_UUID);
  264. assertThat(qualityGate.getName()).isEqualTo(QUALITY_GATE_NAME);
  265. assertThat(qualityGate.getConditions()).hasSize(2);
  266. }
  267. @Test
  268. public void scannerContext_loads_properties_from_scanner_report() {
  269. reportReader.putContextProperties(asList(ScannerReport.ContextProperty.newBuilder().setKey("foo").setValue("bar").build()));
  270. underTest.finished(true);
  271. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  272. org.sonar.api.ce.posttask.ScannerContext scannerContext = taskContextCaptor.getValue().getProjectAnalysis().getScannerContext();
  273. assertThat(scannerContext.getProperties()).containsExactly(entry("foo", "bar"));
  274. }
  275. @Test
  276. @UseDataProvider("booleanValues")
  277. public void logStatistics_add_fails_when_NPE_if_key_or_value_is_null(boolean allStepsExecuted) {
  278. underTest.finished(allStepsExecuted);
  279. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  280. PostProjectAnalysisTask.LogStatistics logStatistics = taskContextCaptor.getValue().getLogStatistics();
  281. assertThat(catchThrowable(() -> logStatistics.add(null, "foo")))
  282. .isInstanceOf(NullPointerException.class)
  283. .hasMessage("Statistic has null key");
  284. assertThat(catchThrowable(() -> logStatistics.add(null, null)))
  285. .isInstanceOf(NullPointerException.class)
  286. .hasMessage("Statistic has null key");
  287. assertThat(catchThrowable(() -> logStatistics.add("bar", null)))
  288. .isInstanceOf(NullPointerException.class)
  289. .hasMessage("Statistic with key [bar] has null value");
  290. }
  291. @Test
  292. @UseDataProvider("booleanValues")
  293. public void logStatistics_add_fails_with_IAE_if_key_is_time_or_status_ignoring_case(boolean allStepsExecuted) {
  294. underTest.finished(allStepsExecuted);
  295. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  296. PostProjectAnalysisTask.LogStatistics logStatistics = taskContextCaptor.getValue().getLogStatistics();
  297. for (String reservedKey : asList("time", "TIME", "TImE", "status", "STATUS", "STaTuS")) {
  298. assertThat(catchThrowable(() -> logStatistics.add(reservedKey, "foo")))
  299. .isInstanceOf(IllegalArgumentException.class)
  300. .hasMessage("Statistic with key [" + reservedKey + "] is not accepted");
  301. }
  302. }
  303. @Test
  304. @UseDataProvider("booleanValues")
  305. public void logStatistics_add_fails_with_IAE_if_same_key_with_exact_case_added_twice(boolean allStepsExecuted) {
  306. underTest.finished(allStepsExecuted);
  307. verify(postProjectAnalysisTask).finished(taskContextCaptor.capture());
  308. PostProjectAnalysisTask.LogStatistics logStatistics = taskContextCaptor.getValue().getLogStatistics();
  309. String key = RandomStringUtils.randomAlphabetic(10);
  310. logStatistics.add(key, new Object());
  311. assertThat(catchThrowable(() -> logStatistics.add(key, "bar")))
  312. .isInstanceOf(IllegalArgumentException.class)
  313. .hasMessage("Statistic with key [" + key + "] is already present");
  314. }
  315. @Test
  316. @UseDataProvider("booleanValues")
  317. public void logStatistics_adds_statistics_to_end_of_task_log(boolean allStepsExecuted) {
  318. Map<String, Object> stats = new HashMap<>();
  319. for (int i = 0; i <= new Random().nextInt(10); i++) {
  320. stats.put("statKey_" + i, "statVal_" + i);
  321. }
  322. PostProjectAnalysisTask logStatisticsTask = mock(PostProjectAnalysisTask.class);
  323. when(logStatisticsTask.getDescription()).thenReturn("PT1");
  324. doAnswer(i -> {
  325. PostProjectAnalysisTask.Context context = i.getArgument(0);
  326. stats.forEach((k, v) -> context.getLogStatistics().add(k, v));
  327. return null;
  328. }).when(logStatisticsTask)
  329. .finished(any(PostProjectAnalysisTask.Context.class));
  330. new PostProjectAnalysisTasksExecutor(
  331. ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader,
  332. system2, new PostProjectAnalysisTask[] {logStatisticsTask})
  333. .finished(allStepsExecuted);
  334. verify(logStatisticsTask).finished(taskContextCaptor.capture());
  335. assertThat(logTester.logs()).hasSize(1);
  336. List<String> logs = logTester.logs(LoggerLevel.INFO);
  337. assertThat(logs).hasSize(1);
  338. StringBuilder expectedLog = new StringBuilder("^PT1 ");
  339. stats.forEach((k, v) -> expectedLog.append("\\| " + k + "=" + v + " "));
  340. expectedLog.append("\\| status=SUCCESS \\| time=\\d+ms$");
  341. assertThat(logs.get(0)).matches(expectedLog.toString());
  342. }
  343. @Test
  344. @UseDataProvider("booleanValues")
  345. public void finished_does_not_fail_if_listener_throws_exception_and_execute_subsequent_listeners(boolean allStepsExecuted) {
  346. PostProjectAnalysisTask postProjectAnalysisTask1 = newPostProjectAnalysisTask("PT1");
  347. PostProjectAnalysisTask postProjectAnalysisTask2 = newPostProjectAnalysisTask("PT2");
  348. PostProjectAnalysisTask postProjectAnalysisTask3 = newPostProjectAnalysisTask("PT3");
  349. InOrder inOrder = inOrder(postProjectAnalysisTask1, postProjectAnalysisTask2, postProjectAnalysisTask3);
  350. doThrow(new RuntimeException("Faking a listener throws an exception"))
  351. .when(postProjectAnalysisTask2)
  352. .finished(any(PostProjectAnalysisTask.Context.class));
  353. new PostProjectAnalysisTasksExecutor(
  354. ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader,
  355. system2, new PostProjectAnalysisTask[] {postProjectAnalysisTask1, postProjectAnalysisTask2, postProjectAnalysisTask3})
  356. .finished(allStepsExecuted);
  357. inOrder.verify(postProjectAnalysisTask1).finished(taskContextCaptor.capture());
  358. inOrder.verify(postProjectAnalysisTask1).getDescription();
  359. inOrder.verify(postProjectAnalysisTask2).finished(taskContextCaptor.capture());
  360. inOrder.verify(postProjectAnalysisTask2).getDescription();
  361. inOrder.verify(postProjectAnalysisTask3).finished(taskContextCaptor.capture());
  362. inOrder.verify(postProjectAnalysisTask3).getDescription();
  363. inOrder.verifyNoMoreInteractions();
  364. assertThat(logTester.logs()).hasSize(4);
  365. List<String> logs = logTester.logs(LoggerLevel.INFO);
  366. assertThat(logs).hasSize(3);
  367. assertThat(logs.get(0)).matches("^PT1 \\| status=SUCCESS \\| time=\\d+ms$");
  368. assertThat(logs.get(1)).matches("^PT2 \\| status=FAILED \\| time=\\d+ms$");
  369. assertThat(logs.get(2)).matches("^PT3 \\| status=SUCCESS \\| time=\\d+ms$");
  370. }
  371. @DataProvider
  372. public static Object[][] booleanValues() {
  373. return new Object[][] {
  374. {true},
  375. {false}
  376. };
  377. }
  378. private static Condition createCondition(String metricKey) {
  379. Metric metric = mock(Metric.class);
  380. when(metric.getKey()).thenReturn(metricKey);
  381. return new Condition(metric, Condition.Operator.LESS_THAN.getDbValue(), "error threshold");
  382. }
  383. private static PostProjectAnalysisTask newPostProjectAnalysisTask(String description) {
  384. PostProjectAnalysisTask res = mock(PostProjectAnalysisTask.class);
  385. when(res.getDescription()).thenReturn(description);
  386. doAnswer(i -> null).when(res).finished(any(PostProjectAnalysisTask.Context.class));
  387. return res;
  388. }
  389. private static List<PostProjectAnalysisTask.ProjectAnalysis> getAllProjectAnalyses(ArgumentCaptor<PostProjectAnalysisTask.Context> taskContextCaptor) {
  390. return taskContextCaptor.getAllValues()
  391. .stream()
  392. .map(PostProjectAnalysisTask.Context::getProjectAnalysis)
  393. .collect(toList());
  394. }
  395. }