From fb21d4cbafc3a5e7b9c63c0df300129d9135045b Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Tue, 20 Jan 2015 16:45:27 +0100 Subject: [PATCH] SONAR-5077 Fix regression when a Sensor try to read "lines" measure. LinesSensor forced to be executed first. --- .../org/sonar/batch/DecoratorsSelector.java | 6 +- .../bootstrap/BatchExtensionDictionnary.java | 192 ++++++++- .../batch/phases/DecoratorsExecutor.java | 2 +- .../batch/phases/InitializersExecutor.java | 4 +- .../sonar/batch/phases/PostJobsExecutor.java | 6 +- .../scan/maven/MavenPluginsConfigurator.java | 2 +- .../org/sonar/batch/source/LinesSensor.java | 2 + .../sonar/batch/DecoratorsSelectorTest.java | 4 +- .../BatchExtensionDictionnaryTest.java | 343 +++++++++++++++- .../batch/phases/DecoratorsExecutorTest.java | 2 +- .../batch/phases/PostJobsExecutorTest.java | 4 +- .../maven/MavenPluginsConfiguratorTest.java | 3 +- .../api/batch/BatchExtensionDictionnary.java | 248 ------------ .../batch/BatchExtensionDictionnaryTest.java | 379 ------------------ 14 files changed, 538 insertions(+), 659 deletions(-) delete mode 100644 sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java delete mode 100644 sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java diff --git a/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java index 25473d9e9c7..7ce661aaa79 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java @@ -21,10 +21,10 @@ package org.sonar.batch; import com.google.common.collect.HashMultimap; import com.google.common.collect.SetMultimap; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Decorator; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import java.util.ArrayList; import java.util.Collection; @@ -39,9 +39,9 @@ public final class DecoratorsSelector { } public Collection select(Project project) { - List decorators = new ArrayList(batchExtDictionnary.select(Decorator.class, project, false)); + List decorators = new ArrayList(batchExtDictionnary.select(Decorator.class, project, false, null)); SetMultimap decoratorsByGeneratedMetric = getDecoratorsByMetric(decorators); - for (Metric metric : batchExtDictionnary.select(Metric.class)) { + for (Metric metric : batchExtDictionnary.select(Metric.class, null, false, null)) { if (metric.getFormula() != null) { decorators.add(new FormulaDecorator(metric, decoratorsByGeneratedMetric.get(metric))); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java index 37cb0c5aa69..b9e3dd0a960 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java @@ -19,34 +19,49 @@ */ package org.sonar.batch.bootstrap; -import org.sonar.batch.sensor.AnalyzerOptimizer; - +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import org.apache.commons.lang.ClassUtils; +import org.sonar.api.BatchExtension; import org.sonar.api.batch.CheckProject; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; import org.sonar.api.batch.Phase; +import org.sonar.api.batch.maven.DependsUponMavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; +import org.sonar.api.utils.AnnotationUtils; +import org.sonar.api.utils.dag.DirectAcyclicGraph; import org.sonar.batch.scan.SensorWrapper; +import org.sonar.batch.sensor.AnalyzerOptimizer; import org.sonar.batch.sensor.DefaultSensorContext; import javax.annotation.Nullable; +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; /** * @since 2.6 */ -public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensionDictionnary { +public class BatchExtensionDictionnary { - private SensorContext context; - private AnalyzerOptimizer analyzerOptimizer; + private final ComponentContainer componentContainer; + private final SensorContext context; + private final AnalyzerOptimizer analyzerOptimizer; public BatchExtensionDictionnary(ComponentContainer componentContainer, DefaultSensorContext context, AnalyzerOptimizer analyzerOptimizer) { - super(componentContainer); + this.componentContainer = componentContainer; this.context = context; this.analyzerOptimizer = analyzerOptimizer; } @@ -59,13 +74,41 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio return result; } - @Override - protected Phase.Name evaluatePhase(Object extension) { + public Collection selectMavenPluginHandlers(Project project) { + List selectedExtensions = Lists.newArrayList(); + for (Object extension : getExtensions(null)) { + if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) { + selectedExtensions.add((DependsUponMavenPlugin) extension); + } + } + List handlers = Lists.newArrayList(); + for (DependsUponMavenPlugin extension : selectedExtensions) { + MavenPluginHandler handler = extension.getMavenPluginHandler(project); + if (handler != null) { + boolean ok = true; + if (handler instanceof CheckProject) { + ok = ((CheckProject) handler).shouldExecuteOnProject(project); + } + if (ok) { + handlers.add(handler); + } + } + } + return handlers; + } + + private Phase.Name evaluatePhase(Object extension) { + Object extensionToEvaluate; if (extension instanceof SensorWrapper) { - return super.evaluatePhase(((SensorWrapper) extension).wrappedSensor()); + extensionToEvaluate = ((SensorWrapper) extension).wrappedSensor(); } else { - return super.evaluatePhase(extension); + extensionToEvaluate = extension; + } + Phase phaseAnnotation = AnnotationUtils.getAnnotation(extensionToEvaluate, Phase.class); + if (phaseAnnotation != null) { + return phaseAnnotation.name(); } + return Phase.Name.DEFAULT; } private List getFilteredExtensions(Class type, @Nullable Project project, @Nullable ExtensionMatcher matcher) { @@ -90,6 +133,135 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio return result; } + protected List getExtensions(@Nullable Class type) { + List extensions = Lists.newArrayList(); + completeBatchExtensions(componentContainer, extensions, type); + return extensions; + } + + private static void completeBatchExtensions(ComponentContainer container, List extensions, @Nullable Class type) { + if (container != null) { + extensions.addAll(container.getComponentsByType(type != null ? type : BatchExtension.class)); + completeBatchExtensions(container.getParent(), extensions, type); + } + } + + public Collection sort(Collection extensions) { + DirectAcyclicGraph dag = new DirectAcyclicGraph(); + + for (T extension : extensions) { + dag.add(extension); + for (Object dependency : getDependencies(extension)) { + dag.add(extension, dependency); + } + for (Object generates : getDependents(extension)) { + dag.add(generates, extension); + } + completePhaseDependencies(dag, extension); + } + List sortedList = dag.sort(); + + return Collections2.filter(sortedList, Predicates.in(extensions)); + } + + /** + * Extension dependencies + */ + private List getDependencies(T extension) { + List result = new ArrayList(); + result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class)); + return result; + } + + /** + * Objects that depend upon this extension. + */ + public List getDependents(T extension) { + List result = new ArrayList(); + result.addAll(evaluateAnnotatedClasses(extension, DependedUpon.class)); + return result; + } + + private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) { + Phase.Name phase = evaluatePhase(extension); + dag.add(extension, phase); + for (Phase.Name name : Phase.Name.values()) { + if (phase.compareTo(name) < 0) { + dag.add(name, extension); + } else if (phase.compareTo(name) > 0) { + dag.add(extension, name); + } + } + } + + protected List evaluateAnnotatedClasses(Object extension, Class annotation) { + List results = Lists.newArrayList(); + Class aClass = extension.getClass(); + while (aClass != null) { + evaluateClass(aClass, annotation, results); + + for (Method method : aClass.getDeclaredMethods()) { + if (method.getAnnotation(annotation) != null) { + checkAnnotatedMethod(method); + evaluateMethod(extension, method, results); + } + } + aClass = aClass.getSuperclass(); + } + + return results; + } + + private void evaluateClass(Class extensionClass, Class annotationClass, List results) { + Annotation annotation = extensionClass.getAnnotation(annotationClass); + if (annotation != null) { + if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) { + results.addAll(Arrays.asList(((DependsUpon) annotation).value())); + + } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) { + results.addAll(Arrays.asList(((DependedUpon) annotation).value())); + } + } + + Class[] interfaces = extensionClass.getInterfaces(); + for (Class anInterface : interfaces) { + evaluateClass(anInterface, annotationClass, results); + } + } + + private void evaluateMethod(Object extension, Method method, List results) { + try { + Object result = method.invoke(extension); + if (result != null) { + if (result instanceof Class) { + results.addAll(componentContainer.getComponentsByType((Class) result)); + + } else if (result instanceof Collection) { + results.addAll((Collection) result); + + } else if (result.getClass().isArray()) { + for (int i = 0; i < Array.getLength(result); i++) { + results.add(Array.get(result, i)); + } + + } else { + results.add(result); + } + } + } catch (Exception e) { + throw new IllegalStateException("Can not invoke method " + method, e); + } + } + + private void checkAnnotatedMethod(Method method) { + if (!Modifier.isPublic(method.getModifiers())) { + throw new IllegalStateException("Annotated method must be public:" + method); + } + if (method.getParameterTypes().length > 0) { + throw new IllegalStateException("Annotated method must not have parameters:" + method); + } + } + private boolean shouldKeep(Class type, Object extension, @Nullable Project project, @Nullable ExtensionMatcher matcher) { boolean keep = (ClassUtils.isAssignable(extension.getClass(), type) || (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class))) diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java index 01ae1e452d8..6da48d12bb8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java @@ -21,7 +21,6 @@ package org.sonar.batch.phases; import com.google.common.collect.Lists; import org.sonar.api.BatchComponent; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.SonarIndex; @@ -32,6 +31,7 @@ import org.sonar.api.utils.MessageException; import org.sonar.api.utils.SonarException; import org.sonar.batch.DecoratorsSelector; import org.sonar.batch.DefaultDecoratorContext; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.events.EventBus; import org.sonar.batch.scan.measure.MeasureCache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java index 2a7191d1849..a869dfb6ddd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java @@ -23,12 +23,12 @@ import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Initializer; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.resources.Project; import org.sonar.api.utils.TimeProfiler; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.events.EventBus; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.maven.MavenPluginExecutor; @@ -55,7 +55,7 @@ public class InitializersExecutor { } public void execute() { - Collection initializers = selector.select(Initializer.class, project, true); + Collection initializers = selector.select(Initializer.class, project, true, null); eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), true)); if (LOG.isDebugEnabled()) { LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PostJobsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PostJobsExecutor.java index 2ce1b52f111..a3ac8027737 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PostJobsExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PostJobsExecutor.java @@ -24,12 +24,12 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.events.EventBus; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.maven.MavenPluginExecutor; @@ -46,7 +46,7 @@ public class PostJobsExecutor implements BatchComponent { private final EventBus eventBus; public PostJobsExecutor(BatchExtensionDictionnary selector, Project project, DefaultModuleFileSystem fs, MavenPluginExecutor mavenExecutor, - EventBus eventBus) { + EventBus eventBus) { this.selector = selector; this.project = project; this.fs = fs; @@ -55,7 +55,7 @@ public class PostJobsExecutor implements BatchComponent { } public void execute(SensorContext context) { - Collection postJobs = selector.select(PostJob.class, project, true); + Collection postJobs = selector.select(PostJob.class, project, true, null); eventBus.fireEvent(new PostJobPhaseEvent(Lists.newArrayList(postJobs), true)); execute(context, postJobs); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/maven/MavenPluginsConfigurator.java b/sonar-batch/src/main/java/org/sonar/batch/scan/maven/MavenPluginsConfigurator.java index 57cb762c02f..010545a709c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/maven/MavenPluginsConfigurator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/maven/MavenPluginsConfigurator.java @@ -24,10 +24,10 @@ import org.apache.maven.project.MavenProject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.maven.MavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import java.io.File; import java.io.FileOutputStream; diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java b/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java index 3272825fd1f..b32630114cb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.source; +import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; @@ -28,6 +29,7 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; +@Phase(name = Phase.Name.PRE) public final class LinesSensor implements Sensor { private final FileSystem fs; diff --git a/sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java b/sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java index fee6a665262..8a0e9a24764 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java @@ -21,7 +21,6 @@ package org.sonar.batch; import com.google.common.collect.Iterables; import org.junit.Test; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.DependedUpon; @@ -33,6 +32,7 @@ import org.sonar.api.measures.Metric; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import java.util.Arrays; import java.util.Collection; @@ -81,7 +81,7 @@ public class DecoratorsSelectorTest { for (Object extension : extensions) { ioc.addSingleton(extension); } - return new BatchExtensionDictionnary(ioc); + return new BatchExtensionDictionnary(ioc, null, null); } class FakeFormula implements Formula { diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java index f6979d924c6..046f6664967 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java @@ -19,17 +19,28 @@ */ package org.sonar.batch.bootstrap; -import org.sonar.batch.sensor.AnalyzerOptimizer; - +import com.google.common.collect.Lists; 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.platform.ComponentContainer; import org.sonar.api.resources.Project; +import org.sonar.batch.sensor.AnalyzerOptimizer; import org.sonar.batch.sensor.DefaultSensorContext; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -61,16 +72,338 @@ public class BatchExtensionDictionnaryTest { assertEquals(1, sensors.size()); } + @Test + public void testGetFilteredExtensions() { + Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor(); + Decorator decorator = mock(Decorator.class); + + BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator); + Collection sensors = selector.select(Sensor.class, null, true, null); + + assertThat(sensors).containsOnly(sensor1, sensor2); + } + + @Test + public void shouldSearchInParentContainers() { + BatchExtension a = new FakeSensor(); + BatchExtension b = new FakeSensor(); + BatchExtension c = new FakeSensor(); + + ComponentContainer grandParent = new ComponentContainer(); + grandParent.addSingleton(a); + + ComponentContainer parent = grandParent.createChild(); + parent.addSingleton(b); + + ComponentContainer child = parent.createChild(); + child.addSingleton(c); + + BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child, mock(DefaultSensorContext.class), mock(AnalyzerOptimizer.class)); + assertThat(dictionnary.select(BatchExtension.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); + + BatchExtensionDictionnary selector = newSelector(b, c, a); + List 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"); + + BatchExtensionDictionnary selector = newSelector(a, b); + List 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")); + + BatchExtensionDictionnary selector = newSelector(a, b); + List 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"}); + + BatchExtensionDictionnary selector = newSelector(a, b); + List 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(); + + BatchExtensionDictionnary selector = newSelector(a, b); + List 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() { + }; + + BatchExtensionDictionnary selector = newSelector(a, b); + List 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(); + + BatchExtensionDictionnary selector = newSelector(ok, ko); + List extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), 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"); + + BatchExtensionDictionnary selector = newSelector(b, a); + List 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() { + BatchExtensionDictionnary 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(); + + BatchExtensionDictionnary 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(); + + BatchExtensionDictionnary 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 buildStatusCheckersAreExecutedAfterOtherPostJobs() { + BuildBreaker checker = new BuildBreaker() { + public void executeOn(Project project, SensorContext context) { + } + }; + + BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob()); + List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true, null)); + + assertThat(extensions).hasSize(3); + assertThat(extensions.get(2)).isEqualTo(checker); + } + class FakeSensor implements Sensor { - @Override - public void analyse(Project project, org.sonar.api.batch.SensorContext context) { + public void analyse(Project project, SensorContext context) { + + } + + public boolean shouldExecuteOnProject(Project project) { + return true; + } + } + + 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; } - @Override + @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) { + } + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java index 3f471711146..45774d11f95 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java @@ -20,7 +20,6 @@ package org.sonar.batch.phases; import org.junit.Test; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.SonarIndex; @@ -30,6 +29,7 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.utils.SonarException; import org.sonar.batch.DefaultDecoratorContext; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.events.EventBus; import org.sonar.batch.scan.measure.MeasureCache; diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java index e8f3371c950..b2617374f6b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java @@ -21,10 +21,10 @@ package org.sonar.batch.phases; import org.junit.Before; import org.junit.Test; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.events.EventBus; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.maven.MavenPluginExecutor; @@ -52,7 +52,7 @@ public class PostJobsExecutorTest { @Test public void should_execute_post_jobs() { - when(selector.select(PostJob.class, project, true)).thenReturn(Arrays.asList(job1, job2)); + when(selector.select(PostJob.class, project, true, null)).thenReturn(Arrays.asList(job1, job2)); executor.execute(context); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/maven/MavenPluginsConfiguratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/maven/MavenPluginsConfiguratorTest.java index c0a69019d52..ca1af7d653f 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/maven/MavenPluginsConfiguratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/maven/MavenPluginsConfiguratorTest.java @@ -21,12 +21,11 @@ package org.sonar.batch.scan.maven; import org.junit.Test; import org.mockito.ArgumentMatcher; -import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.maven.MavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.resources.Project; import org.sonar.api.test.MavenTestUtils; -import org.sonar.batch.scan.maven.MavenPluginsConfigurator; +import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import java.util.Arrays; diff --git a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java deleted file mode 100644 index e81f3509a55..00000000000 --- a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.api.batch; - -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import org.apache.commons.lang.ClassUtils; -import org.sonar.api.BatchExtension; -import org.sonar.api.batch.maven.DependsUponMavenPlugin; -import org.sonar.api.batch.maven.MavenPluginHandler; -import org.sonar.api.platform.ComponentContainer; -import org.sonar.api.resources.Project; -import org.sonar.api.utils.AnnotationUtils; -import org.sonar.api.utils.dag.DirectAcyclicGraph; - -import javax.annotation.Nullable; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * @since 1.11 - * @deprecated since 2.6 was only used by views - */ -@Deprecated -public class BatchExtensionDictionnary { - - private final ComponentContainer componentContainer; - - public BatchExtensionDictionnary(ComponentContainer componentContainer) { - this.componentContainer = componentContainer; - } - - public Collection select(Class type) { - return select(type, null, false); - } - - public Collection select(Class type, @Nullable Project project, boolean sort) { - List result = getFilteredExtensions(type, project); - if (sort) { - return sort(result); - } - return result; - } - - public Collection selectMavenPluginHandlers(Project project) { - List selectedExtensions = Lists.newArrayList(); - for (Object extension : getExtensions(null)) { - if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) { - selectedExtensions.add((DependsUponMavenPlugin) extension); - } - } - List handlers = Lists.newArrayList(); - for (DependsUponMavenPlugin extension : selectedExtensions) { - MavenPluginHandler handler = extension.getMavenPluginHandler(project); - if (handler != null) { - boolean ok = true; - if (handler instanceof CheckProject) { - ok = ((CheckProject) handler).shouldExecuteOnProject(project); - } - if (ok) { - handlers.add(handler); - } - } - - } - return handlers; - } - - protected List getExtensions(@Nullable Class type) { - List extensions = Lists.newArrayList(); - completeBatchExtensions(componentContainer, extensions, type); - return extensions; - } - - private static void completeBatchExtensions(ComponentContainer container, List extensions, @Nullable Class type) { - if (container != null) { - extensions.addAll(container.getComponentsByType(type != null ? type : BatchExtension.class)); - completeBatchExtensions(container.getParent(), extensions, type); - } - } - - private List getFilteredExtensions(Class type, @Nullable Project project) { - List result = Lists.newArrayList(); - for (Object extension : getExtensions(type)) { - if (shouldKeep(type, extension, project)) { - result.add((T) extension); - } - } - return result; - } - - private boolean shouldKeep(Class type, Object extension, @Nullable Project project) { - boolean keep = ClassUtils.isAssignable(extension.getClass(), type); - if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { - keep = ((CheckProject) extension).shouldExecuteOnProject(project); - } - return keep; - } - - public Collection sort(Collection extensions) { - DirectAcyclicGraph dag = new DirectAcyclicGraph(); - - for (T extension : extensions) { - dag.add(extension); - for (Object dependency : getDependencies(extension)) { - dag.add(extension, dependency); - } - for (Object generates : getDependents(extension)) { - dag.add(generates, extension); - } - completePhaseDependencies(dag, extension); - } - List sortedList = dag.sort(); - - return Collections2.filter(sortedList, Predicates.in(extensions)); - } - - /** - * Extension dependencies - */ - private List getDependencies(T extension) { - List result = new ArrayList(); - result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class)); - return result; - } - - /** - * Objects that depend upon this extension. - */ - public List getDependents(T extension) { - List result = new ArrayList(); - result.addAll(evaluateAnnotatedClasses(extension, DependedUpon.class)); - return result; - } - - private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) { - Phase.Name phase = evaluatePhase(extension); - dag.add(extension, phase); - for (Phase.Name name : Phase.Name.values()) { - if (phase.compareTo(name) < 0) { - dag.add(name, extension); - } else if (phase.compareTo(name) > 0) { - dag.add(extension, name); - } - } - } - - protected List evaluateAnnotatedClasses(Object extension, Class annotation) { - List results = Lists.newArrayList(); - Class aClass = extension.getClass(); - while (aClass != null) { - evaluateClass(aClass, annotation, results); - - for (Method method : aClass.getDeclaredMethods()) { - if (method.getAnnotation(annotation) != null) { - checkAnnotatedMethod(method); - evaluateMethod(extension, method, results); - } - } - aClass = aClass.getSuperclass(); - } - - return results; - } - - private void evaluateClass(Class extensionClass, Class annotationClass, List results) { - Annotation annotation = extensionClass.getAnnotation(annotationClass); - if (annotation != null) { - if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) { - results.addAll(Arrays.asList(((DependsUpon) annotation).value())); - - } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) { - results.addAll(Arrays.asList(((DependedUpon) annotation).value())); - } - } - - Class[] interfaces = extensionClass.getInterfaces(); - for (Class anInterface : interfaces) { - evaluateClass(anInterface, annotationClass, results); - } - } - - protected Phase.Name evaluatePhase(Object extension) { - Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class); - if (phaseAnnotation != null) { - return phaseAnnotation.name(); - } - return Phase.Name.DEFAULT; - } - - private void evaluateMethod(Object extension, Method method, List results) { - try { - Object result = method.invoke(extension); - if (result != null) { - if (result instanceof Class) { - results.addAll(componentContainer.getComponentsByType((Class) result)); - - } else if (result instanceof Collection) { - results.addAll((Collection) result); - - } else if (result.getClass().isArray()) { - for (int i = 0; i < Array.getLength(result); i++) { - results.add(Array.get(result, i)); - } - - } else { - results.add(result); - } - } - } catch (Exception e) { - throw new IllegalStateException("Can not invoke method " + method, e); - } - } - - private void checkAnnotatedMethod(Method method) { - if (!Modifier.isPublic(method.getModifiers())) { - throw new IllegalStateException("Annotated method must be public:" + method); - } - if (method.getParameterTypes().length > 0) { - throw new IllegalStateException("Annotated method must not have parameters:" + method); - } - } -} diff --git a/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java b/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java deleted file mode 100644 index 4a8148b3c86..00000000000 --- a/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.api.batch; - -import com.google.common.collect.Lists; -import org.junit.Test; -import org.sonar.api.BatchExtension; -import org.sonar.api.platform.ComponentContainer; -import org.sonar.api.resources.Project; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -public class BatchExtensionDictionnaryTest { - - private BatchExtensionDictionnary newSelector(BatchExtension... extensions) { - ComponentContainer iocContainer = new ComponentContainer(); - for (BatchExtension extension : extensions) { - iocContainer.addSingleton(extension); - } - return new BatchExtensionDictionnary(iocContainer); - } - - @Test - public void testGetFilteredExtensions() { - Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor(); - Decorator decorator = mock(Decorator.class); - - BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator); - Collection sensors = selector.select(Sensor.class, null, true); - - assertThat(sensors).containsOnly(sensor1, sensor2); - } - - @Test - public void shouldSearchInParentContainers() { - BatchExtension a = new FakeSensor(); - BatchExtension b = new FakeSensor(); - BatchExtension c = new FakeSensor(); - - ComponentContainer grandParent = new ComponentContainer(); - grandParent.addSingleton(a); - - ComponentContainer parent = grandParent.createChild(); - parent.addSingleton(b); - - ComponentContainer child = parent.createChild(); - child.addSingleton(c); - - BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child); - assertThat(dictionnary.select(BatchExtension.class)).containsOnly(a, b, c); - } - - @Test - public void sortExtensionsByDependency() { - BatchExtension a = new MethodDependentOf(null); - BatchExtension b = new MethodDependentOf(a); - BatchExtension c = new MethodDependentOf(b); - - BatchExtensionDictionnary selector = newSelector(b, c, a); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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"); - - BatchExtensionDictionnary selector = newSelector(a, b); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - 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")); - - BatchExtensionDictionnary selector = newSelector(a, b); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - 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"}); - - BatchExtensionDictionnary selector = newSelector(a, b); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - 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(); - - BatchExtensionDictionnary selector = newSelector(a, b); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - 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() { - }; - - BatchExtensionDictionnary selector = newSelector(a, b); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - 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(); - - BatchExtensionDictionnary selector = newSelector(ok, ko); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), true)); - - 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"); - - BatchExtensionDictionnary selector = newSelector(b, a); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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)); - - assertThat(extensions).hasSize(2); - assertThat(extensions.get(0)).isEqualTo(a); - assertThat(extensions.get(1)).isEqualTo(b); - } - - @Test(expected = IllegalStateException.class) - public void annotatedMethodsCanNotBePrivate() { - BatchExtensionDictionnary 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(); - - BatchExtensionDictionnary selector = newSelector(analyze, post, pre); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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(); - - BatchExtensionDictionnary selector = newSelector(analyze, post, pre); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - 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 buildStatusCheckersAreExecutedAfterOtherPostJobs() { - BuildBreaker checker = new BuildBreaker() { - public void executeOn(Project project, SensorContext context) { - } - }; - - BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob()); - List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true)); - - assertThat(extensions).hasSize(3); - assertThat(extensions.get(2)).isEqualTo(checker); - } - - class FakeSensor implements Sensor { - - public void analyse(Project project, SensorContext context) { - - } - - public boolean shouldExecuteOnProject(Project project) { - return true; - } - } - - 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) { - } - } -} -- 2.39.5