WorkManagerImplLargeExecutorTest.java revision e326592ec414dfe4c002e2840d9fc4aef0ee8747
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; 18 19import static androidx.work.worker.CheckLimitsWorker.KEY_EXCEEDS_SCHEDULER_LIMIT; 20import static androidx.work.worker.CheckLimitsWorker.KEY_LIMIT_TO_ENFORCE; 21 22import static org.hamcrest.CoreMatchers.is; 23import static org.hamcrest.MatcherAssert.assertThat; 24 25import static java.util.concurrent.TimeUnit.SECONDS; 26 27import android.arch.core.executor.ArchTaskExecutor; 28import android.arch.core.executor.TaskExecutor; 29import android.arch.lifecycle.Observer; 30import android.content.Context; 31import android.support.annotation.NonNull; 32import android.support.annotation.Nullable; 33import android.support.test.InstrumentationRegistry; 34import android.support.test.filters.LargeTest; 35import android.support.test.filters.SdkSuppress; 36import android.support.test.runner.AndroidJUnit4; 37 38import androidx.work.Configuration; 39import androidx.work.Data; 40import androidx.work.OneTimeWorkRequest; 41import androidx.work.TestLifecycleOwner; 42import androidx.work.WorkContinuation; 43import androidx.work.WorkStatus; 44import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule; 45import androidx.work.worker.CheckLimitsWorker; 46 47import org.junit.After; 48import org.junit.Before; 49import org.junit.Rule; 50import org.junit.Test; 51import org.junit.runner.RunWith; 52 53import java.util.ArrayList; 54import java.util.HashSet; 55import java.util.List; 56import java.util.Set; 57import java.util.UUID; 58import java.util.concurrent.BlockingQueue; 59import java.util.concurrent.CountDownLatch; 60import java.util.concurrent.Executor; 61import java.util.concurrent.LinkedBlockingQueue; 62import java.util.concurrent.ThreadPoolExecutor; 63import java.util.concurrent.TimeUnit; 64 65@RunWith(AndroidJUnit4.class) 66public class WorkManagerImplLargeExecutorTest { 67 68 private static final int NUM_WORKERS = 500; 69 private static final int TEST_TIMEOUT_SECONDS = 30; 70 71 // ThreadPoolExecutor parameters. 72 private static final int MIN_POOL_SIZE = 0; 73 // Allocate more threads than the MAX_SCHEDULER_LIMIT 74 private static final int MAX_POOL_SIZE = 150; 75 // Keep alive time for a thread before its claimed. 76 private static final long KEEP_ALIVE_TIME = 2L; 77 78 private WorkManagerImpl mWorkManagerImpl; 79 private TestLifecycleOwner mLifecycleOwner; 80 81 @Rule 82 public InstantTaskExecutorRule mRule = new InstantTaskExecutorRule(); 83 84 @Before 85 public void setUp() { 86 ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() { 87 @Override 88 public void executeOnDiskIO(@NonNull Runnable runnable) { 89 runnable.run(); 90 } 91 92 @Override 93 public void postToMainThread(@NonNull Runnable runnable) { 94 runnable.run(); 95 } 96 97 @Override 98 public boolean isMainThread() { 99 return true; 100 } 101 }); 102 103 Context context = InstrumentationRegistry.getTargetContext(); 104 BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); 105 Executor executor = new ThreadPoolExecutor( 106 MIN_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, SECONDS, queue); 107 Configuration configuration = new Configuration.Builder() 108 .setExecutor(executor) 109 .setMaxSchedulerLimit(50) 110 .build(); 111 mWorkManagerImpl = new WorkManagerImpl(context, configuration, true); 112 mLifecycleOwner = new TestLifecycleOwner(); 113 WorkManagerImpl.setDelegate(mWorkManagerImpl); 114 } 115 116 @After 117 public void tearDown() { 118 WorkManagerImpl.setDelegate(null); 119 ArchTaskExecutor.getInstance().setDelegate(null); 120 } 121 122 @Test 123 @LargeTest 124 @SdkSuppress(maxSdkVersion = 22) 125 public void testSchedulerLimits() throws InterruptedException { 126 List<OneTimeWorkRequest> workRequests = new ArrayList<>(NUM_WORKERS); 127 final Set<UUID> completed = new HashSet<>(NUM_WORKERS); 128 final int schedulerLimit = mWorkManagerImpl 129 .getConfiguration() 130 .getMaxSchedulerLimit(); 131 132 final Data input = new Data.Builder() 133 .putInt(KEY_LIMIT_TO_ENFORCE, schedulerLimit) 134 .build(); 135 136 for (int i = 0; i < NUM_WORKERS; i++) { 137 OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(CheckLimitsWorker.class) 138 .setInputData(input) 139 .build(); 140 141 workRequests.add(request); 142 } 143 144 145 final CountDownLatch latch = new CountDownLatch(NUM_WORKERS); 146 WorkContinuation continuation = mWorkManagerImpl.beginWith(workRequests); 147 148 continuation.getStatuses() 149 .observe(mLifecycleOwner, new Observer<List<WorkStatus>>() { 150 @Override 151 public void onChanged(@Nullable List<WorkStatus> workStatuses) { 152 if (workStatuses == null || workStatuses.isEmpty()) { 153 return; 154 } 155 156 for (WorkStatus workStatus: workStatuses) { 157 if (workStatus.getState().isFinished()) { 158 159 Data output = workStatus.getOutputData(); 160 161 boolean exceededLimits = output.getBoolean( 162 KEY_EXCEEDS_SCHEDULER_LIMIT, true); 163 164 assertThat(exceededLimits, is(false)); 165 if (!completed.contains(workStatus.getId())) { 166 completed.add(workStatus.getId()); 167 latch.countDown(); 168 } 169 } 170 } 171 } 172 }); 173 174 continuation.enqueue(); 175 latch.await(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); 176 assertThat(latch.getCount(), is(0L)); 177 } 178} 179