1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.loader.content; 18 19import static org.junit.Assert.fail; 20 21import android.support.test.InstrumentationRegistry; 22import android.support.test.filters.LargeTest; 23import android.support.test.runner.AndroidJUnit4; 24 25import androidx.annotation.NonNull; 26 27import org.junit.Test; 28import org.junit.runner.RunWith; 29 30import java.util.concurrent.CountDownLatch; 31import java.util.concurrent.Executor; 32import java.util.concurrent.TimeUnit; 33 34@RunWith(AndroidJUnit4.class) 35public class ModernAsyncTaskTest { 36 37 ModernAsyncTask mModernAsyncTask; 38 39 /** 40 * Test to ensure that onCancelled is always called, even if doInBackground throws an exception. 41 * 42 * @throws Throwable 43 */ 44 @LargeTest 45 @Test 46 public void testCancellationWithException() throws Throwable { 47 final CountDownLatch readyToCancel = new CountDownLatch(1); 48 final CountDownLatch readyToThrow = new CountDownLatch(1); 49 final CountDownLatch calledOnCancelled = new CountDownLatch(1); 50 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 51 @Override 52 public void run() { 53 mModernAsyncTask = new ModernAsyncTask() { 54 @Override 55 protected Object doInBackground(Object[] params) { 56 readyToCancel.countDown(); 57 try { 58 readyToThrow.await(); 59 } catch (InterruptedException e) { } 60 // This exception is expected to be caught and ignored 61 throw new RuntimeException(); 62 } 63 64 @Override 65 protected void onCancelled(Object o) { 66 calledOnCancelled.countDown(); 67 } 68 }; 69 } 70 }); 71 72 mModernAsyncTask.execute(); 73 if (!readyToCancel.await(5, TimeUnit.SECONDS)) { 74 fail("Test failure: doInBackground did not run in time."); 75 } 76 mModernAsyncTask.cancel(false); 77 readyToThrow.countDown(); 78 if (!calledOnCancelled.await(5, TimeUnit.SECONDS)) { 79 fail("onCancelled not called!"); 80 } 81 } 82 83 /** 84 * Test to ensure that onCancelled is always called instead of onPostExecute when the exception 85 * is not suppressed by cancelling the task. 86 * 87 * @throws Throwable 88 */ 89 @LargeTest 90 @Test 91 public void testException() throws Throwable { 92 final CountDownLatch calledOnCancelled = new CountDownLatch(1); 93 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 94 @Override 95 public void run() { 96 mModernAsyncTask = new ModernAsyncTask() { 97 @Override 98 protected Object doInBackground(Object[] params) { 99 throw new RuntimeException(); 100 } 101 102 @Override 103 protected void onPostExecute(Object o) { 104 fail("onPostExecute should not be called"); 105 } 106 107 @Override 108 protected void onCancelled(Object o) { 109 calledOnCancelled.countDown(); 110 } 111 }; 112 } 113 }); 114 115 mModernAsyncTask.executeOnExecutor(new Executor() { 116 @Override 117 public void execute(@NonNull Runnable command) { 118 try { 119 command.run(); 120 fail("Exception not thrown"); 121 } catch (Throwable tr) { 122 // expected 123 } 124 } 125 }); 126 127 if (!calledOnCancelled.await(5, TimeUnit.SECONDS)) { 128 fail("onCancelled not called"); 129 } 130 } 131} 132