/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.scanner.bootstrap; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.junit.Test; import org.sonar.api.BatchExtension; import org.sonar.api.batch.BuildBreaker; import org.sonar.api.batch.CheckProject; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DependedUpon; import org.sonar.api.batch.DependsUpon; import org.sonar.api.batch.Phase; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.postjob.PostJobContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.resources.Project; import org.sonar.core.platform.ComponentContainer; import org.sonar.scanner.postjob.PostJobOptimizer; import org.sonar.scanner.sensor.DefaultSensorContext; import org.sonar.scanner.sensor.SensorOptimizer; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; public class ScannerExtensionDictionnaryTest { private ScannerExtensionDictionnary newSelector(Object... extensions) { ComponentContainer iocContainer = new ComponentContainer(); for (Object extension : extensions) { iocContainer.addSingleton(extension); } return new ScannerExtensionDictionnary(iocContainer, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class), mock(PostJobOptimizer.class)); } @Test public void testGetFilteredExtensionWithExtensionMatcher() { final Sensor sensor1 = new FakeSensor(); final Sensor sensor2 = new FakeSensor(); ScannerExtensionDictionnary selector = newSelector(sensor1, sensor2); Collection<Sensor> sensors = selector.select(Sensor.class, null, true, new ExtensionMatcher() { @Override public boolean accept(Object extension) { return extension.equals(sensor1); } }); assertThat(sensors).contains(sensor1); assertEquals(1, sensors.size()); } @Test public void testGetFilteredExtensions() { Sensor sensor1 = new FakeSensor(); Sensor sensor2 = new FakeSensor(); Decorator decorator = mock(Decorator.class); ScannerExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator); Collection<Sensor> sensors = selector.select(Sensor.class, null, true, null); assertThat(sensors).containsOnly(sensor1, sensor2); } @Test public void shouldSearchInParentContainers() { Sensor a = new FakeSensor(); Sensor b = new FakeSensor(); Sensor c = new FakeSensor(); ComponentContainer grandParent = new ComponentContainer(); grandParent.addSingleton(a); ComponentContainer parent = grandParent.createChild(); parent.addSingleton(b); ComponentContainer child = parent.createChild(); child.addSingleton(c); ScannerExtensionDictionnary dictionnary = new ScannerExtensionDictionnary(child, mock(DefaultSensorContext.class), mock(SensorOptimizer.class), mock(PostJobContext.class), mock(PostJobOptimizer.class)); assertThat(dictionnary.select(Sensor.class, null, true, null)).containsOnly(a, b, c); } @Test public void sortExtensionsByDependency() { BatchExtension a = new MethodDependentOf(null); BatchExtension b = new MethodDependentOf(a); BatchExtension c = new MethodDependentOf(b); ScannerExtensionDictionnary selector = newSelector(b, c, a); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(3); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); assertThat(extensions.get(2)).isEqualTo(c); } @Test public void useMethodAnnotationsToSortExtensions() { BatchExtension a = new GeneratesSomething("foo"); BatchExtension b = new MethodDependentOf("foo"); ScannerExtensionDictionnary selector = newSelector(a, b); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions.size()).isEqualTo(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // different initial order selector = newSelector(b, a); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test public void methodDependsUponCollection() { BatchExtension a = new GeneratesSomething("foo"); BatchExtension b = new MethodDependentOf(Arrays.asList("foo")); ScannerExtensionDictionnary selector = newSelector(a, b); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // different initial order selector = newSelector(b, a); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test public void methodDependsUponArray() { BatchExtension a = new GeneratesSomething("foo"); BatchExtension b = new MethodDependentOf(new String[] {"foo"}); ScannerExtensionDictionnary selector = newSelector(a, b); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // different initial order selector = newSelector(b, a); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test public void useClassAnnotationsToSortExtensions() { BatchExtension a = new ClassDependedUpon(); BatchExtension b = new ClassDependsUpon(); ScannerExtensionDictionnary selector = newSelector(a, b); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // different initial order selector = newSelector(b, a); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test public void useClassAnnotationsOnInterfaces() { BatchExtension a = new InterfaceDependedUpon() { }; BatchExtension b = new InterfaceDependsUpon() { }; ScannerExtensionDictionnary selector = newSelector(a, b); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // different initial order selector = newSelector(b, a); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test public void checkProject() { BatchExtension ok = new CheckProjectOK(); BatchExtension ko = new CheckProjectKO(); ScannerExtensionDictionnary selector = newSelector(ok, ko); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, new DefaultInputModule("foo"), true, null)); assertThat(extensions).hasSize(1); assertThat(extensions.get(0)).isInstanceOf(CheckProjectOK.class); } @Test public void inheritAnnotations() { BatchExtension a = new SubClass("foo"); BatchExtension b = new MethodDependentOf("foo"); ScannerExtensionDictionnary selector = newSelector(b, a); List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); // change initial order selector = newSelector(a, b); extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(2); assertThat(extensions.get(0)).isEqualTo(a); assertThat(extensions.get(1)).isEqualTo(b); } @Test(expected = IllegalStateException.class) public void annotatedMethodsCanNotBePrivate() { ScannerExtensionDictionnary selector = newSelector(); BatchExtension wrong = new BatchExtension() { @DependsUpon private Object foo() { return "foo"; } }; selector.evaluateAnnotatedClasses(wrong, DependsUpon.class); } @Test public void dependsUponPhase() { BatchExtension pre = new PreSensor(); BatchExtension analyze = new GeneratesSomething("something"); BatchExtension post = new PostSensor(); ScannerExtensionDictionnary selector = newSelector(analyze, post, pre); List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).hasSize(3); assertThat(extensions.get(0)).isEqualTo(pre); assertThat(extensions.get(1)).isEqualTo(analyze); assertThat(extensions.get(2)).isEqualTo(post); } @Test public void dependsUponInheritedPhase() { BatchExtension pre = new PreSensorSubclass(); BatchExtension analyze = new GeneratesSomething("something"); BatchExtension post = new PostSensorSubclass(); ScannerExtensionDictionnary selector = newSelector(analyze, post, pre); List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); assertThat(extensions).containsExactly(pre, analyze, post); } @Test public void buildStatusCheckersAreExecutedAfterOtherPostJobs() { BuildBreaker checker = new BuildBreaker() { public void executeOn(Project project, SensorContext context) { } }; ScannerExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob()); List extensions = Lists.newArrayList(selector.select(PostJob.class, null, true, null)); assertThat(extensions).hasSize(3); assertThat(extensions.get(2)).isEqualTo(checker); } @Test public void selectSensors() { FakeSensor oldSensor = new FakeSensor(); FakeNewSensor nonGlobalSensor = new FakeNewSensor(); FakeNewGlobalSensor globalSensor = new FakeNewGlobalSensor(); ScannerExtensionDictionnary selector = newSelector(oldSensor, nonGlobalSensor, globalSensor); // verify non-global sensors Collection<Sensor> extensions = selector.selectSensors(null, false); assertThat(extensions).hasSize(2); assertThat(extensions).contains(oldSensor); extensions.remove(oldSensor); assertThat(extensions).extracting("wrappedSensor").containsExactly(nonGlobalSensor); // verify global sensors extensions = selector.selectSensors(null, true); assertThat(extensions).extracting("wrappedSensor").containsExactly(globalSensor); } class FakeSensor implements Sensor { public void analyse(Project project, SensorContext context) { } public boolean shouldExecuteOnProject(Project project) { return true; } } class FakeNewSensor implements org.sonar.api.batch.sensor.Sensor { @Override public void describe(SensorDescriptor descriptor) { } @Override public void execute(org.sonar.api.batch.sensor.SensorContext context) { } } class FakeNewGlobalSensor implements org.sonar.api.batch.sensor.Sensor { @Override public void describe(SensorDescriptor descriptor) { descriptor.global(); } @Override public void execute(org.sonar.api.batch.sensor.SensorContext context) { } } class MethodDependentOf implements BatchExtension { private Object dep; MethodDependentOf(Object o) { this.dep = o; } @DependsUpon public Object dependsUponObject() { return dep; } } @DependsUpon("flag") class ClassDependsUpon implements BatchExtension { } @DependedUpon("flag") class ClassDependedUpon implements BatchExtension { } @DependsUpon("flag") interface InterfaceDependsUpon extends BatchExtension { } @DependedUpon("flag") interface InterfaceDependedUpon extends BatchExtension { } class GeneratesSomething implements BatchExtension { private Object gen; GeneratesSomething(Object o) { this.gen = o; } @DependedUpon public Object generates() { return gen; } } class SubClass extends GeneratesSomething { SubClass(Object o) { super(o); } } @Phase(name = Phase.Name.PRE) class PreSensor implements BatchExtension { } class PreSensorSubclass extends PreSensor { } @Phase(name = Phase.Name.POST) class PostSensor implements BatchExtension { } class PostSensorSubclass extends PostSensor { } class CheckProjectOK implements BatchExtension, CheckProject { public boolean shouldExecuteOnProject(Project project) { return true; } } class CheckProjectKO implements BatchExtension, CheckProject { public boolean shouldExecuteOnProject(Project project) { return false; } } private class FakePostJob implements PostJob { public void executeOn(Project project, SensorContext context) { } } }