Image loader tests
This commit is contained in:
		
							parent
							
								
									ab4c80dca5
								
							
						
					
					
						commit
						173425ed53
					
				| @ -88,7 +88,6 @@ public class MarkwonBuilderImplTest { | |||||||
|         verify(plugin, times(1)).configureConfiguration(any(MarkwonConfiguration.Builder.class)); |         verify(plugin, times(1)).configureConfiguration(any(MarkwonConfiguration.Builder.class)); | ||||||
|         verify(plugin, times(1)).configureVisitor(any(MarkwonVisitor.Builder.class)); |         verify(plugin, times(1)).configureVisitor(any(MarkwonVisitor.Builder.class)); | ||||||
|         verify(plugin, times(1)).configureSpansFactory(any(MarkwonSpansFactory.Builder.class)); |         verify(plugin, times(1)).configureSpansFactory(any(MarkwonSpansFactory.Builder.class)); | ||||||
|         verify(plugin, times(1)).configureHtmlRenderer(any(MarkwonHtmlRenderer.Builder.class)); |  | ||||||
| 
 | 
 | ||||||
|         // note, no render props -> they must be configured on render stage |         // note, no render props -> they must be configured on render stage | ||||||
|         verify(plugin, times(0)).processMarkdown(anyString()); |         verify(plugin, times(0)).processMarkdown(anyString()); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ package io.noties.markwon.core; | |||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| import android.text.method.MovementMethod; | import android.text.method.MovementMethod; | ||||||
|  | import android.widget.ImageView; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| 
 | 
 | ||||||
| import org.commonmark.node.BlockQuote; | import org.commonmark.node.BlockQuote; | ||||||
| @ -12,6 +13,7 @@ import org.commonmark.node.Emphasis; | |||||||
| import org.commonmark.node.FencedCodeBlock; | import org.commonmark.node.FencedCodeBlock; | ||||||
| import org.commonmark.node.HardLineBreak; | import org.commonmark.node.HardLineBreak; | ||||||
| import org.commonmark.node.Heading; | import org.commonmark.node.Heading; | ||||||
|  | import org.commonmark.node.Image; | ||||||
| import org.commonmark.node.IndentedCodeBlock; | import org.commonmark.node.IndentedCodeBlock; | ||||||
| import org.commonmark.node.Link; | import org.commonmark.node.Link; | ||||||
| import org.commonmark.node.ListItem; | import org.commonmark.node.ListItem; | ||||||
| @ -84,7 +86,8 @@ public class CorePluginTest { | |||||||
|                 SoftLineBreak.class, |                 SoftLineBreak.class, | ||||||
|                 StrongEmphasis.class, |                 StrongEmphasis.class, | ||||||
|                 Text.class, |                 Text.class, | ||||||
|                 ThematicBreak.class |                 ThematicBreak.class, | ||||||
|  |                 Image.class | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         final CorePlugin plugin = CorePlugin.create(); |         final CorePlugin plugin = CorePlugin.create(); | ||||||
| @ -202,6 +205,7 @@ public class CorePluginTest { | |||||||
|             add("beforeSetText"); |             add("beforeSetText"); | ||||||
|             add("afterSetText"); |             add("afterSetText"); | ||||||
|             add("priority"); |             add("priority"); | ||||||
|  |             add("addOnTextAddedListener"); | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|         // we will use declaredMethods because it won't return inherited ones |         // we will use declaredMethods because it won't return inherited ones | ||||||
|  | |||||||
| @ -26,7 +26,6 @@ import io.noties.markwon.SpanFactory; | |||||||
| import io.noties.markwon.SpannableBuilder; | import io.noties.markwon.SpannableBuilder; | ||||||
| import io.noties.markwon.core.CorePluginBridge; | import io.noties.markwon.core.CorePluginBridge; | ||||||
| import io.noties.markwon.core.MarkwonTheme; | import io.noties.markwon.core.MarkwonTheme; | ||||||
| import io.noties.markwon.html.MarkwonHtmlRenderer; |  | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||||
| import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||||
| @ -83,7 +82,7 @@ public class SyntaxHighlightTest { | |||||||
| 
 | 
 | ||||||
|         final MarkwonConfiguration configuration = MarkwonConfiguration.builder() |         final MarkwonConfiguration configuration = MarkwonConfiguration.builder() | ||||||
|                 .syntaxHighlight(highlight) |                 .syntaxHighlight(highlight) | ||||||
|                 .build(mock(MarkwonTheme.class), mock(MarkwonHtmlRenderer.class), spansFactory); |                 .build(mock(MarkwonTheme.class), spansFactory); | ||||||
| 
 | 
 | ||||||
|         final Map<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>> visitorMap = Collections.emptyMap(); |         final Map<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>> visitorMap = Collections.emptyMap(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,11 @@ | |||||||
| # LaTeX | # LaTeX | ||||||
| 
 | 
 | ||||||
| [Documentation](https://noties.github.io/Markwon/docs/ext-latex) |  | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ```kotlin | ||||||
|  | implementation "io.noties.markwon:ext-strikethrough:${markwonVersion}" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [Documentation](https://noties.github.io/Markwon/docs/v3/ext-latex) | ||||||
|  | |||||||
| @ -1,6 +1,12 @@ | |||||||
| # Strikethrough | # Strikethrough | ||||||
| 
 | 
 | ||||||
| [](http://search.maven.org/#search|ga|1|g%3A%22io.noties.markwon%22%20AND%20a%3A%22ext-strikethrough%22) |  | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ```kotlin | ||||||
|  | implementation "io.noties.markwon:ext-strikethrough:${markwonVersion}" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`: | This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`: | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,10 +17,11 @@ dependencies { | |||||||
| 
 | 
 | ||||||
|     api project(':markwon-core') |     api project(':markwon-core') | ||||||
| 
 | 
 | ||||||
|  |     // todo: note that it includes these implicitly | ||||||
|     deps.with { |     deps.with { | ||||||
|         compileOnly it['android-gif'] |         api it['android-gif'] | ||||||
|         compileOnly it['android-svg'] |         api it['android-svg'] | ||||||
|         compileOnly it['okhttp'] |         api it['okhttp'] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     deps['test'].with { |     deps['test'].with { | ||||||
|  | |||||||
| @ -9,7 +9,9 @@ import java.util.concurrent.ExecutorService; | |||||||
| import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||||
| 
 | 
 | ||||||
| import io.noties.markwon.image.data.DataUriSchemeHandler; | import io.noties.markwon.image.data.DataUriSchemeHandler; | ||||||
|  | import io.noties.markwon.image.gif.GifMediaDecoder; | ||||||
| import io.noties.markwon.image.network.NetworkSchemeHandler; | import io.noties.markwon.image.network.NetworkSchemeHandler; | ||||||
|  | import io.noties.markwon.image.svg.SvgMediaDecoder; | ||||||
| 
 | 
 | ||||||
| class AsyncDrawableLoaderBuilder { | class AsyncDrawableLoaderBuilder { | ||||||
| 
 | 
 | ||||||
| @ -30,6 +32,15 @@ class AsyncDrawableLoaderBuilder { | |||||||
|         addSchemeHandler(DataUriSchemeHandler.create()); |         addSchemeHandler(DataUriSchemeHandler.create()); | ||||||
|         addSchemeHandler(NetworkSchemeHandler.create()); |         addSchemeHandler(NetworkSchemeHandler.create()); | ||||||
| 
 | 
 | ||||||
|  |         // add SVG and GIF, but only if they are present in the class-path | ||||||
|  |         if (SvgMediaDecoder.available()) { | ||||||
|  |             addMediaDecoder(SvgMediaDecoder.create()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (GifMediaDecoder.available()) { | ||||||
|  |             addMediaDecoder(GifMediaDecoder.create()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         defaultMediaDecoder = DefaultImageMediaDecoder.create(); |         defaultMediaDecoder = DefaultImageMediaDecoder.create(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -92,8 +92,15 @@ class AsyncDrawableLoaderImpl extends AsyncDrawableLoader { | |||||||
|                 Drawable drawable = null; |                 Drawable drawable = null; | ||||||
| 
 | 
 | ||||||
|                 try { |                 try { | ||||||
|  | 
 | ||||||
|  |                     final String scheme = uri.getScheme(); | ||||||
|  |                     if (scheme == null | ||||||
|  |                             || scheme.length() == 0) { | ||||||
|  |                         throw new IllegalStateException("No scheme is found: " + destination); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                     // obtain scheme handler |                     // obtain scheme handler | ||||||
|                     final SchemeHandler schemeHandler = schemeHandlers.get(uri.getScheme()); |                     final SchemeHandler schemeHandler = schemeHandlers.get(scheme); | ||||||
|                     if (schemeHandler != null) { |                     if (schemeHandler != null) { | ||||||
| 
 | 
 | ||||||
|                         // handle scheme |                         // handle scheme | ||||||
|  | |||||||
| @ -10,9 +10,9 @@ import java.io.InputStream; | |||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| 
 | 
 | ||||||
|  | import io.noties.markwon.image.DrawableUtils; | ||||||
| import io.noties.markwon.image.MediaDecoder; | import io.noties.markwon.image.MediaDecoder; | ||||||
| import pl.droidsonroids.gif.GifDrawable; | import pl.droidsonroids.gif.GifDrawable; | ||||||
| import io.noties.markwon.image.DrawableUtils; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @since 1.1.0 |  * @since 1.1.0 | ||||||
| @ -22,11 +22,29 @@ public class GifMediaDecoder extends MediaDecoder { | |||||||
| 
 | 
 | ||||||
|     public static final String CONTENT_TYPE = "image/gif"; |     public static final String CONTENT_TYPE = "image/gif"; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Creates a {@link GifMediaDecoder} with {@code autoPlayGif = true} | ||||||
|  |      * | ||||||
|  |      * @since 4.0.0-SNAPSHOT | ||||||
|  |      */ | ||||||
|  |     @NonNull | ||||||
|  |     public static GifMediaDecoder create() { | ||||||
|  |         return create(true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     public static GifMediaDecoder create(boolean autoPlayGif) { |     public static GifMediaDecoder create(boolean autoPlayGif) { | ||||||
|         return new GifMediaDecoder(autoPlayGif); |         return new GifMediaDecoder(autoPlayGif); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @return boolean indicating if GIF dependency is satisfied | ||||||
|  |      * @since 4.0.0-SNAPSHOT | ||||||
|  |      */ | ||||||
|  |     public static boolean available() { | ||||||
|  |         return Holder.HAS_GIF; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private final boolean autoPlayGif; |     private final boolean autoPlayGif; | ||||||
| 
 | 
 | ||||||
|     protected GifMediaDecoder(boolean autoPlayGif) { |     protected GifMediaDecoder(boolean autoPlayGif) { | ||||||
| @ -105,7 +123,8 @@ public class GifMediaDecoder extends MediaDecoder { | |||||||
|         static void validate() { |         static void validate() { | ||||||
|             if (!HAS_GIF) { |             if (!HAS_GIF) { | ||||||
|                 throw new IllegalStateException("`pl.droidsonroids.gif:android-gif-drawable:*` " + |                 throw new IllegalStateException("`pl.droidsonroids.gif:android-gif-drawable:*` " + | ||||||
|                         "dependency is missing, please add to your project explicitly"); |                         "dependency is missing, please add to your project explicitly if you " + | ||||||
|  |                         "wish to use GIF media decoder"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -15,8 +15,8 @@ import java.io.InputStream; | |||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| 
 | 
 | ||||||
| import io.noties.markwon.image.MediaDecoder; |  | ||||||
| import io.noties.markwon.image.DrawableUtils; | import io.noties.markwon.image.DrawableUtils; | ||||||
|  | import io.noties.markwon.image.MediaDecoder; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @since 1.1.0 |  * @since 1.1.0 | ||||||
| @ -31,7 +31,7 @@ public class SvgMediaDecoder extends MediaDecoder { | |||||||
|      */ |      */ | ||||||
|     @NonNull |     @NonNull | ||||||
|     public static SvgMediaDecoder create() { |     public static SvgMediaDecoder create() { | ||||||
|         return new SvgMediaDecoder(Resources.getSystem()); |         return create(Resources.getSystem()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
| @ -39,6 +39,14 @@ public class SvgMediaDecoder extends MediaDecoder { | |||||||
|         return new SvgMediaDecoder(resources); |         return new SvgMediaDecoder(resources); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @return boolean indicating if SVG dependency is satisfied | ||||||
|  |      * @since 4.0.0-SNAPSHOT | ||||||
|  |      */ | ||||||
|  |     public static boolean available() { | ||||||
|  |         return Holder.HAS_SVG; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private final Resources resources; |     private final Resources resources; | ||||||
| 
 | 
 | ||||||
|     @SuppressWarnings("WeakerAccess") |     @SuppressWarnings("WeakerAccess") | ||||||
| @ -102,7 +110,7 @@ public class SvgMediaDecoder extends MediaDecoder { | |||||||
|         static void validate() { |         static void validate() { | ||||||
|             if (!HAS_SVG) { |             if (!HAS_SVG) { | ||||||
|                 throw new IllegalStateException("`com.caverock:androidsvg:*` dependency is missing, " + |                 throw new IllegalStateException("`com.caverock:androidsvg:*` dependency is missing, " + | ||||||
|                         "please add to your project explicitly"); |                         "please add to your project explicitly if you wish to use SVG media decoder"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -10,8 +10,8 @@ import java.util.Arrays; | |||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||||
| 
 | 
 | ||||||
| import io.noties.markwon.image.network.NetworkSchemeHandler; |  | ||||||
| import io.noties.markwon.image.data.DataUriSchemeHandler; | import io.noties.markwon.image.data.DataUriSchemeHandler; | ||||||
|  | import io.noties.markwon.image.network.NetworkSchemeHandler; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||||
| import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||||
| @ -62,7 +62,7 @@ public class AsyncDrawableLoaderBuilderTest { | |||||||
|     public void defaults_initialized() { |     public void defaults_initialized() { | ||||||
|         // default-media-decoder and executor-service must be initialized |         // default-media-decoder and executor-service must be initialized | ||||||
| 
 | 
 | ||||||
|         assertNull(builder.defaultMediaDecoder); |         assertNotNull(builder.defaultMediaDecoder); | ||||||
|         assertNull(builder.executorService); |         assertNull(builder.executorService); | ||||||
| 
 | 
 | ||||||
|         builder.build(); |         builder.build(); | ||||||
| @ -71,6 +71,18 @@ public class AsyncDrawableLoaderBuilderTest { | |||||||
|         assertNotNull(builder.executorService); |         assertNotNull(builder.executorService); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Test | ||||||
|  |     public void default_media_decoder_removed() { | ||||||
|  |         // we init default-media-decoder right away, but further it can be removed (nulled-out) | ||||||
|  | 
 | ||||||
|  |         assertNotNull(builder.defaultMediaDecoder); | ||||||
|  | 
 | ||||||
|  |         builder.defaultMediaDecoder(null); | ||||||
|  |         builder.build(); | ||||||
|  | 
 | ||||||
|  |         assertNull(builder.defaultMediaDecoder); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void executor() { |     public void executor() { | ||||||
|         // supplied executor-service must be used |         // supplied executor-service must be used | ||||||
| @ -155,7 +167,7 @@ public class AsyncDrawableLoaderBuilderTest { | |||||||
|     @Test |     @Test | ||||||
|     public void default_media_decoder() { |     public void default_media_decoder() { | ||||||
| 
 | 
 | ||||||
|         assertNull(builder.defaultMediaDecoder); |         assertNotNull(builder.defaultMediaDecoder); | ||||||
| 
 | 
 | ||||||
|         final MediaDecoder mediaDecoder = mock(MediaDecoder.class); |         final MediaDecoder mediaDecoder = mock(MediaDecoder.class); | ||||||
|         builder.defaultMediaDecoder(mediaDecoder); |         builder.defaultMediaDecoder(mediaDecoder); | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| package io.noties.markwon.image; | package io.noties.markwon.image; | ||||||
| 
 | 
 | ||||||
|  | import android.graphics.drawable.Drawable; | ||||||
|  | import android.net.Uri; | ||||||
| import android.os.Handler; | import android.os.Handler; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| @ -7,15 +9,31 @@ import android.support.annotation.Nullable; | |||||||
| import org.junit.Before; | import org.junit.Before; | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||||
|  | import org.mockito.ArgumentCaptor; | ||||||
|  | import org.mockito.invocation.InvocationOnMock; | ||||||
|  | import org.mockito.stubbing.Answer; | ||||||
| import org.robolectric.RobolectricTestRunner; | import org.robolectric.RobolectricTestRunner; | ||||||
| import org.robolectric.annotation.Config; | import org.robolectric.annotation.Config; | ||||||
| 
 | 
 | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||||
|  | import java.util.concurrent.Future; | ||||||
| 
 | 
 | ||||||
|  | import io.noties.markwon.image.ImagesPlugin.ErrorHandler; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.assertEquals; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
| import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||||
|  | import static org.mockito.ArgumentMatchers.anyBoolean; | ||||||
|  | import static org.mockito.ArgumentMatchers.anyLong; | ||||||
|  | import static org.mockito.ArgumentMatchers.eq; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
|  | import static org.mockito.Mockito.never; | ||||||
| import static org.mockito.Mockito.times; | import static org.mockito.Mockito.times; | ||||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
| 
 | 
 | ||||||
| @RunWith(RobolectricTestRunner.class) | @RunWith(RobolectricTestRunner.class) | ||||||
| @Config(manifest = Config.NONE) | @Config(manifest = Config.NONE) | ||||||
| @ -40,60 +58,508 @@ public class AsyncDrawableLoaderImplTest { | |||||||
|                 .providePlaceholder(any(AsyncDrawable.class)); |                 .providePlaceholder(any(AsyncDrawable.class)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_cancel() { | ||||||
|  |         // verify that load/cancel works as expected | ||||||
|  | 
 | ||||||
|  |         final ExecutorService executorService = mock(ExecutorService.class); | ||||||
|  |         final Future future = mock(Future.class); | ||||||
|  |         { | ||||||
|  |             //noinspection unchecked | ||||||
|  |             when(executorService.submit(any(Runnable.class))) | ||||||
|  |                     .thenReturn(future); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         final Handler handler = mock(Handler.class); | ||||||
|  | 
 | ||||||
|  |         final AsyncDrawable drawable = mock(AsyncDrawable.class); | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(executorService) | ||||||
|  |                 .handler(handler) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         impl.load(drawable); | ||||||
|  | 
 | ||||||
|  |         verify(executorService, times(1)).submit(any(Runnable.class)); | ||||||
|  | 
 | ||||||
|  |         impl.cancel(drawable); | ||||||
|  | 
 | ||||||
|  |         verify(future, times(1)).cancel(eq(true)); | ||||||
|  |         verify(handler, times(1)).removeCallbacksAndMessages(eq(drawable)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_no_scheme_handler() { | ||||||
|  |         // when loading is triggered for a scheme which has no registered scheme-handler | ||||||
|  | 
 | ||||||
|  |         final ErrorHandler errorHandler = mock(ErrorHandler.class); | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService()) | ||||||
|  |                 .errorHandler(errorHandler) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "blah://blah.JPEG"; | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable(destination)); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Throwable> throwableCaptor = ArgumentCaptor.forClass(Throwable.class); | ||||||
|  |         verify(errorHandler, times(1)) | ||||||
|  |                 .handleError(eq(destination), throwableCaptor.capture()); | ||||||
|  |         final Throwable value = throwableCaptor.getValue(); | ||||||
|  |         assertTrue(value.getClass().getName(), value instanceof IllegalStateException); | ||||||
|  |         assertTrue(value.getMessage(), value.getMessage().contains("No scheme-handler is found")); | ||||||
|  |         assertTrue(value.getMessage(), value.getMessage().contains(destination)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_scheme_handler_throws() { | ||||||
|  | 
 | ||||||
|  |         final ErrorHandler errorHandler = mock(ErrorHandler.class); | ||||||
|  |         final SchemeHandler schemeHandler = new SchemeHandler() { | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                 throw new RuntimeException("We throw!"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public Collection<String> supportedSchemes() { | ||||||
|  |                 return Collections.singleton("hey"); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService()) | ||||||
|  |                 .errorHandler(errorHandler) | ||||||
|  |                 .addSchemeHandler(schemeHandler) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "hey://whe.er"; | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable(destination)); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class); | ||||||
|  |         verify(errorHandler, times(1)) | ||||||
|  |                 .handleError(eq(destination), captor.capture()); | ||||||
|  | 
 | ||||||
|  |         final Throwable throwable = captor.getValue(); | ||||||
|  |         assertTrue(throwable.getClass().getName(), throwable instanceof RuntimeException); | ||||||
|  |         assertEquals("We throw!", throwable.getMessage()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_scheme_handler_returns_result() { | ||||||
|  | 
 | ||||||
|  |         final Drawable drawable = mock(Drawable.class); | ||||||
|  |         final SchemeHandler schemeHandler = new SchemeHandler() { | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                 return ImageItem.withResult(drawable); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public Collection<String> supportedSchemes() { | ||||||
|  |                 return Collections.singleton("*"); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         final String destination = "*://yo"; | ||||||
|  | 
 | ||||||
|  |         final Future future = mock(Future.class); | ||||||
|  |         final ExecutorService executorService = immediateExecutorService(future); | ||||||
|  |         final Handler handler = mock(Handler.class); | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(executorService) | ||||||
|  |                 .handler(handler) | ||||||
|  |                 .addSchemeHandler(schemeHandler) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         verify(executorService, times(1)) | ||||||
|  |                 .submit(any(Runnable.class)); | ||||||
|  | 
 | ||||||
|  |         // we must use captor in order to let the internal (async) logic settle | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, times(1)) | ||||||
|  |                 .setResult(eq(drawable)); | ||||||
|  | 
 | ||||||
|  |         // now, let's cancel the request (at this point it must be removed from referencing) | ||||||
|  |         impl.cancel(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         verify(future, never()).cancel(anyBoolean()); | ||||||
|  | 
 | ||||||
|  |         // this method will be called anyway (we have no mean to check if token has queue) | ||||||
|  | //        verify(handler, never()).removeCallbacksAndMessages(eq(asyncDrawable)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_scheme_handler_returns_decoding_default_used() { | ||||||
|  |         // we won't be registering media decoder, but provide a default one (which must be used) | ||||||
|  | 
 | ||||||
|  |         final MediaDecoder mediaDecoder = mock(MediaDecoder.class); | ||||||
|  |         final InputStream inputStream = mock(InputStream.class); | ||||||
|  |         final Drawable drawable = mock(Drawable.class); | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             when(mediaDecoder.decode(any(String.class), any(InputStream.class))) | ||||||
|  |                     .thenReturn(drawable); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService(mock(Future.class))) | ||||||
|  |                 .defaultMediaDecoder(mediaDecoder) | ||||||
|  |                 .addSchemeHandler(new SchemeHandler() { | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                         return ImageItem.withDecodingNeeded("no/op", inputStream); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public Collection<String> supportedSchemes() { | ||||||
|  |                         return Collections.singleton("whatever"); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "whatever://yeah-yeah-yeah"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         verify(mediaDecoder, times(1)) | ||||||
|  |                 .decode(eq("no/op"), eq(inputStream)); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(builder._handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, times(1)) | ||||||
|  |                 .setResult(eq(drawable)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_no_media_decoder_present() { | ||||||
|  |         // if some content-type is requested (and it has no registered media-decoder), | ||||||
|  |         // and default-media-decoder is not added -> throws | ||||||
|  | 
 | ||||||
|  |         final ErrorHandler errorHandler = mock(ErrorHandler.class); | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .defaultMediaDecoder(null) | ||||||
|  |                 .executorService(immediateExecutorService()) | ||||||
|  |                 .errorHandler(errorHandler) | ||||||
|  |                 .addSchemeHandler(new SchemeHandler() { | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                         return ImageItem.withDecodingNeeded("np/op", mock(InputStream.class)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public Collection<String> supportedSchemes() { | ||||||
|  |                         return Collections.singleton("ftp"); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "ftp://xxx"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class); | ||||||
|  | 
 | ||||||
|  |         verify(errorHandler, times(1)) | ||||||
|  |                 .handleError(eq(destination), captor.capture()); | ||||||
|  | 
 | ||||||
|  |         final Throwable throwable = captor.getValue(); | ||||||
|  |         assertTrue(throwable.getClass().getName(), throwable instanceof IllegalStateException); | ||||||
|  |         assertTrue(throwable.getMessage(), throwable.getMessage().contains("No media-decoder is found")); | ||||||
|  |         assertTrue(throwable.getMessage(), throwable.getMessage().contains(destination)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_error_handler_drawable() { | ||||||
|  |         // error-handler can return optional error-drawable that can be used as a result | ||||||
|  | 
 | ||||||
|  |         final ErrorHandler errorHandler = mock(ErrorHandler.class); | ||||||
|  |         final Drawable drawable = mock(Drawable.class); | ||||||
|  |         { | ||||||
|  |             when(errorHandler.handleError(any(String.class), any(Throwable.class))) | ||||||
|  |                     .thenReturn(drawable); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService(mock(Future.class))) | ||||||
|  |                 .errorHandler(errorHandler) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         // we will rely on _internal_ error, which is also delivered to error-handler | ||||||
|  |         // in this case -> no scheme-handler | ||||||
|  | 
 | ||||||
|  |         final String destination = "uo://uo?true=false"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         verify(errorHandler, times(1)) | ||||||
|  |                 .handleError(eq(destination), any(Throwable.class)); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(builder._handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, times(1)) | ||||||
|  |                 .setResult(eq(drawable)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_success_request_cancelled() { | ||||||
|  |         // when loading finishes it must check if request had been cancelled and not deliver result | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService(mock(Future.class))) | ||||||
|  |                 .addSchemeHandler(new SchemeHandler() { | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                         return ImageItem.withResult(mock(Drawable.class)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public Collection<String> supportedSchemes() { | ||||||
|  |                         return Collections.singleton("ja"); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "ja://jajaja"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(builder._handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         // now, cancel | ||||||
|  |         impl.cancel(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, never()) | ||||||
|  |                 .setResult(any(Drawable.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_success_async_drawable_not_attached() { | ||||||
|  |         // when loading finishes, it must check if async-drawable is attached | ||||||
|  | 
 | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService(mock(Future.class))) | ||||||
|  |                 .addSchemeHandler(new SchemeHandler() { | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                         return ImageItem.withResult(mock(Drawable.class)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public Collection<String> supportedSchemes() { | ||||||
|  |                         return Collections.singleton("ha"); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "ha://hahaha"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  |         when(asyncDrawable.isAttached()).thenReturn(false); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(builder._handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, never()) | ||||||
|  |                 .setResult(any(Drawable.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void load_success_result_null() { | ||||||
|  |         // if result is null (but no exception) - no result must be delivered | ||||||
|  | 
 | ||||||
|  |         // we won't be adding scheme-handler, thus causing internal error | ||||||
|  |         // (will have to mock error-handler because for the tests we re-throw errors) | ||||||
|  |         impl = builder | ||||||
|  |                 .executorService(immediateExecutorService(mock(Future.class))) | ||||||
|  |                 .errorHandler(mock(ErrorHandler.class)) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "xa://xaxaxa"; | ||||||
|  |         final AsyncDrawable asyncDrawable = asyncDrawable(destination); | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); | ||||||
|  |         verify(builder._handler, times(1)) | ||||||
|  |                 .postAtTime(captor.capture(), eq(asyncDrawable), anyLong()); | ||||||
|  | 
 | ||||||
|  |         captor.getValue().run(); | ||||||
|  | 
 | ||||||
|  |         verify(asyncDrawable, never()) | ||||||
|  |                 .setResult(any(Drawable.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void media_decoder_is_used() { | ||||||
|  | 
 | ||||||
|  |         final MediaDecoder mediaDecoder = mock(MediaDecoder.class); | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             when(mediaDecoder.decode(any(String.class), any(InputStream.class))) | ||||||
|  |                     .thenReturn(mock(Drawable.class)); | ||||||
|  |             when(mediaDecoder.supportedTypes()) | ||||||
|  |                     .thenReturn(Collections.singleton("fa/ke")); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl = builder.executorService(immediateExecutorService()) | ||||||
|  |                 .addSchemeHandler(new SchemeHandler() { | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||||
|  |                         return ImageItem.withDecodingNeeded("fa/ke", mock(InputStream.class)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     @NonNull | ||||||
|  |                     @Override | ||||||
|  |                     public Collection<String> supportedSchemes() { | ||||||
|  |                         return Collections.singleton("fake"); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .addMediaDecoder(mediaDecoder) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         final String destination = "fake://1234"; | ||||||
|  | 
 | ||||||
|  |         impl.load(asyncDrawable(destination)); | ||||||
|  | 
 | ||||||
|  |         verify(mediaDecoder, times(1)) | ||||||
|  |                 .decode(eq("fa/ke"), any(InputStream.class)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private static class BuilderImpl { |     private static class BuilderImpl { | ||||||
| 
 | 
 | ||||||
|         AsyncDrawableLoaderBuilder builder; |         AsyncDrawableLoaderBuilder _builder = new AsyncDrawableLoaderBuilder(); | ||||||
|         Handler handler = mock(Handler.class); |         Handler _handler = mock(Handler.class); | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl executorService(@NonNull ExecutorService executorService) { |         { | ||||||
|             builder.executorService(executorService); |             // be default it just logs the exception, let's rethrow | ||||||
|  |             _builder.errorHandler(new ErrorHandler() { | ||||||
|  |                 @Nullable | ||||||
|  |                 @Override | ||||||
|  |                 public Drawable handleError(@NonNull String url, @NonNull Throwable throwable) { | ||||||
|  |                     throw new AsyncDrawableException(url, throwable); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         BuilderImpl executorService(@NonNull ExecutorService executorService) { | ||||||
|  |             _builder.executorService(executorService); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl addSchemeHandler(@NonNull SchemeHandler schemeHandler) { |         BuilderImpl addSchemeHandler(@NonNull SchemeHandler schemeHandler) { | ||||||
|             builder.addSchemeHandler(schemeHandler); |             _builder.addSchemeHandler(schemeHandler); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl addMediaDecoder(@NonNull MediaDecoder mediaDecoder) { |         BuilderImpl addMediaDecoder(@NonNull MediaDecoder mediaDecoder) { | ||||||
|             builder.addMediaDecoder(mediaDecoder); |             _builder.addMediaDecoder(mediaDecoder); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl defaultMediaDecoder(@Nullable MediaDecoder mediaDecoder) { |         BuilderImpl defaultMediaDecoder(@Nullable MediaDecoder mediaDecoder) { | ||||||
|             builder.defaultMediaDecoder(mediaDecoder); |             _builder.defaultMediaDecoder(mediaDecoder); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl removeSchemeHandler(@NonNull String scheme) { |         BuilderImpl placeholderProvider(@NonNull ImagesPlugin.PlaceholderProvider placeholderDrawableProvider) { | ||||||
|             builder.removeSchemeHandler(scheme); |             _builder.placeholderProvider(placeholderDrawableProvider); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public BuilderImpl removeMediaDecoder(@NonNull String contentType) { |         BuilderImpl errorHandler(@NonNull ErrorHandler errorHandler) { | ||||||
|             builder.removeMediaDecoder(contentType); |             _builder.errorHandler(errorHandler); | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public BuilderImpl placeholderProvider(@NonNull ImagesPlugin.PlaceholderProvider placeholderDrawableProvider) { |  | ||||||
|             builder.placeholderProvider(placeholderDrawableProvider); |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public BuilderImpl errorHandler(@NonNull ImagesPlugin.ErrorHandler errorHandler) { |  | ||||||
|             builder.errorHandler(errorHandler); |  | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         public BuilderImpl handler(Handler handler) { |         BuilderImpl handler(Handler handler) { | ||||||
|             this.handler = handler; |             this._handler = handler; | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         AsyncDrawableLoaderImpl build() { |         AsyncDrawableLoaderImpl build() { | ||||||
|             return new AsyncDrawableLoaderImpl(builder, handler); |             return new AsyncDrawableLoaderImpl(_builder, _handler); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static class AsyncDrawableException extends RuntimeException { | ||||||
|  |             AsyncDrawableException(String message, Throwable cause) { | ||||||
|  |                 super(message, cause); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     private static ExecutorService immediateExecutorService() { | ||||||
|  |         return immediateExecutorService(null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     private static ExecutorService immediateExecutorService(@Nullable final Future future) { | ||||||
|  |         final ExecutorService service = mock(ExecutorService.class); | ||||||
|  |         when(service.submit(any(Runnable.class))).then(new Answer<Future>() { | ||||||
|  |             @Override | ||||||
|  |             public Future answer(InvocationOnMock invocation) { | ||||||
|  |                 ((Runnable) invocation.getArgument(0)).run(); | ||||||
|  |                 return future; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         return service; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     private static AsyncDrawable asyncDrawable(@NonNull String destination) { | ||||||
|  |         final AsyncDrawable drawable = mock(AsyncDrawable.class); | ||||||
|  |         when(drawable.getDestination()).thenReturn(destination); | ||||||
|  |         when(drawable.isAttached()).thenReturn(true); | ||||||
|  |         return drawable; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -18,7 +18,6 @@ import java.util.concurrent.ExecutorService; | |||||||
| import io.noties.markwon.MarkwonConfiguration; | import io.noties.markwon.MarkwonConfiguration; | ||||||
| import io.noties.markwon.MarkwonSpansFactory; | import io.noties.markwon.MarkwonSpansFactory; | ||||||
| import io.noties.markwon.SpanFactory; | import io.noties.markwon.SpanFactory; | ||||||
| import ru.noties.markwon.image.R; |  | ||||||
| import io.noties.markwon.image.data.DataUriSchemeHandler; | import io.noties.markwon.image.data.DataUriSchemeHandler; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov