package com.amazon.mqa.datagen.rof.spy;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;

import com.beust.jcommander.internal.Lists;
import com.google.common.collect.ImmutableList;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;

/** Unit test for {@link DefaultClassSpy}. */
public final class DefaultClassSpyTest {

    /**
     * A class with out constructor.
     */
    private static final class ClassWithoutConstructor { }

    /**
     * A class with a default constructor.
     */
    private static final class ClassWithNoArgumentConstructor {

        /**
         * Instantiates a new {@link ClassWithNoArgumentConstructor}.
         */
        public ClassWithNoArgumentConstructor() { }

        /**
         * Test method.
         */
        public void ab() { }

        /**
         * Test method.
         */
        public void abc() { }
    }

    /**
     * A class with two constructors.
     */
    private static final class ClassWithTwoConstructor {

        /**
         * Instantiates a new {@link ClassWithTwoConstructor}.
         *
         * @param num dummy field.
         */
        public ClassWithTwoConstructor(final int num) { }

        /**
         * Instantiates a new {@link ClassWithTwoConstructor}.
         *
         * @param num1 dummy field 1.
         * @param num2 dummy field 2.
         */
        public ClassWithTwoConstructor(final int num1, final int num2) { }
    }

    /** Instance under test. */
    private final ClassSpy spy = new DefaultClassSpy();

    /**
     * Test data.
     *
     * @return the data.
     */
    @DataProvider
    private Object[][] classWithConstructor() {
        return new Object[][] {
                {ClassWithoutConstructor.class,
                    "private com.amazon.mqa.datagen.rof.spy.DefaultClassSpyTest$ClassWithoutConstructor()"},
                {ClassWithNoArgumentConstructor.class,
                    "public com.amazon.mqa.datagen.rof.spy.DefaultClassSpyTest$ClassWithNoArgumentConstructor()"},
                {ClassWithTwoConstructor.class,
                    "public com.amazon.mqa.datagen.rof.spy.DefaultClassSpyTest$ClassWithTwoConstructor(int)"}
        };
    }

    /**
     * Tests finding constructor for class with at least one constructor.
     *
     * @param clazz the class to test.
     * @param expectedSignature the expected constructor signature.
     * @throws NullPointerException if any argument is <code>null</code>.
     */
    @Test(dataProvider = "classWithConstructor")
    public void testFindConstructor(final Class clazz, final String expectedSignature) {
        checkNotNull(clazz, "clazz cannot be null");
        checkNotNull(expectedSignature, "expectedSignature cannot be null");

        // exercise
        final Constructor constructor = spy.findConstructor(clazz);

        // verify
        assertEquals(constructor.toGenericString(), expectedSignature, "wrong signature");
    }

    /**
     * Test data for find method.
     *
     * @return test data.
     */
    @DataProvider
    private Object[][] methodPrefix() {
        return new Object[][] {
                {"ab", ImmutableList.of("ab", "abc")},
                {"abc", ImmutableList.of("abc")}
        };
    }

    /**
     * Tests finding methods with prefix.
     *
     * @param prefix method prefix.
     * @param methodNames expected method names.
     */
    @Test(dataProvider = "methodPrefix")
    public void testFindMethodsWithPrefix(final String prefix, final List<String> methodNames) {
        checkNotNull(prefix, "prefix cannot be null");
        checkNotNull(methodNames, "methodNames cannot be null");

        // exercise
        final List<Method> methods = spy.findMethods(ClassWithNoArgumentConstructor.class, prefix);

        // verify
        assertEquals(methods.size(), methodNames.size(), "wrong methods size");

        final List<String> actualMethodNames = Lists.newArrayList();
        for (final Method method : methods) {
            actualMethodNames.add(method.getName());
        }

        assertEquals(actualMethodNames, methodNames, "wrong method names");
    }

}

@@ -238,16 +238,102 @@
 leList;%0A
+import com.google.common.collect.ImmutableSet;%0Aimport com.google.common.collect.Sets;%0A
 import o
@@ -498,16 +498,38 @@
 il.List;
+%0Aimport java.util.Set;
 %0A%0A/** Un
@@ -3422,35 +3422,34 @@
 %7B%22ab%22, Immutable
-Lis
+Se
 t.of(%22ab%22, %22abc%22
@@ -3485,19 +3485,18 @@
 mmutable
-Lis
+Se
 t.of(%22ab
@@ -3774,27 +3774,26 @@
 efix, final 
-Lis
+Se
 t%3CString%3E me
@@ -4160,19 +4160,18 @@
   final 
-Lis
+Se
 t%3CString
@@ -4196,25 +4196,22 @@
 s = 
-Lis
+Se
 ts.new
-ArrayLis
+HashSe
 t();

fix test