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