ConstraintTrackingWorkerTest.java revision b24ef38a0b526b524e3e6a7849f62f934a6ad58c
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.work.impl.workers;
18
19import static org.hamcrest.CoreMatchers.is;
20import static org.hamcrest.MatcherAssert.assertThat;
21import static org.mockito.Mockito.mock;
22import static org.mockito.Mockito.spy;
23import static org.mockito.Mockito.when;
24
25import android.content.Context;
26import android.os.Handler;
27import android.os.Looper;
28import android.support.annotation.NonNull;
29import android.support.test.InstrumentationRegistry;
30import android.support.test.filters.SdkSuppress;
31import android.support.test.filters.SmallTest;
32import android.support.test.runner.AndroidJUnit4;
33
34import androidx.work.Constraints;
35import androidx.work.Data;
36import androidx.work.DatabaseTest;
37import androidx.work.OneTimeWorkRequest;
38import androidx.work.State;
39import androidx.work.impl.ExecutionListener;
40import androidx.work.impl.Extras;
41import androidx.work.impl.Scheduler;
42import androidx.work.impl.WorkManagerImpl;
43import androidx.work.impl.WorkerWrapper;
44import androidx.work.impl.constraints.trackers.BatteryChargingTracker;
45import androidx.work.impl.constraints.trackers.BatteryNotLowTracker;
46import androidx.work.impl.constraints.trackers.NetworkStateTracker;
47import androidx.work.impl.constraints.trackers.StorageNotLowTracker;
48import androidx.work.impl.constraints.trackers.Trackers;
49import androidx.work.impl.model.WorkSpec;
50import androidx.work.worker.EchoingWorker;
51import androidx.work.worker.SleepTestWorker;
52import androidx.work.worker.TestWorker;
53
54import org.junit.After;
55import org.junit.Before;
56import org.junit.Test;
57import org.junit.runner.RunWith;
58
59import java.util.Collections;
60import java.util.concurrent.CountDownLatch;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.Executors;
63import java.util.concurrent.TimeUnit;
64
65@RunWith(AndroidJUnit4.class)
66@SmallTest
67public class ConstraintTrackingWorkerTest extends DatabaseTest implements ExecutionListener {
68
69    private static final long DELAY_IN_MILLIS = 100;
70    private static final long TEST_TIMEOUT_IN_SECONDS = 6;
71    private static final String TEST_ARGUMENT_NAME = "test";
72
73    private Context mContext;
74    private Handler mHandler;
75    private CountDownLatch mLatch;
76    private ExecutorService mExecutorService;
77
78    private WorkManagerImpl mWorkManagerImpl;
79    private Scheduler mScheduler;
80    private Trackers mTracker;
81    private BatteryChargingTracker mBatteryChargingTracker;
82    private BatteryNotLowTracker mBatteryNotLowTracker;
83    private NetworkStateTracker mNetworkStateTracker;
84    private StorageNotLowTracker mStorageNotLowTracker;
85
86    @Before
87    public void setUp() {
88        mContext = InstrumentationRegistry.getTargetContext().getApplicationContext();
89        mHandler = new Handler(Looper.getMainLooper());
90        mExecutorService = Executors.newSingleThreadScheduledExecutor();
91        mLatch = new CountDownLatch(1);
92
93        mWorkManagerImpl = mock(WorkManagerImpl.class);
94        mScheduler = mock(Scheduler.class);
95        when(mWorkManagerImpl.getWorkDatabase()).thenReturn(mDatabase);
96
97        mBatteryChargingTracker = spy(new BatteryChargingTracker(mContext));
98        mBatteryNotLowTracker = spy(new BatteryNotLowTracker(mContext));
99        // Requires API 24+ types.
100        mNetworkStateTracker = mock(NetworkStateTracker.class);
101        mStorageNotLowTracker = spy(new StorageNotLowTracker(mContext));
102        mTracker = mock(Trackers.class);
103
104        when(mTracker.getBatteryChargingTracker()).thenReturn(mBatteryChargingTracker);
105        when(mTracker.getBatteryNotLowTracker()).thenReturn(mBatteryNotLowTracker);
106        when(mTracker.getNetworkStateTracker()).thenReturn(mNetworkStateTracker);
107        when(mTracker.getStorageNotLowTracker()).thenReturn(mStorageNotLowTracker);
108
109        // Override Trackers being used by WorkConstraintsProxy
110        Trackers.setInstance(mTracker);
111    }
112
113    @After
114    public void tearDown() {
115        mExecutorService.shutdownNow();
116    }
117
118    @Test
119    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
120    public void testConstraintTrackingWorker_onConstraintsMet() throws InterruptedException {
121        when(mBatteryNotLowTracker.getInitialState()).thenReturn(true);
122        Constraints constraints = new Constraints.Builder()
123                .setRequiresBatteryNotLow(true)
124                .build();
125
126        String delegateName = EchoingWorker.class.getName();
127
128        Data input = new Data.Builder()
129                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, delegateName)
130                .putBoolean(TEST_ARGUMENT_NAME, true)
131                .build();
132
133        final OneTimeWorkRequest work =
134                new OneTimeWorkRequest.Builder(ConstraintTrackingWorker.class)
135                    .setInputData(input)
136                    .setConstraints(constraints)
137                    .build();
138
139        insertWork(work);
140        String workSpecId = work.getStringId();
141
142        ConstraintTrackingWorker worker =
143                (ConstraintTrackingWorker) WorkerWrapper.workerFromClassName(
144                        mContext,
145                        ConstraintTrackingWorker.class.getName(),
146                        work.getId(),
147                        new Extras(input, Collections.<String>emptyList(), null, 1));
148
149        ConstraintTrackingWorker spyWorker = spy(worker);
150        when(spyWorker.getWorkDatabase()).thenReturn(mDatabase);
151
152        WorkerWrapper.Builder builder = new WorkerWrapper.Builder(mContext, mDatabase, workSpecId);
153        builder.withWorker(spyWorker)
154                .withListener(this)
155                .withSchedulers(Collections.singletonList(mScheduler));
156
157        mExecutorService.submit(builder.build());
158        mLatch.await(TEST_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
159        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(workSpecId);
160        assertThat(mLatch.getCount(), is(0L));
161        assertThat(workSpec.state, is(State.SUCCEEDED));
162        Data output = workSpec.output;
163        assertThat(output.getBoolean(TEST_ARGUMENT_NAME, false), is(true));
164    }
165
166    @Test
167    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
168    public void testConstraintTrackingWorker_onConstraintsNotMet() throws InterruptedException {
169        when(mBatteryNotLowTracker.getInitialState()).thenReturn(false);
170        Constraints constraints = new Constraints.Builder()
171                .setRequiresBatteryNotLow(true)
172                .build();
173
174        String delegateName = TestWorker.class.getName();
175        Data input = new Data.Builder()
176                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, delegateName)
177                .build();
178
179        final OneTimeWorkRequest work =
180                new OneTimeWorkRequest.Builder(ConstraintTrackingWorker.class)
181                    .setConstraints(constraints)
182                    .build();
183
184        insertWork(work);
185        String workSpecId = work.getStringId();
186
187        ConstraintTrackingWorker worker =
188                (ConstraintTrackingWorker) WorkerWrapper.workerFromClassName(
189                        mContext,
190                        ConstraintTrackingWorker.class.getName(),
191                        work.getId(),
192                        new Extras(input, Collections.<String>emptyList(), null, 1));
193
194        ConstraintTrackingWorker spyWorker = spy(worker);
195        when(spyWorker.getWorkDatabase()).thenReturn(mDatabase);
196
197        WorkerWrapper.Builder builder = new WorkerWrapper.Builder(mContext, mDatabase, workSpecId);
198        builder.withWorker(spyWorker)
199                .withListener(this)
200                .withSchedulers(Collections.singletonList(mScheduler));
201
202        mExecutorService.submit(builder.build());
203        mLatch.await(TEST_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
204        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(workSpecId);
205        assertThat(mLatch.getCount(), is(0L));
206        assertThat(workSpec.state, is(State.ENQUEUED));
207    }
208
209    @Test
210    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
211    public void testConstraintTrackingWorker_onConstraintsChanged() throws InterruptedException {
212        when(mBatteryNotLowTracker.getInitialState()).thenReturn(true);
213        Constraints constraints = new Constraints.Builder()
214                .setRequiresBatteryNotLow(true)
215                .build();
216
217        String delegateName = SleepTestWorker.class.getName();
218        Data input = new Data.Builder()
219                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, delegateName)
220                .build();
221
222        final OneTimeWorkRequest work =
223                new OneTimeWorkRequest.Builder(ConstraintTrackingWorker.class)
224                    .setConstraints(constraints)
225                    .build();
226
227        insertWork(work);
228
229        String workSpecId = work.getStringId();
230
231        ConstraintTrackingWorker worker =
232                (ConstraintTrackingWorker) WorkerWrapper.workerFromClassName(
233                        mContext,
234                        ConstraintTrackingWorker.class.getName(),
235                        work.getId(),
236                        new Extras(input, Collections.<String>emptyList(), null, 1));
237
238        ConstraintTrackingWorker spyWorker = spy(worker);
239        when(spyWorker.getWorkDatabase()).thenReturn(mDatabase);
240
241        WorkerWrapper.Builder builder = new WorkerWrapper.Builder(mContext, mDatabase, workSpecId);
242        builder.withWorker(spyWorker)
243                .withListener(this)
244                .withSchedulers(Collections.singletonList(mScheduler));
245
246        mExecutorService.submit(builder.build());
247        mHandler.postDelayed(new Runnable() {
248            @Override
249            public void run() {
250                mBatteryNotLowTracker.setState(false);
251            }
252        }, DELAY_IN_MILLIS);
253
254        mLatch.await(TEST_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
255        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(workSpecId);
256        assertThat(mLatch.getCount(), is(0L));
257        assertThat(workSpec.state, is(State.ENQUEUED));
258    }
259
260    @Test
261    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
262    public void testConstraintTrackingWorker_onConstraintsChangedTwice()
263            throws InterruptedException {
264        when(mBatteryNotLowTracker.getInitialState()).thenReturn(true);
265        Constraints constraints = new Constraints.Builder()
266                .setRequiresBatteryNotLow(true)
267                .build();
268
269        String delegateName = SleepTestWorker.class.getName();
270        Data input = new Data.Builder()
271                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, delegateName)
272                .build();
273
274        final OneTimeWorkRequest work =
275                new OneTimeWorkRequest.Builder(ConstraintTrackingWorker.class)
276                    .setConstraints(constraints)
277                    .build();
278
279        insertWork(work);
280
281        String workSpecId = work.getStringId();
282
283        ConstraintTrackingWorker worker =
284                (ConstraintTrackingWorker) WorkerWrapper.workerFromClassName(
285                        mContext,
286                        ConstraintTrackingWorker.class.getName(),
287                        work.getId(),
288                        new Extras(input, Collections.<String>emptyList(), null, 1));
289
290        ConstraintTrackingWorker spyWorker = spy(worker);
291        when(spyWorker.getWorkDatabase()).thenReturn(mDatabase);
292
293        WorkerWrapper.Builder builder = new WorkerWrapper.Builder(mContext, mDatabase, workSpecId);
294        builder.withWorker(spyWorker)
295                .withListener(this)
296                .withSchedulers(Collections.singletonList(mScheduler));
297
298        mExecutorService.submit(builder.build());
299
300        mHandler.postDelayed(new Runnable() {
301            @Override
302            public void run() {
303                mBatteryNotLowTracker.setState(false);
304            }
305        }, DELAY_IN_MILLIS);
306
307        mHandler.postDelayed(new Runnable() {
308            @Override
309            public void run() {
310                mBatteryNotLowTracker.setState(true);
311            }
312        }, DELAY_IN_MILLIS);
313
314        mLatch.await(TEST_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
315        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(workSpecId);
316        assertThat(mLatch.getCount(), is(0L));
317        assertThat(workSpec.state, is(State.ENQUEUED));
318    }
319
320    @Override
321    public void onExecuted(
322            @NonNull String workSpecId,
323            boolean isSuccessful,
324            boolean needsReschedule) {
325        mLatch.countDown();
326    }
327}
328