package com.tinkerpop.blueprints;

import com.tinkerpop.blueprints.util.FeatureDescriptor;
import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
 * All features should be prefixed with the word "supports" and return boolean.  Furthermore, all should have an
 * FeatureDescriptor annotation with a "name" that represents the suffix after "supports" in the same case as the
 * method.  A String name of the feature should be supplied as a public static variable and be equal to the value
 * of the "name" in all upper case.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public class FeaturesConventionTest {
    private static final String FEATURE_METHOD_PREFIX = "supports";
    private static final String FEATURE_FIELD_PREFIX = "FEATURE_";

    private static final String ERROR_FIELD = "Feature [%s] must have a field decleared with the name of the feature as 'public static final'";

    @Test
    public void shouldFollowConventionsForGraphFeatures() {
        final Class<Graph.Features> featuresClass = Graph.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForPropertyFeatures() {
        final Class<Property.Features> featuresClass = Property.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForEdgeFeatures() {
        final Class<Edge.Features> featuresClass = Edge.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForTransactionFeatures() {
        final Class<Transaction.Features> featuresClass = Transaction.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    @Test
    public void shouldFollowConventionsForVertexFeatures() {
        final Class<Vertex.Features> featuresClass = Vertex.Features.class;
        Arrays.asList(featuresClass.getMethods()).stream()
                .filter(FeaturesConventionTest::chooseFeatureMethod)
                .forEach(FeaturesConventionTest::assertFeatureConvention);
    }

    private static String convertToUnderscore(final String text) {
        final String regex = "([a-z])([A-Z])";
        final String replacement = "$1_$2";
        final String underscored = text.replaceAll(regex, replacement);
        return underscored.substring(0, underscored.length());
    }

    private static boolean chooseFeatureMethod(Method m) {
        return m.getName().startsWith(FEATURE_METHOD_PREFIX) && !m.getName().equals(FEATURE_METHOD_PREFIX);
    }

    private static void assertFeatureConvention(final Method m) {
        final FeatureDescriptor annotation = m.getAnnotation(FeatureDescriptor.class);
        final Class featuresClass = m.getDeclaringClass();

        // all "features" need an annotation
        assertNotNull(annotation);

        // needs to match pattern of "support" followed by "name" value in annotation
        assertEquals(m.getName(), FEATURE_METHOD_PREFIX + annotation.name());

        try {
            final Field f = featuresClass.getField(FEATURE_FIELD_PREFIX + convertToUnderscore(annotation.name()).toUpperCase());
            assertEquals(annotation.name(), f.get(null));
        } catch (Exception e) {
            fail(String.format(ERROR_FIELD, annotation.name()));
        }
    }
}

@@ -111,42 +111,82 @@
 st;%0A
-%0A
 import 
-java.lang.reflect.Metho
+org.junit.runner.RunWith;%0Aimport org.junit.runners.Parameterize
 d;%0A
+%0A
 impo
@@ -211,15 +211,13 @@
 ct.M
-odifier
+ethod
 ;%0Aim
@@ -367,51 +367,8 @@
 ll;%0A
-import static org.junit.Assert.assertTrue;%0A
 impo
@@ -848,16 +848,46 @@
 om)%0A */%0A
+@RunWith(Parameterized.class)%0A
 public c
@@ -1134,17 +1134,16 @@
 eld decl
-e
 ared wit
@@ -1205,840 +1205,277 @@
    @
-Test%0A    public void shouldFollowConventionsForGraphFeatures() %7B%0A        final Class%3CGraph.Features%3E featuresClass = Graph.Features.class;%0A        Arrays.asList(featuresClass.getMethods()).stream()%0A                .filter(FeaturesConventionTest::chooseFeatureMethod)%0A                .forEach(FeaturesConventionTest::assertFeatureConvention);%0A    %7D%0A%0A    @Test%0A    public void shouldFollowConventionsForPropertyFeatures() %7B%0A        final Class%3CProperty.Features%3E featuresClass = Property.Features.class;%0A        Arrays.asList(featuresClass.getMethods()).stream()%0A                .filter(FeaturesConventionTest::chooseFeatureMethod)%0A                .forEach(FeaturesConventionTest::assertFeatureConvention);%0A    %7D%0A%0A    @Test%0A    public void shouldFollowConventionsForEdgeFeatures() %7B%0A        final Class%3CEdge.Features%3E featuresClass = 
+Parameterized.Parameters(name = %22%7Bindex%7D: %7B0%7D.test() = follows conventions%22)%0A    public static Iterable%3CObject%5B%5D%3E data() %7B%0A        return Arrays.asList(new Object%5B%5D%5B%5D %7B%0A                %7BGraph.Features.class%7D,%0A                %7BProperty.Features.class%7D,%0A                %7B
 Edge
@@ -1481,33 +1481,34 @@
 e.Features.class
-;
+%7D,
 %0A        Arrays.
@@ -1504,344 +1504,58 @@
     
-Arrays.asList(featuresClass.getMethods()).stream()%0A                .filter(FeaturesConventionTest::chooseFeatureMethod)%0A                .forEach(FeaturesConventionTest::assertFeatureConvention);%0A    %7D%0A%0A    @Test%0A    public void shouldFollowConventionsForTransactionFeatures() %7B%0A        final Class%3CTransaction.Features%3E featuresClass = 
+        %7BVertex.Features.class%7D,%0A                %7B
 Tran
@@ -1568,33 +1568,33 @@
 n.Features.class
-;
+%7D
 %0A        Arrays.
@@ -1590,208 +1590,93 @@
     
-Arrays.asList(featuresClass.getMethods()).stream()%0A  
+%7D);%0A
     
+%7D%0A%0A
     
-      .filter(FeaturesConventionTest::chooseFeatureMethod)%0A                .forEach(FeaturesConventionTest::assertFeatureConvention);%0A    %7D
+@Parameterized.Parameter(value = 0)%0A    public Class%3C?%3E featuresClass;
 %0A%0A  
@@ -1725,22 +1725,16 @@
 tionsFor
-Vertex
 Features
@@ -1742,84 +1742,8 @@
 ) %7B%0A
-        final Class%3CVertex.Features%3E featuresClass = Vertex.Features.class;%0A
     

Refactor Feature convention tests to use parameters.