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.

CoreExtensionsInstallerTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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.core.extension;
  21. import com.google.common.collect.ImmutableList;
  22. import com.google.common.collect.ImmutableSet;
  23. import com.tngtech.java.junit.dataprovider.DataProvider;
  24. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  25. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  26. import java.lang.annotation.Documented;
  27. import java.lang.annotation.ElementType;
  28. import java.lang.annotation.Retention;
  29. import java.lang.annotation.RetentionPolicy;
  30. import java.lang.annotation.Target;
  31. import java.util.Collection;
  32. import java.util.List;
  33. import java.util.function.BiConsumer;
  34. import java.util.function.Consumer;
  35. import java.util.stream.Stream;
  36. import org.junit.Test;
  37. import org.junit.runner.RunWith;
  38. import org.mockito.ArgumentCaptor;
  39. import org.mockito.InOrder;
  40. import org.mockito.Mockito;
  41. import org.picocontainer.ComponentAdapter;
  42. import org.sonar.api.Property;
  43. import org.sonar.api.SonarRuntime;
  44. import org.sonar.api.config.Configuration;
  45. import org.sonar.api.config.PropertyDefinition;
  46. import org.sonar.api.config.PropertyDefinitions;
  47. import org.sonar.api.config.internal.MapSettings;
  48. import org.sonar.core.platform.ComponentContainer;
  49. import static org.assertj.core.api.Assertions.assertThat;
  50. import static org.mockito.ArgumentMatchers.any;
  51. import static org.mockito.Mockito.doAnswer;
  52. import static org.mockito.Mockito.mock;
  53. import static org.mockito.Mockito.verify;
  54. import static org.mockito.Mockito.when;
  55. import static org.sonar.core.extension.CoreExtensionsInstaller.noAdditionalSideFilter;
  56. import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter;
  57. import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
  58. @RunWith(DataProviderRunner.class)
  59. public class CoreExtensionsInstallerTest {
  60. private SonarRuntime sonarRuntime = mock(SonarRuntime.class);
  61. private CoreExtensionRepository coreExtensionRepository = mock(CoreExtensionRepository.class);
  62. private CoreExtensionsInstaller underTest = new CoreExtensionsInstaller(sonarRuntime, coreExtensionRepository, WestSide.class) {
  63. };
  64. private ArgumentCaptor<CoreExtension.Context> contextCaptor = ArgumentCaptor.forClass(CoreExtension.Context.class);
  65. private static int name_counter = 0;
  66. @Test
  67. public void install_has_no_effect_if_CoreExtensionRepository_has_no_loaded_CoreExtension() {
  68. ComponentContainer container = new ComponentContainer();
  69. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  70. assertAddedExtensions(container, 0);
  71. }
  72. @Test
  73. public void install_calls_load_method_on_all_loaded_CoreExtension() {
  74. CoreExtension coreExtension1 = newCoreExtension();
  75. CoreExtension coreExtension2 = newCoreExtension();
  76. CoreExtension coreExtension3 = newCoreExtension();
  77. CoreExtension coreExtension4 = newCoreExtension();
  78. List<CoreExtension> coreExtensions = ImmutableList.of(coreExtension1, coreExtension2, coreExtension3, coreExtension4);
  79. InOrder inOrder = Mockito.inOrder(coreExtension1, coreExtension2, coreExtension3, coreExtension4);
  80. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(coreExtensions.stream());
  81. ComponentContainer container = new ComponentContainer();
  82. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  83. inOrder.verify(coreExtension1).load(contextCaptor.capture());
  84. inOrder.verify(coreExtension2).load(contextCaptor.capture());
  85. inOrder.verify(coreExtension3).load(contextCaptor.capture());
  86. inOrder.verify(coreExtension4).load(contextCaptor.capture());
  87. // verify each core extension gets its own Context
  88. assertThat(contextCaptor.getAllValues())
  89. .hasSameElementsAs(ImmutableSet.copyOf(contextCaptor.getAllValues()));
  90. }
  91. @Test
  92. public void install_provides_runtime_from_constructor_in_context() {
  93. CoreExtension coreExtension1 = newCoreExtension();
  94. CoreExtension coreExtension2 = newCoreExtension();
  95. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension1, coreExtension2));
  96. ComponentContainer container = new ComponentContainer();
  97. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  98. verify(coreExtension1).load(contextCaptor.capture());
  99. verify(coreExtension2).load(contextCaptor.capture());
  100. assertThat(contextCaptor.getAllValues())
  101. .extracting(CoreExtension.Context::getRuntime)
  102. .containsOnly(sonarRuntime);
  103. }
  104. @Test
  105. public void install_provides_new_Configuration_when_getBootConfiguration_is_called_and_there_is_none_in_container() {
  106. CoreExtension coreExtension1 = newCoreExtension();
  107. CoreExtension coreExtension2 = newCoreExtension();
  108. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension1, coreExtension2));
  109. ComponentContainer container = new ComponentContainer();
  110. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  111. verify(coreExtension1).load(contextCaptor.capture());
  112. verify(coreExtension2).load(contextCaptor.capture());
  113. // verify each core extension gets its own configuration
  114. assertThat(contextCaptor.getAllValues())
  115. .hasSameElementsAs(ImmutableSet.copyOf(contextCaptor.getAllValues()));
  116. }
  117. @Test
  118. public void install_provides_Configuration_from_container_when_getBootConfiguration_is_called() {
  119. CoreExtension coreExtension1 = newCoreExtension();
  120. CoreExtension coreExtension2 = newCoreExtension();
  121. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension1, coreExtension2));
  122. Configuration configuration = new MapSettings().asConfig();
  123. ComponentContainer container = new ComponentContainer();
  124. container.add(configuration);
  125. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  126. verify(coreExtension1).load(contextCaptor.capture());
  127. verify(coreExtension2).load(contextCaptor.capture());
  128. assertThat(contextCaptor.getAllValues())
  129. .extracting(CoreExtension.Context::getBootConfiguration)
  130. .containsOnly(configuration);
  131. }
  132. @Test
  133. @UseDataProvider("allMethodsToAddExtension")
  134. public void install_installs_extensions_annotated_with_expected_annotation(BiConsumer<CoreExtension.Context, Collection<Object>> extensionAdder) {
  135. List<Object> extensions = ImmutableList.of(WestSideClass.class, EastSideClass.class, OtherSideClass.class, Latitude.class);
  136. CoreExtension coreExtension = newCoreExtension(context -> extensionAdder.accept(context, extensions));
  137. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension));
  138. ComponentContainer container = new ComponentContainer();
  139. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  140. assertAddedExtensions(container, WestSideClass.class, Latitude.class);
  141. assertPropertyDefinitions(container);
  142. }
  143. @Test
  144. @UseDataProvider("allMethodsToAddExtension")
  145. public void install_does_not_install_extensions_annotated_with_expected_annotation_but_filtered_out(BiConsumer<CoreExtension.Context, Collection<Object>> extensionAdder) {
  146. List<Object> extensions = ImmutableList.of(WestSideClass.class, EastSideClass.class, OtherSideClass.class, Latitude.class);
  147. CoreExtension coreExtension = newCoreExtension(context -> extensionAdder.accept(context, extensions));
  148. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension));
  149. ComponentContainer container = new ComponentContainer();
  150. underTest.install(container, noExtensionFilter(), t -> t != Latitude.class);
  151. assertAddedExtensions(container, WestSideClass.class);
  152. assertPropertyDefinitions(container);
  153. }
  154. @Test
  155. @UseDataProvider("allMethodsToAddExtension")
  156. public void install_adds_PropertyDefinition_from_annotation_no_matter_annotations(BiConsumer<CoreExtension.Context, Collection<Object>> extensionAdder) {
  157. List<Object> extensions = ImmutableList.of(WestSidePropertyDefinition.class, EastSidePropertyDefinition.class,
  158. OtherSidePropertyDefinition.class, LatitudePropertyDefinition.class, BlankPropertyDefinition.class);
  159. CoreExtension coreExtension = newCoreExtension(context -> extensionAdder.accept(context, extensions));
  160. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension));
  161. ComponentContainer container = new ComponentContainer();
  162. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  163. assertAddedExtensions(container, WestSidePropertyDefinition.class, LatitudePropertyDefinition.class);
  164. assertPropertyDefinitions(container, "westKey", "eastKey", "otherKey", "latitudeKey", "blankKey");
  165. }
  166. @Test
  167. @UseDataProvider("allMethodsToAddExtension")
  168. public void install_adds_PropertyDefinition_from_annotation_no_matter_annotations_even_if_filtered_out(BiConsumer<CoreExtension.Context, Collection<Object>> extensionAdder) {
  169. List<Object> extensions = ImmutableList.of(WestSidePropertyDefinition.class, EastSidePropertyDefinition.class,
  170. OtherSidePropertyDefinition.class, LatitudePropertyDefinition.class, BlankPropertyDefinition.class);
  171. CoreExtension coreExtension = newCoreExtension(context -> extensionAdder.accept(context, extensions));
  172. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension));
  173. ComponentContainer container = new ComponentContainer();
  174. underTest.install(container, noExtensionFilter(), t -> false);
  175. assertAddedExtensions(container, 0);
  176. assertPropertyDefinitions(container, "westKey", "eastKey", "otherKey", "latitudeKey", "blankKey");
  177. }
  178. @Test
  179. @UseDataProvider("allMethodsToAddExtension")
  180. public void install_adds_PropertyDefinition_with_extension_name_as_default_category(BiConsumer<CoreExtension.Context, Collection<Object>> extensionAdder) {
  181. PropertyDefinition propertyDefinitionNoCategory = PropertyDefinition.builder("fooKey").build();
  182. PropertyDefinition propertyDefinitionWithCategory = PropertyDefinition.builder("barKey").category("donut").build();
  183. List<Object> extensions = ImmutableList.of(propertyDefinitionNoCategory, propertyDefinitionWithCategory);
  184. CoreExtension coreExtension = newCoreExtension(context -> extensionAdder.accept(context, extensions));
  185. when(coreExtensionRepository.loadedCoreExtensions()).thenReturn(Stream.of(coreExtension));
  186. ComponentContainer container = new ComponentContainer();
  187. underTest.install(container, noExtensionFilter(), noAdditionalSideFilter());
  188. assertAddedExtensions(container, 0);
  189. assertPropertyDefinitions(container, coreExtension, propertyDefinitionNoCategory, propertyDefinitionWithCategory);
  190. }
  191. @DataProvider
  192. public static Object[][] allMethodsToAddExtension() {
  193. BiConsumer<CoreExtension.Context, Collection<Object>> addExtension = (context, objects) -> objects.forEach(context::addExtension);
  194. BiConsumer<CoreExtension.Context, Collection<Object>> addExtensionsVarArg = (context, objects) -> {
  195. if (objects.isEmpty()) {
  196. return;
  197. }
  198. if (objects.size() == 1) {
  199. context.addExtensions(objects.iterator().next());
  200. }
  201. context.addExtensions(objects.iterator().next(), objects.stream().skip(1).toArray(Object[]::new));
  202. };
  203. BiConsumer<CoreExtension.Context, Collection<Object>> addExtensions = CoreExtension.Context::addExtensions;
  204. return new Object[][] {
  205. {addExtension},
  206. {addExtensions},
  207. {addExtensionsVarArg}
  208. };
  209. }
  210. private static void assertAddedExtensions(ComponentContainer container, int addedExtensions) {
  211. Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
  212. assertThat(adapters)
  213. .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + addedExtensions);
  214. }
  215. private static void assertAddedExtensions(ComponentContainer container, Class... classes) {
  216. Collection<ComponentAdapter<?>> adapters = container.getPicoContainer().getComponentAdapters();
  217. assertThat(adapters)
  218. .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + classes.length);
  219. Stream<Class> installedExtensions = adapters.stream()
  220. .map(t -> (Class) t.getComponentImplementation())
  221. .filter(t -> !PropertyDefinitions.class.isAssignableFrom(t) && t != ComponentContainer.class);
  222. assertThat(installedExtensions)
  223. .contains(classes)
  224. .hasSize(classes.length);
  225. }
  226. private void assertPropertyDefinitions(ComponentContainer container, String... keys) {
  227. PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class);
  228. if (keys.length == 0) {
  229. assertThat(propertyDefinitions.getAll()).isEmpty();
  230. } else {
  231. for (String key : keys) {
  232. assertThat(propertyDefinitions.get(key)).isNotNull();
  233. }
  234. }
  235. }
  236. private void assertPropertyDefinitions(ComponentContainer container, CoreExtension coreExtension, PropertyDefinition... definitions) {
  237. PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class);
  238. if (definitions.length == 0) {
  239. assertThat(propertyDefinitions.getAll()).isEmpty();
  240. } else {
  241. for (PropertyDefinition definition : definitions) {
  242. PropertyDefinition actual = propertyDefinitions.get(definition.key());
  243. assertThat(actual.category()).isEqualTo(definition.category() == null ? coreExtension.getName() : definition.category());
  244. }
  245. }
  246. }
  247. private static CoreExtension newCoreExtension() {
  248. return newCoreExtension(t -> {
  249. });
  250. }
  251. private static CoreExtension newCoreExtension(Consumer<CoreExtension.Context> loadImplementation) {
  252. CoreExtension res = mock(CoreExtension.class);
  253. when(res.getName()).thenReturn("name_" + name_counter);
  254. name_counter++;
  255. doAnswer(invocation -> {
  256. CoreExtension.Context context = invocation.getArgument(0);
  257. loadImplementation.accept(context);
  258. return null;
  259. }).when(res).load(any());
  260. return res;
  261. }
  262. @Documented
  263. @Retention(RetentionPolicy.RUNTIME)
  264. @Target(ElementType.TYPE)
  265. public @interface WestSide {
  266. }
  267. @Documented
  268. @Retention(RetentionPolicy.RUNTIME)
  269. @Target(ElementType.TYPE)
  270. public @interface EastSide {
  271. }
  272. @Documented
  273. @Retention(RetentionPolicy.RUNTIME)
  274. @Target(ElementType.TYPE)
  275. public @interface OtherSide {
  276. }
  277. @WestSide
  278. public static class WestSideClass {
  279. }
  280. @EastSide
  281. public static class EastSideClass {
  282. }
  283. @OtherSide
  284. public static class OtherSideClass {
  285. }
  286. @WestSide
  287. @EastSide
  288. public static class Latitude {
  289. }
  290. @Property(key = "westKey", name = "westName")
  291. @WestSide
  292. public static class WestSidePropertyDefinition {
  293. }
  294. @Property(key = "eastKey", name = "eastName")
  295. @EastSide
  296. public static class EastSidePropertyDefinition {
  297. }
  298. @Property(key = "otherKey", name = "otherName")
  299. @OtherSide
  300. public static class OtherSidePropertyDefinition {
  301. }
  302. @Property(key = "latitudeKey", name = "latitudeName")
  303. @WestSide
  304. @EastSide
  305. public static class LatitudePropertyDefinition {
  306. }
  307. @Property(key = "blankKey", name = "blankName")
  308. public static class BlankPropertyDefinition {
  309. }
  310. }