1package org.robolectric.shadows;
2
3import static org.assertj.core.api.Assertions.assertThat;
4import static org.assertj.core.api.Assertions.fail;
5import static org.junit.Assert.assertEquals;
6import static org.junit.Assert.assertFalse;
7import static org.junit.Assert.assertTrue;
8
9import android.os.AsyncTask;
10import java.util.ArrayList;
11import java.util.List;
12import java.util.concurrent.Executor;
13import java.util.concurrent.TimeUnit;
14import org.junit.Before;
15import org.junit.Test;
16import org.junit.runner.RunWith;
17import org.robolectric.Robolectric;
18import org.robolectric.RobolectricTestRunner;
19import org.robolectric.util.Join;
20
21@RunWith(RobolectricTestRunner.class)
22public class ShadowAsyncTaskTest {
23  private List<String> transcript;
24
25  @Before
26  public void setUp() throws Exception {
27    transcript = new ArrayList<>();
28    Robolectric.getBackgroundThreadScheduler().pause();
29    Robolectric.getForegroundThreadScheduler().pause();
30  }
31
32  @Test
33  public void testNormalFlow() throws Exception {
34    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
35
36    asyncTask.execute("a", "b");
37    assertThat(transcript).containsExactly("onPreExecute");
38    transcript.clear();
39
40    ShadowApplication.runBackgroundTasks();
41    assertThat(transcript).containsExactly("doInBackground a, b");
42    transcript.clear();
43    assertEquals("Result should get stored in the AsyncTask", "c", asyncTask.get(100, TimeUnit.MILLISECONDS));
44
45    ShadowLooper.runUiThreadTasks();
46    assertThat(transcript).containsExactly("onPostExecute c");
47  }
48
49  @Test
50  public void testCancelBeforeBackground() throws Exception {
51    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
52
53    asyncTask.execute("a", "b");
54    assertThat(transcript).containsExactly("onPreExecute");
55    transcript.clear();
56
57    assertTrue(asyncTask.cancel(true));
58    assertTrue(asyncTask.isCancelled());
59
60    ShadowApplication.runBackgroundTasks();
61    assertThat(transcript).isEmpty();
62
63    ShadowLooper.runUiThreadTasks();
64    assertThat(transcript).containsExactly("onCancelled");
65  }
66
67  @Test
68  public void testCancelBeforePostExecute() throws Exception {
69    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
70
71    asyncTask.execute("a", "b");
72    assertThat(transcript).containsExactly("onPreExecute");
73    transcript.clear();
74
75    ShadowApplication.runBackgroundTasks();
76    assertThat(transcript).containsExactly("doInBackground a, b");
77    transcript.clear();
78    assertEquals("Result should get stored in the AsyncTask", "c", asyncTask.get(100, TimeUnit.MILLISECONDS));
79
80    assertFalse(asyncTask.cancel(true));
81    assertFalse(asyncTask.isCancelled());
82
83    ShadowLooper.runUiThreadTasks();
84    assertThat(transcript).containsExactly("onPostExecute c");
85  }
86
87  @Test
88  public void progressUpdatesAreQueuedUntilBackgroundThreadFinishes() throws Exception {
89    AsyncTask<String, String, String> asyncTask = new MyAsyncTask() {
90      @Override
91      protected String doInBackground(String... strings) {
92        publishProgress("33%");
93        publishProgress("66%");
94        publishProgress("99%");
95        return "done";
96      }
97    };
98
99    asyncTask.execute("a", "b");
100    assertThat(transcript).containsExactly("onPreExecute");
101    transcript.clear();
102
103    ShadowApplication.runBackgroundTasks();
104    assertThat(transcript).isEmpty();
105    assertEquals("Result should get stored in the AsyncTask", "done", asyncTask.get(100, TimeUnit.MILLISECONDS));
106
107    ShadowLooper.runUiThreadTasks();
108    assertThat(transcript).containsExactly(
109        "onProgressUpdate 33%",
110        "onProgressUpdate 66%",
111        "onProgressUpdate 99%",
112        "onPostExecute done"
113    );
114  }
115
116  @Test
117  public void executeReturnsAsyncTask() throws Exception {
118    Robolectric.getBackgroundThreadScheduler().unPause();
119    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
120    assertThat(asyncTask.execute("a", "b").get()).isEqualTo("c");
121  }
122
123  @Test
124  public void shouldGetStatusForAsyncTask() throws Exception {
125    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
126    assertThat(asyncTask.getStatus()).isEqualTo(AsyncTask.Status.PENDING);
127    asyncTask.execute("a");
128    assertThat(asyncTask.getStatus()).isEqualTo(AsyncTask.Status.RUNNING);
129    Robolectric.getBackgroundThreadScheduler().unPause();
130    assertThat(asyncTask.getStatus()).isEqualTo(AsyncTask.Status.FINISHED);
131  }
132
133  @Test
134  public void onPostExecute_doesNotSwallowExceptions() throws Exception {
135    Robolectric.getBackgroundThreadScheduler().unPause();
136    Robolectric.getForegroundThreadScheduler().unPause();
137
138    AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
139      @Override
140      protected Void doInBackground(Void... params) {
141        return null;
142      }
143
144      @Override
145      protected void onPostExecute(Void aVoid) {
146        throw new RuntimeException("Don't swallow me!");
147      }
148    };
149
150    try {
151      asyncTask.execute();
152      fail("Task swallowed onPostExecute() exception!");
153    } catch (RuntimeException e) {
154      assertThat(e.getCause().getMessage()).isEqualTo("Don't swallow me!");
155    }
156  }
157
158  @Test
159  public void executeOnExecutor_usesPassedExecutor() throws Exception {
160    AsyncTask<String, String, String> asyncTask = new MyAsyncTask();
161
162    assertThat(asyncTask.getStatus()).isEqualTo(AsyncTask.Status.PENDING);
163
164    asyncTask.executeOnExecutor(new ImmediateExecutor(), "a", "b");
165
166    assertThat(asyncTask.getStatus()).isEqualTo(AsyncTask.Status.FINISHED);
167    assertThat(transcript).containsExactly("onPreExecute", "doInBackground a, b");
168    transcript.clear();
169    assertEquals("Result should get stored in the AsyncTask", "c", asyncTask.get());
170
171    ShadowLooper.runUiThreadTasks();
172    assertThat(transcript).containsExactly("onPostExecute c");
173  }
174
175  private class MyAsyncTask extends AsyncTask<String, String, String> {
176    @Override
177    protected void onPreExecute() {
178      transcript.add("onPreExecute");
179    }
180
181    @Override
182    protected String doInBackground(String... strings) {
183      transcript.add("doInBackground " + Join.join(", ", (Object[]) strings));
184      return "c";
185    }
186
187    @Override
188    protected void onProgressUpdate(String... values) {
189      transcript.add("onProgressUpdate " + Join.join(", ", (Object[]) values));
190    }
191
192    @Override
193    protected void onPostExecute(String s) {
194      transcript.add("onPostExecute " + s);
195    }
196
197    @Override
198    protected void onCancelled() {
199      transcript.add("onCancelled");
200    }
201  }
202
203  public static class ImmediateExecutor implements Executor {
204    @Override
205    public void execute(Runnable command) {
206      command.run();
207    }
208  }
209}
210