]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5077 Fix regression when a Sensor try to read "lines" measure. LinesSensor...
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 20 Jan 2015 15:45:27 +0000 (16:45 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 20 Jan 2015 15:55:06 +0000 (16:55 +0100)
14 files changed:
sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/PostJobsExecutor.java
sonar-batch/src/main/java/org/sonar/batch/scan/maven/MavenPluginsConfigurator.java
sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java
sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java
sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java
sonar-batch/src/test/java/org/sonar/batch/phases/PostJobsExecutorTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/maven/MavenPluginsConfiguratorTest.java
sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java [deleted file]
sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java [deleted file]

index 25473d9e9c70e0636c6523e93639485c3d09dcee..7ce661aaa79d12a13c1a4a4d701beb543209c182 100644 (file)
@@ -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<Decorator> select(Project project) {
-    List<Decorator> decorators = new ArrayList<Decorator>(batchExtDictionnary.select(Decorator.class, project, false));
+    List<Decorator> decorators = new ArrayList<Decorator>(batchExtDictionnary.select(Decorator.class, project, false, null));
     SetMultimap<Metric, Decorator> 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)));
       }
index 37cb0c5aa694434d2cfa7be0fb3e6b7e03166921..b9e3dd0a96013eb6346ff21f6dac15296d9fe8a6 100644 (file)
  */
 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<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
+    List<DependsUponMavenPlugin> selectedExtensions = Lists.newArrayList();
+    for (Object extension : getExtensions(null)) {
+      if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) {
+        selectedExtensions.add((DependsUponMavenPlugin) extension);
+      }
+    }
+    List<MavenPluginHandler> 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 <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project, @Nullable ExtensionMatcher matcher) {
@@ -90,6 +133,135 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio
     return result;
   }
 
+  protected List<Object> getExtensions(@Nullable Class type) {
+    List<Object> extensions = Lists.newArrayList();
+    completeBatchExtensions(componentContainer, extensions, type);
+    return extensions;
+  }
+
+  private static void completeBatchExtensions(ComponentContainer container, List<Object> extensions, @Nullable Class type) {
+    if (container != null) {
+      extensions.addAll(container.getComponentsByType(type != null ? type : BatchExtension.class));
+      completeBatchExtensions(container.getParent(), extensions, type);
+    }
+  }
+
+  public <T> Collection<T> sort(Collection<T> 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 <T> List<Object> getDependencies(T extension) {
+    List<Object> result = new ArrayList<Object>();
+    result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class));
+    return result;
+  }
+
+  /**
+   * Objects that depend upon this extension.
+   */
+  public <T> List<Object> getDependents(T extension) {
+    List<Object> result = new ArrayList<Object>();
+    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<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
+    List<Object> 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<Object> 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<Object> 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)))
index 01ae1e452d896b1744f69067e7ac103c11786e5e..6da48d12bb85314fbca20067ac3bcf29e613d2ad 100644 (file)
@@ -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;
index 2a7191d1849ae5b54f8c23371e2bee54484d33d0..a869dfb6ddd318a562a9189a4b2d5f54a8413c27 100644 (file)
@@ -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<Initializer> initializers = selector.select(Initializer.class, project, true);
+    Collection<Initializer> 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, " -> "));
index 2ce1b52f1112dbdaeb6267f4505736c34b4c06a2..a3ac80277374d29233c6d0848a6cf04b9162d14a 100644 (file)
@@ -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<PostJob> postJobs = selector.select(PostJob.class, project, true);
+    Collection<PostJob> postJobs = selector.select(PostJob.class, project, true, null);
 
     eventBus.fireEvent(new PostJobPhaseEvent(Lists.newArrayList(postJobs), true));
     execute(context, postJobs);
index 57cb762c02ff9a9e22e91eb2de2b12f6dfc346e4..010545a709cbc1366753e1633fa57bc35f7997a7 100644 (file)
@@ -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;
index 3272825fd1faf533aebf5c1a53ae058199be0b85..b32630114cbc0f10d15cb2babfca2d6d583fa4e9 100644 (file)
@@ -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;
index fee6a665262be8ff90edcedc95760aeb4c41b37b..8a0e9a247642b8281eb5d0f1f7729688c3912711 100644 (file)
@@ -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 {
index f6979d924c67967ced1ccda379e185b698c6c2a2..046f6664967da5c2a78d7dfa31c3d0d9b3e07890 100644 (file)
  */
 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<Sensor> 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<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");
+
+    BatchExtensionDictionnary 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"));
+
+    BatchExtensionDictionnary 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"});
+
+    BatchExtensionDictionnary 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();
+
+    BatchExtensionDictionnary 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() {
+    };
+
+    BatchExtensionDictionnary 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();
+
+    BatchExtensionDictionnary selector = newSelector(ok, ko);
+    List<BatchExtension> 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<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() {
+    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) {
+    }
+  }
 }
index 3f4717111466e60a6a384afc6f8274c8f93f579e..45774d11f95eae042b916b9f94932d6ef4257658 100644 (file)
@@ -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;
index e8f3371c950c4c5964c228da7589214073370454..b2617374f6b7a62e392702189a8326dfaac711ef 100644 (file)
@@ -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);
 
index c0a69019d528cf128824a3acae5b1318b7bcf179..ca1af7d653f8d5d247a3b4c093ce7d88fe50120c 100644 (file)
@@ -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 (file)
index e81f350..0000000
+++ /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 <T> Collection<T> select(Class<T> type) {
-    return select(type, null, false);
-  }
-
-  public <T> Collection<T> select(Class<T> type, @Nullable Project project, boolean sort) {
-    List<T> result = getFilteredExtensions(type, project);
-    if (sort) {
-      return sort(result);
-    }
-    return result;
-  }
-
-  public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
-    List<DependsUponMavenPlugin> selectedExtensions = Lists.newArrayList();
-    for (Object extension : getExtensions(null)) {
-      if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) {
-        selectedExtensions.add((DependsUponMavenPlugin) extension);
-      }
-    }
-    List<MavenPluginHandler> 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<Object> getExtensions(@Nullable Class type) {
-    List<Object> extensions = Lists.newArrayList();
-    completeBatchExtensions(componentContainer, extensions, type);
-    return extensions;
-  }
-
-  private static void completeBatchExtensions(ComponentContainer container, List<Object> extensions, @Nullable Class type) {
-    if (container != null) {
-      extensions.addAll(container.getComponentsByType(type != null ? type : BatchExtension.class));
-      completeBatchExtensions(container.getParent(), extensions, type);
-    }
-  }
-
-  private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project) {
-    List<T> 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 <T> Collection<T> sort(Collection<T> 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 <T> List<Object> getDependencies(T extension) {
-    List<Object> result = new ArrayList<Object>();
-    result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class));
-    return result;
-  }
-
-  /**
-   * Objects that depend upon this extension.
-   */
-  public <T> List<Object> getDependents(T extension) {
-    List<Object> result = new ArrayList<Object>();
-    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<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
-    List<Object> 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<Object> 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<Object> 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 (file)
index 4a8148b..0000000
+++ /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<Sensor> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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<BatchExtension> 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) {
-    }
-  }
-}