package com.bumptech.glide.load.model; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; 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.Test; import org.junit.function.ThrowingRunnable; 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; // containsExactly produces a spurious warning. @SuppressWarnings("ResultOfMethodCallIgnored") @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 18) public class MultiModelLoaderFactoryTest { @Mock private ModelLoaderFactory firstFactory; @Mock private ModelLoader firstModelLoader; @Mock private MultiModelLoaderFactory.Factory multiModelLoaderFactory; @Mock private ModelLoaderFactory secondFactory; @Mock private ModelLoader secondModelLoader; private Pool> throwableListPool; private MultiModelLoaderFactory multiFactory; @Before public void setUp() { MockitoAnnotations.initMocks(this); throwableListPool = FactoryPools.threadSafeList(); multiFactory = new MultiModelLoaderFactory(throwableListPool, 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> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testAppend_addsModelLoaderForModelAndDataClass() { multiFactory.append(String.class, String.class, firstFactory); ModelLoader modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testPrepend_addsModelLoaderForModelClass() { multiFactory.prepend(String.class, String.class, firstFactory); List> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testPrepend_addsModelLoaderForModelAndDataClass() { multiFactory.prepend(String.class, String.class, firstFactory); ModelLoader modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testReplace_addsModelLoaderForModelClass() { multiFactory.replace(String.class, String.class, firstFactory); List> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testReplace_addsModelLoaderForModelAndDataClasses() { multiFactory.replace(String.class, String.class, firstFactory); ModelLoader modelLoader = multiFactory.build(String.class, String.class); assertThat(modelLoader).isEqualTo(firstModelLoader); } @Test public void testReplace_returnsPreviouslyRegisteredFactories_withModelAndDataClasses() { ModelLoaderFactory firstOtherFactory = mockFactory(); ModelLoaderFactory secondOtherFactory = mockFactory(); multiFactory.append(String.class, String.class, firstOtherFactory); multiFactory.append(String.class, String.class, secondOtherFactory); List> 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> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(firstModelLoader); } @Test public void testRemove_returnsPreviouslyRegisteredFactories_withModelAndDataClasses() { ModelLoaderFactory other = mockFactory(); multiFactory.append(String.class, String.class, other); multiFactory.append(String.class, String.class, firstFactory); List> 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> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).isEmpty(); } @Test public void testBuild_withModelClass_returnsMultipleModelLoaders_ofGivenModelAndDataClasses() { ModelLoader otherLoader = appendFactoryFor(String.class, String.class); multiFactory.append(String.class, String.class, firstFactory); List> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelClass_returnsMultipleModelLoaders_ofGivenModelClassWithDifferentDataClasses() { ModelLoader otherLoader = appendFactoryFor(String.class, Integer.class); multiFactory.append(String.class, String.class, firstFactory); List> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelClass_excludesModelLoadersForOtherModelClasses() { multiFactory.append(String.class, String.class, firstFactory); List> modelLoaders = multiFactory.build(Integer.class); assertThat(modelLoaders).doesNotContain(firstModelLoader); } @Test public void testBuild_withModelAndDataClasses_returnsMultipleModelLoaders_ofGivenModelAndDataClasses() { ModelLoader otherLoader = appendFactoryFor(String.class, String.class); multiFactory.append(String.class, String.class, firstFactory); List> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(otherLoader, firstModelLoader); } @Test public void testBuild_withModelAndDataClasses_excludesModelLoadersForOtherDataClasses() { multiFactory.append(String.class, String.class, firstFactory); assertThrows( NoModelLoaderAvailableException.class, new ThrowingRunnable() { @Override public void run() throws Throwable { multiFactory.build(String.class, Integer.class); } }); } @Test public void testBuild_withModelAndDataClasses_excludesModelLoadersForOtherModelClasses() { multiFactory.append(String.class, String.class, firstFactory); assertThrows( NoModelLoaderAvailableException.class, new ThrowingRunnable() { @Override public void run() throws Throwable { multiFactory.build(Integer.class, String.class); } }); } @Test public void testBuild_withModelClass_doesNotMatchSubclassesOfModelClass() { ModelLoader subclass = appendFactoryFor(String.class, Object.class); List> modelLoaders = multiFactory.build(Object.class); assertThat(modelLoaders).doesNotContain(subclass); } @Test public void testBuild_withModelClass_matchesSuperclassesOfModelClass() { ModelLoader superclass = appendFactoryFor(Object.class, Object.class); List> modelLoaders = multiFactory.build(String.class); assertThat(modelLoaders).contains(superclass); } @Test public void testBuild_withModelAndDataClass_doesNotMatchSubclassesOfModelClass() { appendFactoryFor(String.class, Object.class); assertThrows( NoModelLoaderAvailableException.class, new ThrowingRunnable() { @Override public void run() throws Throwable { multiFactory.build(Object.class, Object.class); } }); } @Test public void testBuild_withModelAndDataClass_doesNotMatchSubclassesOfDataClass() { appendFactoryFor(Object.class, String.class); assertThrows( NoModelLoaderAvailableException.class, new ThrowingRunnable() { @Override public void run() throws Throwable { multiFactory.build(Object.class, Object.class); } }); } @Test public void testBuild_withModelAndDataClass_doesMatchSuperclassesOfModelClass() { ModelLoader firstSuperClass = appendFactoryFor(Object.class, Object.class); ModelLoader secondSuperClass = appendFactoryFor(Object.class, Object.class); List> modelLoaders = buildModelLoaders(String.class, Object.class); assertThat(modelLoaders).containsExactly(firstSuperClass, secondSuperClass); } @Test public void testBuild_withModelAndDataClass_matchesSuperclassesOfDataClass() { ModelLoader firstSuperClass = appendFactoryFor(Object.class, Object.class); ModelLoader secondSuperClass = appendFactoryFor(Object.class, Object.class); List> modelLoaders = buildModelLoaders(Object.class, String.class); assertThat(modelLoaders).containsExactly(firstSuperClass, secondSuperClass); } @Test public void testBuild_withModelAndDataClass_matchesSuperclassOfModelAndDataClass() { ModelLoader firstSuperclass = appendFactoryFor(Object.class, Object.class); ModelLoader secondSuperclass = appendFactoryFor(Object.class, Object.class); List> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(firstSuperclass, secondSuperclass); } @Test public void testBuild_respectsAppendOrder() { ModelLoader first = appendFactoryFor(String.class, String.class); ModelLoader second = appendFactoryFor(String.class, String.class); ModelLoader third = appendFactoryFor(String.class, String.class); List> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(first, second, third).inOrder(); } @Test public void testBuild_respectsPrependOrder() { ModelLoader first = prependFactoryFor(String.class, String.class); ModelLoader second = prependFactoryFor(String.class, String.class); ModelLoader third = prependFactoryFor(String.class, String.class); List> modelLoaders = buildModelLoaders(String.class, String.class); assertThat(modelLoaders).containsExactly(third, second, first).inOrder(); } private List> buildModelLoaders(Class modelClass, Class dataClass) { ArgumentCaptor>> captor = Util.cast(ArgumentCaptor.forClass(List.class)); multiFactory.build(modelClass, dataClass); verify(multiModelLoaderFactory).build(captor.capture(), eq(throwableListPool)); List> captured = captor.getValue(); List> result = new ArrayList<>(captured.size()); result.addAll(captured); return result; } private ModelLoader appendFactoryFor(Class modelClass, Class dataClass) { return registerFactoryFor(modelClass, dataClass, true /*append*/); } private ModelLoader prependFactoryFor(Class modelClass, Class dataClass) { return registerFactoryFor(modelClass, dataClass, false /*append*/); } private ModelLoader registerFactoryFor( Class modelClass, Class dataClass, boolean append) { ModelLoaderFactory factory = mockFactory(); @SuppressWarnings("unchecked") ModelLoader 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 ModelLoaderFactory mockFactory() { return mock(ModelLoaderFactory.class); } }