package com.fluentinterface;

import com.fluentinterface.domain.Person;
import com.fluentinterface.domain.PersonBuilder;
import com.fluentinterface.proxy.AttributeAccessStrategy;
import com.fluentinterface.proxy.impl.FieldAttributeAccessStrategy;
import com.fluentinterface.proxy.impl.SetterAttributeAccessStrategy;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.ArrayDeque;

import static com.fluentinterface.ReflectionBuilder.implementationFor;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;

@RunWith(Parameterized.class)
public class BuilderProxyTest {

    @Parameterized.Parameters
    public static Iterable<Object[]> strategies() {
        return asList(
                new Object[] {new FieldAttributeAccessStrategy()},
                new Object[] {new SetterAttributeAccessStrategy()}
        );
    }

    private PersonBuilder personBuilder;

    private AttributeAccessStrategy attributeAccessStrategy;

    public BuilderProxyTest(AttributeAccessStrategy attributeAccessStrategy) {
        this.attributeAccessStrategy = attributeAccessStrategy;
    }

    @Before
    public void setup() throws InstantiationException, IllegalAccessException {
        personBuilder = aPerson();
    }

    private PersonBuilder aPerson() {
        return implementationFor(PersonBuilder.class)
                .usingAttributeAccessStrategy(attributeAccessStrategy)
                .create();
    }

    @Test
    public void shouldSupportAnyMethodNamedAfterTargetPropertyName() {
        Person built = personBuilder
                .forAge(10)
                .build();

        assertThat(built.getAge(), is(10));

        built = personBuilder
                .withAge(10)
                .build();

        assertThat(built.getAge(), is(10));
    }

    @Test
    public void shouldSetPropertyValueString() {

        Person built = personBuilder
                .withName("John Smith")
                .build();

        assertThat(built.getName(), is("John Smith"));
    }

    @Test
    public void shouldSetPropertyValuePrimitive() {

        Person built = personBuilder
                .withAge(10)
                .build();

        assertThat(built.getAge(), is(10));
    }

    @Test
    public void shouldSetPropertyValueToLastOneWhenCalledMultipleTimes() {

        Person built = personBuilder
                .withAge(10)
                .withAge(20)
                .build();

        assertThat(built.getAge(), is(20));
    }

    @Test
    public void shouldSetPropertyValueArrayToList() {


        Person built = personBuilder
                .withFriends(
                        aPerson().withName("John").build(),
                        aPerson().withName("Diane").build())
                .build();

        assertThat(built.getFriends().size(), is(2));
        assertThat(built.getFriends().get(0).getName(), is("John"));
        assertThat(built.getFriends().get(1).getName(), is("Diane"));
    }

    @Test
    public void shouldSetPropertyValueArrayOfBuildersToCollection() {

        Person built = personBuilder
                .withFriends(
                        aPerson().withName("Joe"),
                        aPerson().withName("Blow"))
                .build();

        assertThat(built.getFriends().size(), is(2));
        assertThat(built.getFriends().get(0).getName(), is("Joe"));
        assertThat(built.getFriends().get(1).getName(), is("Blow"));
    }

    @Test
    public void shouldSetPropertyValueArrayOfBuildersToArray() {

        Person built = personBuilder
                .withParents(
                        aPerson().withName("Mommy"),
                        aPerson().withName("Daddy"))
                .build();

        assertThat(built.getParents().length, is(2));
        assertThat(built.getParents()[0].getName(), is("Mommy"));
        assertThat(built.getParents()[1].getName(), is("Daddy"));
    }

    @Test
    public void shouldSetPropertyValueCollectionOfBuildersToCollection() {
        Person built = personBuilder
                .withFriends(asList(
                        aPerson().withName("Joe"),
                        aPerson().withName("Blow")
                ))
                .build();

        assertThat(built.getFriends().size(), is(2));
        assertThat(built.getFriends().get(0).getName(), is("Joe"));
        assertThat(built.getFriends().get(1).getName(), is("Blow"));
    }

    @Test
    public void shouldSetPropertyValueCollectionToCollection() {

        Person built = personBuilder
                .withParents(asList(
                        aPerson().withName("Mommy").build(),
                        aPerson().withName("Daddy").build()))
                .build();

        assertThat(built.getParents().length, is(2));
        assertThat(built.getParents()[0].getName(), is("Mommy"));
        assertThat(built.getParents()[1].getName(), is("Daddy"));
    }

    @Test
    public void shouldSetPropertyValueArrayToSet() {

        Person built = personBuilder
                .withSurnames("Bill", "William", "Guillaume")
                .build();

        assertThat(built.getSurnames().size(), is(3));
        assertThat(built.getSurnames(), hasItem("Bill"));
        assertThat(built.getSurnames(), hasItem("William"));
        assertThat(built.getSurnames(), hasItem("Guillaume"));
    }

    @Test
    public void shouldSetPropertyValueArrayOfPrimitivesToArrayOfPrimitives() {

        Person built = personBuilder
                .withAgesOfMarriages(23, 45)
                .build();

        assertThat(built.getAgesOfMarriages(), is(new int[]{23, 45}));
    }

    @Test
    public void shouldSetPropertyValueBuilderToObject() {

        Person built = personBuilder
                .withPartner(
                        aPerson().withName("Diane")
                ).build();

        assertThat(built.getPartner(), notNullValue());
        assertThat(built.getPartner().getName(), is("Diane"));
    }

    @Test
    public void shouldCopyCollectionDirectlyWhenNotSupported() {

        ArrayDeque queue = new ArrayDeque();
        Person built = personBuilder.withQueue(queue).build();

        assert built.getQueue() == queue;
    }

    @Test
    public void shouldCallSpecificConstructorWhenBuildMethodCalledWithParameters() {

        Person person = personBuilder.build("Jeremy", 3);

        assertThat(person.getName(), is("Jeremy"));
        assertThat(person.getAge(), is(3));
    }

    @Test
    public void shouldCallSpecificConstructorWhenBuildMethodCalledWithParametersWithNullValue() {

        Person person = personBuilder.build("Jeremy", 3, null);

        assertThat(person.getName(), is("Jeremy"));
        assertThat(person.getAge(), is(3));
        assertThat(person.getPartner(), is(nullValue()));
    }

    @Test
    public void shouldUseBuilderWhenPassedInBuildMethodArguments() {

        Person person = personBuilder.build("Jeremy", 3, aPerson().withName("Suzana"));

        assertThat(person.getName(), is("Jeremy"));
        assertThat(person.getAge(), is(3));
        assertThat(person.getPartner().getName(), is("Suzana"));
    }

    @Test
    public void shouldSetPropertyUserSetsAnnotation() {

        Person built = personBuilder
                .named("John Smith")
                .aged(20)
                .build();

        assertThat(built.getName(), is("John Smith"));
        assertThat(built.getAge(), is(20));
    }

    @Test(expected = IllegalArgumentException.class)
    public void shouldFailWhenMultipleConstructorsMatchBuildMethodArguments() {

        personBuilder.build(null, 3);
    }

    @Test(expected = IllegalStateException.class)
    public void shouldFailWhenBuilderUsesAnUnknownProperty() {

        personBuilder.withAnUnknownProperty("fails").build();
    }

    @Test(expected = IllegalArgumentException.class)
    public void shouldFailWhenBuilderUsesAnIncompatiblePropertyType() {

        personBuilder.withAge("fails").build();
    }

    @Test(expected = IllegalStateException.class)
    public void shouldFailWhenBuilderMethodDoesNotContainPropertyName() {

        personBuilder.something("fails").build();
    }
}

@@ -6313,20 +6313,37 @@
 tion
-DirectlyWhen
+ByReferenceWhenCollectionType
 NotS

Rename test method for clarity