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