package br.com.caelum.iogi;
import static org.hamcrest.Matchers.emptyArray;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import br.com.caelum.iogi.conversion.StringConverter;
import br.com.caelum.iogi.parameters.Parameter;
import br.com.caelum.iogi.parameters.Parameters;
import br.com.caelum.iogi.reflection.ParanamerParameterNamesProvider;
import br.com.caelum.iogi.reflection.Target;
import br.com.caelum.iogi.spi.DependencyProvider;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class EmptyObjectsProviderTest {
private Mockery context = new Mockery();
private DependencyProvider underlying = context.mock(DependencyProvider.class);
private Class<?> classThatMayBeEmpty = MayBeEmpty.class;
private Supplier<MayBeEmpty> emptyValueSupplier = new Supplier<MayBeEmpty>() {
public MayBeEmpty get() {
return new MayBeEmpty();
}
};
private Target<?> targetForEmptyObject = Target.create(classThatMayBeEmpty, "");
private EmptyObjectsProvider provider =
new EmptyObjectsProvider(underlying, ImmutableMap.of(classThatMayBeEmpty, emptyValueSupplier));
public void assumeUnderlyingCannotProvideAnything() {
context.checking(new Expectations() {{
allowing(underlying).canProvide(with(any(Target.class))); will(returnValue(false));
}});
}
@After
public void tearDown() {
context.assertIsSatisfied();
}
@Test
public void willProvideAnEmptyObjectWhenOneIsRequired() throws Exception {
assumeUnderlyingCannotProvideAnything();
assertTrue(provider.canProvide(targetForEmptyObject));
Assert.assertThat(provider.provide(targetForEmptyObject), instanceOf(MayBeEmpty.class));
}
@Test
public void willNotProvideObjectsThatHaveNoMappingNorAreProvidableByTheUnderlyingProvider() throws Exception {
assumeUnderlyingCannotProvideAnything();
final Target<NormalObject> target = Target.create(NormalObject.class, "");
assertFalse(provider.canProvide(target));
}
@Test
public void objectsProvidedForAGivenTargetWillNotBeTheSame() throws Exception {
assumeUnderlyingCannotProvideAnything();
assertTrue(provider.canProvide(targetForEmptyObject));
Object first = provider.provide(targetForEmptyObject);
Object second = provider.provide(targetForEmptyObject);
Assert.assertNotSame(first, second);
}
@Test
public void willDelegateWhenTheUnderlyingProviderIsAbleToProvideForTheTarget() throws Exception {
final Target<?> target = Target.create(DifferentObject.class, "");
final Object provided = new DifferentObject();
context.checking(new Expectations() {{
oneOf(underlying).canProvide(target); will(returnValue(true));
oneOf(underlying).provide(target); will(returnValue(provided));
}});
Assert.assertSame(provided, provider.provide(target));
}
@Test
public void ifUnderlyingIsAbleThenWillDelegateEvenIfCanProvideEmptyInstance() throws Exception {
final Target<?> target = Target.create(MayBeEmpty.class, "");
final Object provided = new MayBeEmpty();
context.checking(new Expectations() {{
oneOf(underlying).canProvide(target); will(returnValue(true));
oneOf(underlying).provide(target); will(returnValue(provided));
}});
Assert.assertSame(provided, provider.provide(target));
}
@Test
public void willDirectlyProvideEmptyArraysRegardlessOfTheReceivedSuppliers() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<NormalObject[]> normalObjectTarget = Target.create(NormalObject[].class, "");
assertTrue(provider.canProvide(normalObjectTarget));
Object theArray = provider.provide(normalObjectTarget);
assertThat(theArray ,is(instanceOf(NormalObject[].class)));
assertThat((Object[])theArray, is(emptyArray()));
}
private Instantiator<Object> instantiator = new MultiInstantiator(
ImmutableList.of(
new StringConverter(),
new ObjectInstantiator(new RecursiveInstantiator(), provider, new ParanamerParameterNamesProvider())));
private final class RecursiveInstantiator implements Instantiator<Object> {
public boolean isAbleToInstantiate(Target<?> target) {
return instantiator.isAbleToInstantiate(target);
}
public Object instantiate(Target<?> target, Parameters parameters) {
return instantiator.instantiate(target, parameters);
}
}
@Test
public void canBeUsedToInstantiateAnObject() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<DependsOnEmptyAndNormal> target = Target.create(DependsOnEmptyAndNormal.class, "root");
Parameters parameters = new Parameters(new Parameter("root.normal.parameter", "foo"));
DependsOnEmptyAndNormal object = (DependsOnEmptyAndNormal) instantiator.instantiate(target, parameters);
assertEquals("foo", object.normal.parameter);
assertThat(object.mayBeEmpty, instanceOf(MayBeEmpty.class));
}
private EmptyObjectsProvider javaProvider = EmptyObjectsProvider.javaEmptyObjectsProvider(underlying);
@SuppressWarnings("unchecked")
@Test
public void javaEmptyObjectsProviderCanProvideNewEmptyMutableSets() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<Set<Object>> setTarget = new Target<Set<Object>>(Set.class, "");
assertTrue(javaProvider.canProvide(setTarget));
Set<Object> firstSet = (Set<Object>) javaProvider.provide(setTarget);
assertTrue(firstSet.isEmpty());
Set<Object> secondSet = (Set<Object>)javaProvider.provide(setTarget);
assertTrue(secondSet.isEmpty());
assertNotSame(firstSet, secondSet);
firstSet.add(new Object());
assertEquals(1, firstSet.size());
}
@SuppressWarnings("unchecked")
@Test
public void javaEmptyObjectsProviderCanProvideNewEmptyMutableLists() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<List<Object>> listTarget = new Target<List<Object>>(List.class, "");
assertTrue(javaProvider.canProvide(listTarget));
List<Object> firstList = (List<Object>) javaProvider.provide(listTarget);
assertTrue(firstList.isEmpty());
List<Object> secondList = (List<Object>) javaProvider.provide(listTarget);
assertTrue(secondList.isEmpty());
assertNotSame(firstList, secondList);
firstList.add(new Object());
assertEquals(1, firstList.size());
}
@SuppressWarnings("unchecked")
@Test
public void javaEmptyObjectsProviderCanProvideNewEmptyMutableMaps() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<Map<Object, Object>> mapTarget = new Target<Map<Object,Object>>(Map.class, "");
assertTrue(javaProvider.canProvide(mapTarget));
Map<Object, Object> firstMap = (Map<Object, Object>) javaProvider.provide(mapTarget);
assertTrue(firstMap.isEmpty());
Map<Object, Object> secondMap = (Map<Object, Object>) javaProvider.provide(mapTarget);
assertTrue(secondMap.isEmpty());
assertNotSame(firstMap, secondMap);
firstMap.put("key", "value");
assertEquals(1, firstMap.size());
}
@Test
public void javaEmptyObjectsProviderCanProvideNewObjectArrays() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<Object[]> arrayTarget = Target.create(Object[].class, "");
assertTrue(javaProvider.canProvide(arrayTarget));
Object[] array = (Object[]) javaProvider.provide(arrayTarget);
assertThat(array, is(emptyArray()));
}
@Test
public void javaEmptyObjectsProviderCanProvideListsOfCustomObjects() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<List<NormalObject>> listTarget = new Target<List<NormalObject>>(List.class, "");
assertTrue(javaProvider.canProvide(listTarget));
@SuppressWarnings("unchecked")
List<NormalObject> list = (List<NormalObject>) javaProvider.provide(listTarget);
assertNotNull(list);
}
@Test
public void javaEmptyObjectsProviderCanProvideArraysOfCustomObjects() throws Exception {
assumeUnderlyingCannotProvideAnything();
Target<NormalObject[]> arrayTarget = Target.create(NormalObject[].class, "");
assertTrue(javaProvider.canProvide(arrayTarget));
NormalObject[] array = (NormalObject[]) javaProvider.provide(arrayTarget);
assertThat(array, is(emptyArray()));
}
static class NormalObject {
private final String parameter;
public NormalObject(String parameter) {
this.parameter = parameter;
}
}
static class MayBeEmpty {
}
static class DependsOnEmptyAndNormal {
final NormalObject normal;
final MayBeEmpty mayBeEmpty;
public DependsOnEmptyAndNormal(MayBeEmpty mayBeEmpty, NormalObject normal) {
this.mayBeEmpty = mayBeEmpty;
this.normal = normal;
}
}
static class DifferentObject {
}
}