1 /* 2 * Copyright (C) 2016 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 17 package com.android.phone.vvm.omtp.scheduling; 18 19 import static org.mockito.Matchers.any; 20 import static org.mockito.Mockito.mock; 21 import static org.mockito.Mockito.when; 22 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.IBinder; 27 import android.os.Message; 28 import android.support.test.InstrumentationRegistry; 29 import android.support.test.rule.ServiceTestRule; 30 import android.support.test.runner.AndroidJUnit4; 31 32 import com.android.phone.Assert; 33 import com.android.phone.vvm.omtp.scheduling.Task.TaskId; 34 import com.android.phone.vvm.omtp.scheduling.TaskSchedulerService.MainThreadHandler; 35 import com.android.phone.vvm.omtp.scheduling.TaskSchedulerService.WorkerThreadHandler; 36 37 import org.junit.After; 38 import org.junit.Before; 39 import org.junit.Rule; 40 import org.junit.rules.ExpectedException; 41 import org.junit.runner.RunWith; 42 43 import java.util.concurrent.TimeoutException; 44 45 @RunWith(AndroidJUnit4.class) 46 public class TaskSchedulerServiceTestBase { 47 48 private static final String EXTRA_ID = "test_extra_id"; 49 private static final String EXTRA_SUB_ID = "test_extra_sub_id"; 50 51 public TaskSchedulerService mService; 52 53 @Rule 54 public final ExpectedException thrown = ExpectedException.none(); 55 56 @Rule 57 public final ServiceTestRule mServiceRule = new ServiceTestRule(); 58 59 public Context mTargetContext; 60 public Context mTestContext; 61 62 private static boolean sIsMainThread = true; 63 64 private final TestMessageSender mMessageSender = new TestMessageSender(); 65 putTaskId(Intent intent, TaskId taskId)66 public static Intent putTaskId(Intent intent, TaskId taskId) { 67 intent.putExtra(EXTRA_ID, taskId.id); 68 intent.putExtra(EXTRA_SUB_ID, taskId.subId); 69 return intent; 70 } 71 getTaskId(Intent intent)72 public static TaskId getTaskId(Intent intent) { 73 return new TaskId(intent.getIntExtra(EXTRA_ID, 0), intent.getIntExtra(EXTRA_SUB_ID, 0)); 74 } 75 76 @Before setUp()77 public void setUp() throws TimeoutException { 78 Assert.setIsMainThreadForTesting(true); 79 mTargetContext = InstrumentationRegistry.getTargetContext(); 80 IBinder binder = null; 81 // bindService() might returns null on 2nd invocation because the service is not unbinded 82 // yet. See https://code.google.com/p/android/issues/detail?id=180396 83 while (binder == null) { 84 binder = mServiceRule 85 .bindService(new Intent(mTargetContext, TaskSchedulerService.class)); 86 } 87 mService = ((TaskSchedulerService.LocalBinder) binder).getService(); 88 mTestContext = createTestContext(mTargetContext, mService); 89 mService.setMessageSenderForTest(mMessageSender); 90 mService.setTaskAutoRunDisabledForTest(true); 91 mService.setContextForTest(mTestContext); 92 } 93 94 @After tearDown()95 public void tearDown() { 96 Assert.setIsMainThreadForTesting(null); 97 mService.setTaskAutoRunDisabledForTest(false); 98 mService.clearTasksForTest(); 99 mService.stopSelf(); 100 } 101 submitTask(Intent intent)102 public Task submitTask(Intent intent) { 103 Task task = mService.createTask(intent, 0, 0); 104 mService.addTask(task); 105 return task; 106 } 107 verifyRanOnce(TestTask task)108 public static void verifyRanOnce(TestTask task) { 109 assertTrue(task.onBeforeExecuteCounter.invokedOnce()); 110 assertTrue(task.executeCounter.invokedOnce()); 111 assertTrue(task.onCompletedCounter.invokedOnce()); 112 } 113 verifyNotRan(TestTask task)114 public static void verifyNotRan(TestTask task) { 115 assertTrue(task.onBeforeExecuteCounter.neverInvoked()); 116 assertTrue(task.executeCounter.neverInvoked()); 117 assertTrue(task.onCompletedCounter.neverInvoked()); 118 } 119 120 public static class TestTask implements Task { 121 122 public int readyInMilliseconds; 123 124 private TaskId mId; 125 126 public final InvocationCounter onCreateCounter = new InvocationCounter(); 127 public final InvocationCounter onBeforeExecuteCounter = new InvocationCounter(); 128 public final InvocationCounter executeCounter = new InvocationCounter(); 129 public final InvocationCounter onCompletedCounter = new InvocationCounter(); 130 public final InvocationCounter onDuplicatedTaskAddedCounter = new InvocationCounter(); 131 132 @Override onCreate(Context context, Intent intent, int flags, int startId)133 public void onCreate(Context context, Intent intent, int flags, int startId) { 134 onCreateCounter.invoke(); 135 mId = getTaskId(intent); 136 } 137 138 @Override getId()139 public TaskId getId() { 140 return mId; 141 } 142 143 @Override getReadyInMilliSeconds()144 public long getReadyInMilliSeconds() { 145 Assert.isMainThread(); 146 return readyInMilliseconds; 147 } 148 149 @Override onBeforeExecute()150 public void onBeforeExecute() { 151 Assert.isMainThread(); 152 onBeforeExecuteCounter.invoke(); 153 } 154 155 @Override onExecuteInBackgroundThread()156 public void onExecuteInBackgroundThread() { 157 Assert.isNotMainThread(); 158 executeCounter.invoke(); 159 } 160 161 @Override onCompleted()162 public void onCompleted() { 163 Assert.isMainThread(); 164 onCompletedCounter.invoke(); 165 } 166 167 @Override onDuplicatedTaskAdded(Task task)168 public void onDuplicatedTaskAdded(Task task) { 169 Assert.isMainThread(); 170 onDuplicatedTaskAddedCounter.invoke(); 171 } 172 } 173 174 public static class InvocationCounter { 175 176 private int mCounter; 177 invoke()178 public void invoke() { 179 mCounter++; 180 } 181 invokedOnce()182 public boolean invokedOnce() { 183 return mCounter == 1; 184 } 185 neverInvoked()186 public boolean neverInvoked() { 187 return mCounter == 0; 188 } 189 } 190 191 private class TestMessageSender extends TaskSchedulerService.MessageSender { 192 193 @Override send(Message message)194 public void send(Message message) { 195 if (message.getTarget() instanceof MainThreadHandler) { 196 Assert.setIsMainThreadForTesting(true); 197 } else if (message.getTarget() instanceof WorkerThreadHandler) { 198 Assert.setIsMainThreadForTesting(false); 199 } else { 200 throw new AssertionError("unexpected Handler " + message.getTarget()); 201 } 202 message.getTarget().handleMessage(message); 203 } 204 } 205 assertTrue(boolean condition)206 public static void assertTrue(boolean condition) { 207 if (!condition) { 208 throw new AssertionError(); 209 } 210 } 211 createTestContext(Context targetContext, TaskSchedulerService service)212 private static Context createTestContext(Context targetContext, TaskSchedulerService service) { 213 TestContext context = mock(TestContext.class); 214 when(context.getService()).thenReturn(service); 215 when(context.startService(any())).thenCallRealMethod(); 216 when(context.getPackageName()).thenReturn(targetContext.getPackageName()); 217 return context; 218 } 219 220 public abstract class TestContext extends Context { 221 222 @Override startService(Intent service)223 public ComponentName startService(Intent service) { 224 getService().onStartCommand(service, 0, 0); 225 return null; 226 } 227 getService()228 public abstract TaskSchedulerService getService(); 229 } 230 } 231