您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

CoreExtensionsInstallerTest.java 17KB

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