3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.project;
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.Collections;
26 import java.util.Random;
28 import java.util.stream.Collectors;
29 import java.util.stream.IntStream;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.mockito.InOrder;
33 import org.mockito.Mockito;
35 import static java.util.Collections.singleton;
36 import static org.assertj.core.api.Assertions.assertThatCode;
37 import static org.assertj.core.api.Assertions.assertThatNoException;
38 import static org.assertj.core.api.Assertions.assertThatThrownBy;
39 import static org.mockito.ArgumentMatchers.any;
40 import static org.mockito.ArgumentMatchers.same;
41 import static org.mockito.Mockito.doThrow;
42 import static org.mockito.Mockito.mock;
43 import static org.mockito.Mockito.verifyNoInteractions;
44 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
46 @RunWith(DataProviderRunner.class)
47 public class ProjectLifeCycleListenersImplTest {
49 private final ProjectLifeCycleListener listener1 = mock(ProjectLifeCycleListener.class);
50 private final ProjectLifeCycleListener listener2 = mock(ProjectLifeCycleListener.class);
51 private final ProjectLifeCycleListener listener3 = mock(ProjectLifeCycleListener.class);
52 private final ProjectLifeCycleListenersImpl underTestNoListeners = new ProjectLifeCycleListenersImpl();
53 private final ProjectLifeCycleListenersImpl underTestWithListeners = new ProjectLifeCycleListenersImpl(
54 new ProjectLifeCycleListener[] {listener1, listener2, listener3});
57 public void onProjectsDeleted_throws_NPE_if_set_is_null() {
58 assertThatThrownBy(() -> underTestWithListeners.onProjectsDeleted(null))
59 .isInstanceOf(NullPointerException.class)
60 .hasMessage("projects can't be null");
64 public void onProjectsDeleted_throws_NPE_if_set_is_null_even_if_no_listeners() {
65 assertThatThrownBy(() -> underTestNoListeners.onProjectsDeleted(null))
66 .isInstanceOf(NullPointerException.class)
67 .hasMessage("projects can't be null");
71 public void onProjectsDeleted_has_no_effect_if_set_is_empty() {
72 underTestNoListeners.onProjectsDeleted(Collections.emptySet());
74 underTestWithListeners.onProjectsDeleted(Collections.emptySet());
75 verifyNoInteractions(listener1, listener2, listener3);
79 @UseDataProvider("oneOrManyDeletedProjects")
80 public void onProjectsDeleted_does_not_fail_if_there_is_no_listener(Set<DeletedProject> projects) {
81 assertThatCode(() -> underTestNoListeners.onProjectsDeleted(projects)).doesNotThrowAnyException();
85 @UseDataProvider("oneOrManyDeletedProjects")
86 public void onProjectsDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<DeletedProject> projects) {
87 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
89 underTestWithListeners.onProjectsDeleted(projects);
91 inOrder.verify(listener1).onProjectsDeleted(same(projects));
92 inOrder.verify(listener2).onProjectsDeleted(same(projects));
93 inOrder.verify(listener3).onProjectsDeleted(same(projects));
94 inOrder.verifyNoMoreInteractions();
98 @UseDataProvider("oneOrManyDeletedProjects")
99 public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<DeletedProject> projects) {
100 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
101 doThrow(new RuntimeException("Faking listener2 throwing an exception"))
103 .onProjectsDeleted(any());
105 underTestWithListeners.onProjectsDeleted(projects);
107 inOrder.verify(listener1).onProjectsDeleted(same(projects));
108 inOrder.verify(listener2).onProjectsDeleted(same(projects));
109 inOrder.verify(listener3).onProjectsDeleted(same(projects));
110 inOrder.verifyNoMoreInteractions();
114 @UseDataProvider("oneOrManyDeletedProjects")
115 public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<DeletedProject> projects) {
116 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
117 doThrow(new Error("Faking listener2 throwing an Error"))
119 .onProjectsDeleted(any());
121 underTestWithListeners.onProjectsDeleted(projects);
123 inOrder.verify(listener1).onProjectsDeleted(same(projects));
124 inOrder.verify(listener2).onProjectsDeleted(same(projects));
125 inOrder.verify(listener3).onProjectsDeleted(same(projects));
126 inOrder.verifyNoMoreInteractions();
130 public void onProjectBranchesChanged_throws_NPE_if_set_is_null() {
131 assertThatThrownBy(() -> underTestWithListeners.onProjectBranchesChanged(null))
132 .isInstanceOf(NullPointerException.class)
133 .hasMessage("projects can't be null");
137 public void onProjectBranchesChanged_throws_NPE_if_set_is_null_even_if_no_listeners() {
138 assertThatThrownBy(() -> underTestNoListeners.onProjectBranchesChanged(null))
139 .isInstanceOf(NullPointerException.class)
140 .hasMessage("projects can't be null");
144 public void onProjectBranchesChanged_has_no_effect_if_set_is_empty() {
145 underTestNoListeners.onProjectBranchesChanged(Collections.emptySet());
147 underTestWithListeners.onProjectBranchesChanged(Collections.emptySet());
148 verifyNoInteractions(listener1, listener2, listener3);
152 @UseDataProvider("oneOrManyProjects")
153 public void onProjectBranchesChanged_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
154 assertThatNoException().isThrownBy(()-> underTestNoListeners.onProjectBranchesChanged(projects));
158 @UseDataProvider("oneOrManyProjects")
159 public void onProjectBranchesChanged_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
160 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
162 underTestWithListeners.onProjectBranchesChanged(projects);
164 inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
165 inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
166 inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
167 inOrder.verifyNoMoreInteractions();
171 @UseDataProvider("oneOrManyProjects")
172 public void onProjectBranchesChanged_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
173 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
174 doThrow(new RuntimeException("Faking listener2 throwing an exception"))
176 .onProjectBranchesChanged(any());
178 underTestWithListeners.onProjectBranchesChanged(projects);
180 inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
181 inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
182 inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
183 inOrder.verifyNoMoreInteractions();
187 @UseDataProvider("oneOrManyProjects")
188 public void onProjectBranchesChanged_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
189 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
190 doThrow(new Error("Faking listener2 throwing an Error"))
192 .onProjectBranchesChanged(any());
194 underTestWithListeners.onProjectBranchesChanged(projects);
196 inOrder.verify(listener1).onProjectBranchesChanged(same(projects));
197 inOrder.verify(listener2).onProjectBranchesChanged(same(projects));
198 inOrder.verify(listener3).onProjectBranchesChanged(same(projects));
199 inOrder.verifyNoMoreInteractions();
203 public static Object[][] oneOrManyProjects() {
204 return new Object[][] {
205 {singleton(newUniqueProject())},
206 {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueProject()).collect(Collectors.toSet())}
211 public static Object[][] oneOrManyDeletedProjects() {
212 return new Object[][] {
213 {singleton(newUniqueProject())},
214 {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> new DeletedProject(newUniqueProject(), "branch_" + i))
215 .collect(Collectors.toSet())}
220 public void onProjectsRekeyed_throws_NPE_if_set_is_null() {
221 assertThatThrownBy(() -> underTestWithListeners.onProjectsRekeyed(null))
222 .isInstanceOf(NullPointerException.class)
223 .hasMessage("rekeyedProjects can't be null");
227 public void onProjectsRekeyed_throws_NPE_if_set_is_null_even_if_no_listeners() {
228 assertThatThrownBy(() -> underTestNoListeners.onProjectsRekeyed(null))
229 .isInstanceOf(NullPointerException.class)
230 .hasMessage("rekeyedProjects can't be null");
234 public void onProjectsRekeyed_has_no_effect_if_set_is_empty() {
235 underTestNoListeners.onProjectsRekeyed(Collections.emptySet());
237 underTestWithListeners.onProjectsRekeyed(Collections.emptySet());
238 verifyNoInteractions(listener1, listener2, listener3);
242 @UseDataProvider("oneOrManyRekeyedProjects")
243 public void onProjectsRekeyed_does_not_fail_if_there_is_no_listener(Set<RekeyedProject> projects) {
244 assertThatNoException().isThrownBy(() -> underTestNoListeners.onProjectsRekeyed(projects));
248 @UseDataProvider("oneOrManyRekeyedProjects")
249 public void onProjectsRekeyed_calls_all_listeners_in_order_of_addition_to_constructor(Set<RekeyedProject> projects) {
250 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
252 underTestWithListeners.onProjectsRekeyed(projects);
254 inOrder.verify(listener1).onProjectsRekeyed(same(projects));
255 inOrder.verify(listener2).onProjectsRekeyed(same(projects));
256 inOrder.verify(listener3).onProjectsRekeyed(same(projects));
257 inOrder.verifyNoMoreInteractions();
261 @UseDataProvider("oneOrManyRekeyedProjects")
262 public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Exception(Set<RekeyedProject> projects) {
263 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
264 doThrow(new RuntimeException("Faking listener2 throwing an exception"))
266 .onProjectsRekeyed(any());
268 underTestWithListeners.onProjectsRekeyed(projects);
270 inOrder.verify(listener1).onProjectsRekeyed(same(projects));
271 inOrder.verify(listener2).onProjectsRekeyed(same(projects));
272 inOrder.verify(listener3).onProjectsRekeyed(same(projects));
273 inOrder.verifyNoMoreInteractions();
277 @UseDataProvider("oneOrManyRekeyedProjects")
278 public void onProjectsRekeyed_calls_all_listeners_even_if_one_throws_an_Error(Set<RekeyedProject> projects) {
279 InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
280 doThrow(new Error("Faking listener2 throwing an Error"))
282 .onProjectsRekeyed(any());
284 underTestWithListeners.onProjectsRekeyed(projects);
286 inOrder.verify(listener1).onProjectsRekeyed(same(projects));
287 inOrder.verify(listener2).onProjectsRekeyed(same(projects));
288 inOrder.verify(listener3).onProjectsRekeyed(same(projects));
289 inOrder.verifyNoMoreInteractions();
293 public static Object[][] oneOrManyRekeyedProjects() {
294 return new Object[][] {
295 {singleton(newUniqueRekeyedProject())},
296 {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueRekeyedProject()).collect(Collectors.toSet())}
300 private static Project newUniqueProject() {
301 return Project.from(newPrivateProjectDto());
304 private static int counter = 3_989;
306 private static RekeyedProject newUniqueRekeyedProject() {
307 int base = counter++;
308 Project project = Project.from(newPrivateProjectDto());
309 return new RekeyedProject(project, base + "_old_key");