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