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