package com.bumptech.glide.load.model; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.v4.util.Pools.Pool; import com.bumptech.glide.Registry.NoModelLoaderAvailableException; import com.bumptech.glide.tests.Util; import com.bumptech.glide.util.pool.FactoryPools; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 18) public class MultiModelLoaderFactoryTest { @Mock ModelLoaderFactory<String, String> firstFactory; @Mock ModelLoader<String, String> firstModelLoader; @Mock MultiModelLoaderFactory.Factory multiModelLoaderFactory; @Mock ModelLoaderFactory<String, String> secondFactory; @Mock ModelLoader<String, String> secondModelLoader; @Rule public ExpectedException exception = ExpectedException.none(); private Pool<List<Exception>> exceptionListPool; private MultiModelLoaderFactory multiFactory; @Before public void setUp() { MockitoAnnotations.initMocks(this); exceptionListPool = FactoryPools.threadSafeList(); multiFactory = new MultiModelLoaderFactory(exceptionListPool, multiModelLoaderFactory); when(firstFactory.build(eq(multiFactory))).thenReturn(firstModelLoader); when(secondFactory.build(eq(multiFactory))).thenReturn(secondModelLoader); } @Test public void testAppend_addsModelLoaderForModelClass() { multiFactory.append(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testAppend_addsModelLoaderForModelAndDataClass() { multiFactory.append(String.class, String.class, firstFactory); ModelLoader<String, String> modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testPrepend_addsModelLoaderForModelClass() { multiFactory.prepend(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testPrepend_addsModelLoaderForModelAndDataClass() { multiFactory.prepend(String.class, String.class, firstFactory); ModelLoader<String, String> modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testReplace_addsModelLoaderForModelClass() { multiFactory.replace(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testReplace_addsModelLoaderForModelAndDataClasses() { multiFactory.replace(String.class, String.class, firstFactory); ModelLoader<String, String> modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testReplace_returnsPreviouslyRegisteredFactories_withModelAndDataClasses() { ModelLoaderFactory<String, String> firstOtherFactory = mockFactory(); ModelLoaderFactory<String, String> secondOtherFactory = mockFactory(); multiFactory.append(String.class, String.class, firstOtherFactory); multiFactory.append(String.class, String.class, secondOtherFactory); List<ModelLoaderFactory<String, String>> removed = multiFactory.replace(String.class, String.class, firstFactory); assertThat(removed).containsExactly(firstOtherFactory, secondOtherFactory); } @Test public void testReplace_removesPreviouslyRegisteredFactories_withModelAndDataClasses() { appendFactoryFor(String.class, String.class); appendFactoryFor(String.class, String.class); multiFactory.replace(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testRemove_returnsPreviouslyRegisteredFactories_withModelAndDataClasses() { ModelLoaderFactory<String, String> other = mockFactory(); multiFactory.append(String.class, String.class, other); multiFactory.append(String.class, String.class, firstFactory); List<ModelLoaderFactory<String, String>> removed = multiFactory.remove(String.class, String.class); assertThat(removed).containsExactly(firstFactory, other); } @Test public void testRemove_removesPreviouslyRegisteredFactories_withModelAndDataClasses() { appendFactoryFor(String.class, String.class); appendFactoryFor(String.class, String.class); multiFactory.remove(String.class, String.class); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).isEmpty(); } @Test public void testBuild_withModelClass_returnsMultipleModelLoaders_ofGivenModelAndDataClasses() { ModelLoader<String, String> otherLoader = appendFactoryFor(String.class, String.class); multiFactory.append(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelClass_returnsMultipleModelLoaders_ofGivenModelClassWithDifferentDataClasses() { ModelLoader<String, Integer> otherLoader = appendFactoryFor(String.class, Integer.class); multiFactory.append(String.class, String.class, firstFactory); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelClass_excludesModelLoadersForOtherModelClasses() { multiFactory.append(String.class, String.class, firstFactory); List<ModelLoader<Integer, ?>> modelLoaders = multiFactory.build(Integer.class); assertThat(modelLoaders).doesNotContain(firstModelLoader); } @Test public void testBuild_withModelAndDataClasses_returnsMultipleModelLoaders_ofGivenModelAndDataClasses() { ModelLoader<String, String> otherLoader = appendFactoryFor(String.class, String.class); multiFactory.append(String.class, String.class, firstFactory); List<ModelLoader<String, String>> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelAndDataClasses_excludesModelLoadersForOtherDataClasses() { multiFactory.append(String.class, String.class, firstFactory); exception.expect(NoModelLoaderAvailableException.class); multiFactory.build(String.class, Integer.class); } @Test public void testBuild_withModelAndDataClasses_excludesModelLoadersForOtherModelClasses() { multiFactory.append(String.class, String.class, firstFactory); exception.expect(NoModelLoaderAvailableException.class); multiFactory.build(Integer.class, String.class); } @Test public void testBuild_withModelClass_doesNotMatchSubclassesOfModelClass() { ModelLoader<String, Object> subclass = appendFactoryFor(String.class, Object.class); List<ModelLoader<Object, ?>> modelLoaders = multiFactory.build(Object.class); assertThat(modelLoaders).doesNotContain(subclass); } @Test public void testBuild_withModelClass_matchesSuperclassesOfModelClass() { ModelLoader<Object, Object> superclass = appendFactoryFor(Object.class, Object.class); List<ModelLoader<String, ?>> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).contains(superclass); } @Test public void testBuild_withModelAndDataClass_doesNotMatchSubclassesOfModelClass() { appendFactoryFor(String.class, Object.class); exception.expect(NoModelLoaderAvailableException.class); multiFactory.build(Object.class, Object.class); } @Test public void testBuild_withModelAndDataClass_doesNotMatchSubclassesOfDataClass() { appendFactoryFor(Object.class, String.class); exception.expect(NoModelLoaderAvailableException.class); multiFactory.build(Object.class, Object.class); } @Test public void testBuild_withModelAndDataClass_doesMatchSuperclassesOfModelClass() { ModelLoader<Object, Object> firstSuperClass = appendFactoryFor(Object.class, Object.class); ModelLoader<Object, Object> secondSuperClass = appendFactoryFor(Object.class, Object.class); List<ModelLoader<String, Object>> modelLoaders = buildModelLoaders(String.class, Object.class); assertThat(modelLoaders).containsExactly(firstSuperClass, secondSuperClass); } @Test public void testBuild_withModelAndDataClass_matchesSuperclassesOfDataClass() { ModelLoader<Object, Object> firstSuperClass = appendFactoryFor(Object.class, Object.class); ModelLoader<Object, Object> secondSuperClass = appendFactoryFor(Object.class, Object.class); List<ModelLoader<Object, String>> modelLoaders = buildModelLoaders(Object.class, String.class); assertThat(modelLoaders).containsExactly(firstSuperClass, secondSuperClass); } @Test public void testBuild_withModelAndDataClass_matchesSuperclassOfModelAndDataClass() { ModelLoader<Object, Object> firstSuperclass = appendFactoryFor(Object.class, Object.class); ModelLoader<Object, Object> secondSuperclass = appendFactoryFor(Object.class, Object.class); List<ModelLoader<String, String>> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(firstSuperclass, secondSuperclass); } @Test public void testBuild_respectsAppendOrder() { ModelLoader<String, String> first = appendFactoryFor(String.class, String.class); ModelLoader<String, String> second = appendFactoryFor(String.class, String.class); ModelLoader<String, String> third = appendFactoryFor(String.class, String.class); List<ModelLoader<String, String>> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(first, second, third).inOrder(); } @Test public void testBuild_respectsPrependOrder() { ModelLoader<String, String> first = prependFactoryFor(String.class, String.class); ModelLoader<String, String> second = prependFactoryFor(String.class, String.class); ModelLoader<String, String> third = prependFactoryFor(String.class, String.class); List<ModelLoader<String, String>> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(third, second, first).inOrder(); } private <X, Y> List<ModelLoader<X, Y>> buildModelLoaders(Class<X> modelClass, Class<Y> dataClass) { ArgumentCaptor<List<ModelLoader<X, Y>>> captor = Util.cast(ArgumentCaptor.forClass(List.class)); multiFactory.build(modelClass, dataClass); verify(multiModelLoaderFactory).build(captor.capture(), eq(exceptionListPool)); List<ModelLoader<X, Y>> captured = captor.getValue(); List<ModelLoader<X, Y>> result = new ArrayList<>(captured.size()); for (ModelLoader<X, Y> modelLoader : captured) { result.add(modelLoader); } return result; } private <X, Y> ModelLoader<X, Y> appendFactoryFor(Class<X> modelClass, Class<Y> dataClass) { return registerFactoryFor(modelClass, dataClass, true /*append*/); } private <X, Y> ModelLoader<X, Y> prependFactoryFor(Class<X> modelClass, Class<Y> dataClass) { return registerFactoryFor(modelClass, dataClass, false /*append*/); } private <X, Y> ModelLoader<X, Y> registerFactoryFor( Class<X> modelClass, Class<Y> dataClass, boolean append) { ModelLoaderFactory<X, Y> factory = mockFactory(); @SuppressWarnings("unchecked") ModelLoader<X, Y> loader = mock(ModelLoader.class); when(factory.build(eq(multiFactory))).thenReturn(loader); if (append) { multiFactory.append(modelClass, dataClass, factory); } else { multiFactory.prepend(modelClass, dataClass, factory); } return loader; } @SuppressWarnings("unchecked") private static <X, Y> ModelLoaderFactory<X, Y> mockFactory() { return mock(ModelLoaderFactory.class); } }