1/*
2 * Copyright 2017 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.constraints.controllers;
18
19import static org.hamcrest.CoreMatchers.is;
20import static org.hamcrest.MatcherAssert.assertThat;
21import static org.mockito.ArgumentMatchers.eq;
22import static org.mockito.Mockito.mock;
23import static org.mockito.Mockito.times;
24import static org.mockito.Mockito.verify;
25import static org.mockito.Mockito.verifyZeroInteractions;
26
27import android.support.annotation.NonNull;
28import android.support.test.filters.SdkSuppress;
29import android.support.test.filters.SmallTest;
30import android.support.test.runner.AndroidJUnit4;
31
32import androidx.work.Constraints;
33import androidx.work.OneTimeWorkRequest;
34import androidx.work.WorkManagerTest;
35import androidx.work.impl.constraints.trackers.ConstraintTracker;
36import androidx.work.impl.model.WorkSpec;
37import androidx.work.worker.TestWorker;
38
39import org.junit.Before;
40import org.junit.Test;
41import org.junit.runner.RunWith;
42
43import java.util.Collections;
44import java.util.List;
45
46@RunWith(AndroidJUnit4.class)
47@SdkSuppress(minSdkVersion = 23)
48public class ConstraintControllerTest extends WorkManagerTest {
49    private TestDeviceIdleConstraintController mTestIdleController;
50    private ConstraintTracker<Boolean> mMockTracker = mock(ConstraintTracker.class);
51    private ConstraintController.OnConstraintUpdatedCallback mCallback =
52            mock(ConstraintController.OnConstraintUpdatedCallback.class);
53
54    @Before
55    public void setUp() {
56        mTestIdleController = new TestDeviceIdleConstraintController(mMockTracker, mCallback);
57    }
58
59    private WorkSpec createTestWorkSpec(Constraints constraints) {
60        return getWorkSpec(new OneTimeWorkRequest.Builder(TestWorker.class)
61                .setConstraints(constraints)
62                .build());
63    }
64
65    private WorkSpec createTestConstraintWorkSpec() {
66        Constraints mConstraintsWithTestConstraint = new Constraints.Builder()
67                .setRequiresDeviceIdle(true)
68                .build();
69
70        return createTestWorkSpec(mConstraintsWithTestConstraint);
71    }
72
73    private WorkSpec createNoConstraintWorkSpec() {
74        return createTestWorkSpec(Constraints.NONE);
75    }
76
77    @Test
78    @SmallTest
79    public void testReplace_empty() {
80        mTestIdleController.replace(Collections.<WorkSpec>emptyList());
81        verify(mMockTracker).removeListener(mTestIdleController);
82        verifyZeroInteractions(mCallback);
83    }
84
85    @Test
86    @SmallTest
87    public void testReplace_workSpecNoConstraints() {
88        WorkSpec workSpecNoConstraints = createNoConstraintWorkSpec();
89        List<WorkSpec> workSpecs = Collections.singletonList(workSpecNoConstraints);
90        mTestIdleController.replace(workSpecs);
91        verify(mMockTracker).removeListener(mTestIdleController);
92        verifyZeroInteractions(mCallback);
93    }
94
95    @Test
96    @SmallTest
97    public void testReplace_workSpecWithConstraint_constrained() {
98        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
99        List<String> expectedWorkIds = Collections.singletonList(workSpecWithConstraint.id);
100        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
101
102        mTestIdleController.setDeviceActive();
103        mTestIdleController.replace(workSpecs);
104        verify(mMockTracker).addListener(mTestIdleController);
105        verify(mCallback).onConstraintNotMet(eq(expectedWorkIds));
106    }
107
108    @Test
109    @SmallTest
110    public void testReplace_workSpecWithConstraint_unconstrained() {
111        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
112        List<String> expectedWorkIds = Collections.singletonList(workSpecWithConstraint.id);
113        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
114
115        mTestIdleController.setDeviceIdle();
116        mTestIdleController.replace(workSpecs);
117        verify(mMockTracker).addListener(mTestIdleController);
118        verify(mCallback).onConstraintMet(eq(expectedWorkIds));
119    }
120
121    @Test
122    @SmallTest
123    public void testReplace_workSpecWithConstraint_constraintNotSet() {
124        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
125        List<String> expectedWorkIds = Collections.singletonList(workSpecWithConstraint.id);
126        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
127
128        mTestIdleController.replace(workSpecs);
129        verify(mCallback).onConstraintNotMet(expectedWorkIds);
130    }
131
132    @Test
133    @SmallTest
134    public void testReset_alreadyNoMatchingWorkSpecs() {
135        mTestIdleController.reset();
136        verifyZeroInteractions(mMockTracker);
137    }
138
139    @Test
140    @SmallTest
141    public void testReset_withMatchingWorkSpecs() {
142        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
143        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
144        mTestIdleController.replace(workSpecs);
145
146        mTestIdleController.reset();
147        verify(mMockTracker).removeListener(mTestIdleController);
148    }
149
150    @Test
151    @SmallTest
152    public void testOnConstraintChanged_noMatchingWorkSpecs() {
153        mTestIdleController.onConstraintChanged(true);
154        verifyZeroInteractions(mCallback);
155    }
156
157    @Test
158    @SmallTest
159    public void testOnConstraintChanged_toConstrained_withMatchingWorkSpecs() {
160        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
161        List<String> expectedWorkIds = Collections.singletonList(workSpecWithConstraint.id);
162        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
163        mTestIdleController.replace(workSpecs);
164        verify(mCallback).onConstraintNotMet(expectedWorkIds);
165
166        final boolean deviceIdle = false;
167        mTestIdleController.onConstraintChanged(deviceIdle);
168        verify(mCallback, times(2)).onConstraintNotMet(expectedWorkIds);
169    }
170
171    @Test
172    @SmallTest
173    public void testOnConstraintChanged_toUnconstrained_withMatchingWorkSpecs() {
174        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
175        List<String> expectedWorkIds = Collections.singletonList(workSpecWithConstraint.id);
176        List<WorkSpec> workSpecs = Collections.singletonList(workSpecWithConstraint);
177        mTestIdleController.replace(workSpecs);
178
179        final boolean deviceIdle = true;
180        mTestIdleController.onConstraintChanged(deviceIdle);
181        verify(mCallback).onConstraintMet(expectedWorkIds);
182    }
183
184    @Test
185    @SmallTest
186    public void testIsWorkSpecConstrained_noMatchingWorkSpecs() {
187        WorkSpec workSpecNoConstraints = createNoConstraintWorkSpec();
188        mTestIdleController.replace(Collections.singletonList(workSpecNoConstraints));
189        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecNoConstraints.id),
190                is(false));
191    }
192
193    @Test
194    @SmallTest
195    public void testIsWorkSpecConstrained_constraintNotSet() {
196        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
197        mTestIdleController.replace(Collections.singletonList(workSpecWithConstraint));
198        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecWithConstraint.id),
199                is(false));
200    }
201
202    @Test
203    @SmallTest
204    public void testIsWorkSpecConstrained_constrained_withMatchingWorkSpecs() {
205        mTestIdleController.setDeviceActive();
206
207        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
208        mTestIdleController.replace(Collections.singletonList(workSpecWithConstraint));
209        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecWithConstraint.id),
210                is(true));
211    }
212
213    @Test
214    @SmallTest
215    public void testIsWorkSpecConstrained_constrained_noMatchingWorkSpecs() {
216        mTestIdleController.setDeviceActive();
217
218        WorkSpec workSpecNoConstraints = createNoConstraintWorkSpec();
219        mTestIdleController.replace(Collections.singletonList(workSpecNoConstraints));
220        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecNoConstraints.id),
221                is(false));
222    }
223
224    @Test
225    @SmallTest
226    public void testIsWorkSpecConstrained_unconstrained_withMatchingWorkSpecs() {
227        mTestIdleController.setDeviceIdle();
228
229        WorkSpec workSpecWithConstraint = createTestConstraintWorkSpec();
230        mTestIdleController.replace(Collections.singletonList(workSpecWithConstraint));
231        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecWithConstraint.id),
232                is(false));
233    }
234
235    @Test
236    @SmallTest
237    public void testIsWorkSpecConstrained_unconstrained_noMatchingWorkSpecs() {
238        mTestIdleController.setDeviceIdle();
239
240        WorkSpec workSpecNoConstraints = createNoConstraintWorkSpec();
241        mTestIdleController.replace(Collections.singletonList(workSpecNoConstraints));
242        assertThat(mTestIdleController.isWorkSpecConstrained(workSpecNoConstraints.id),
243                is(false));
244    }
245
246    private static class TestDeviceIdleConstraintController extends ConstraintController<Boolean> {
247        TestDeviceIdleConstraintController(ConstraintTracker<Boolean> tracker,
248                OnConstraintUpdatedCallback callback) {
249            super(tracker, callback);
250        }
251
252        @Override
253        boolean hasConstraint(@NonNull WorkSpec workSpec) {
254            return workSpec.constraints.requiresDeviceIdle();
255        }
256
257        @Override
258        boolean isConstrained(@NonNull Boolean isDeviceIdle) {
259            return !isDeviceIdle;
260        }
261
262        void setDeviceActive() {
263            onConstraintChanged(false);
264        }
265
266        void setDeviceIdle() {
267            onConstraintChanged(true);
268        }
269    }
270}
271