1 /* 2 * Copyright (C) 2023 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.server.healthconnect; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.when; 22 23 import android.app.ActivityManager; 24 import android.content.Context; 25 import android.os.Process; 26 27 import androidx.test.ext.junit.runners.AndroidJUnit4; 28 import androidx.test.platform.app.InstrumentationRegistry; 29 30 import com.android.server.healthconnect.testing.TestUtils; 31 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.mockito.Mock; 36 import org.mockito.MockitoAnnotations; 37 38 import java.util.List; 39 import java.util.concurrent.Future; 40 import java.util.concurrent.ThreadPoolExecutor; 41 42 @RunWith(AndroidJUnit4.class) 43 public class HealthConnectThreadSchedulerTest { 44 private ThreadPoolExecutor mInternalTaskScheduler; 45 private ThreadPoolExecutor mControllerTaskScheduler; 46 private ThreadPoolExecutor mForegroundTaskScheduler; 47 private ThreadPoolExecutor mBackgroundTaskScheduler; 48 private long mInternalTaskSchedulerCompletedJobs; 49 private long mControllerTaskSchedulerCompletedJobs; 50 private long mForegroundTaskSchedulerCompletedJobs; 51 private long mBackgroundTaskSchedulerCompletedJobs; 52 private Context mContext; 53 private HealthConnectThreadScheduler mHealthConnectThreadScheduler; 54 55 @Mock private Context mMockContext; 56 @Mock private ActivityManager mActivityManager; 57 58 @Before setUp()59 public void setUp() { 60 mHealthConnectThreadScheduler = new HealthConnectThreadScheduler(); 61 MockitoAnnotations.initMocks(this); 62 63 mHealthConnectThreadScheduler.resetThreadPools(); 64 65 mInternalTaskScheduler = mHealthConnectThreadScheduler.mInternalBackgroundExecutor; 66 mInternalTaskSchedulerCompletedJobs = mInternalTaskScheduler.getCompletedTaskCount(); 67 mControllerTaskScheduler = mHealthConnectThreadScheduler.mControllerExecutor; 68 mControllerTaskSchedulerCompletedJobs = mControllerTaskScheduler.getCompletedTaskCount(); 69 mForegroundTaskScheduler = mHealthConnectThreadScheduler.mForegroundExecutor; 70 mForegroundTaskSchedulerCompletedJobs = mForegroundTaskScheduler.getCompletedTaskCount(); 71 mBackgroundTaskScheduler = mHealthConnectThreadScheduler.mBackgroundThreadExecutor; 72 mBackgroundTaskSchedulerCompletedJobs = mBackgroundTaskScheduler.getCompletedTaskCount(); 73 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 74 } 75 76 @Test testSchedulerScheduleInternal()77 public void testSchedulerScheduleInternal() throws Exception { 78 mHealthConnectThreadScheduler.scheduleInternalTask(() -> {}); 79 TestUtils.waitForTaskToFinishSuccessfully( 80 () -> { 81 if (mInternalTaskScheduler.getCompletedTaskCount() 82 != mInternalTaskSchedulerCompletedJobs + 1) { 83 throw new RuntimeException(); 84 } 85 }); 86 } 87 88 @Test testScheduleControllerTask()89 public void testScheduleControllerTask() throws Exception { 90 mHealthConnectThreadScheduler.scheduleControllerTask(() -> {}); 91 TestUtils.waitForTaskToFinishSuccessfully( 92 () -> { 93 if (mControllerTaskScheduler.getCompletedTaskCount() 94 != mControllerTaskSchedulerCompletedJobs + 1) { 95 throw new RuntimeException(); 96 } 97 }); 98 } 99 100 @Test testScheduleBackgroundTask()101 public void testScheduleBackgroundTask() throws Exception { 102 mockCurrentProcessImportance(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); 103 104 mHealthConnectThreadScheduler.schedule(mMockContext, () -> {}, Process.myUid(), false); 105 TestUtils.waitForTaskToFinishSuccessfully( 106 () -> { 107 if (mBackgroundTaskScheduler.getCompletedTaskCount() 108 != mBackgroundTaskSchedulerCompletedJobs + 1) { 109 throw new RuntimeException(); 110 } 111 }); 112 } 113 114 @Test testScheduleForegroundTask()115 public void testScheduleForegroundTask() throws Exception { 116 mockCurrentProcessImportance(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); 117 118 mHealthConnectThreadScheduler.schedule(mMockContext, () -> {}, Process.myUid(), false); 119 TestUtils.waitForTaskToFinishSuccessfully( 120 () -> { 121 if (mForegroundTaskScheduler.getCompletedTaskCount() 122 != mForegroundTaskSchedulerCompletedJobs + 1) { 123 throw new RuntimeException(); 124 } 125 }); 126 } 127 128 @Test testHealthConnectScheduler_runningAppProcessNull()129 public void testHealthConnectScheduler_runningAppProcessNull() throws Exception { 130 when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); 131 when(mActivityManager.getRunningAppProcesses()).thenReturn(null); 132 133 mHealthConnectThreadScheduler.scheduleInternalTask(() -> {}); 134 TestUtils.waitForTaskToFinishSuccessfully( 135 () -> { 136 if (mInternalTaskScheduler.getCompletedTaskCount() 137 != mInternalTaskSchedulerCompletedJobs + 1) { 138 throw new RuntimeException(); 139 } 140 }); 141 } 142 143 @Test testHealthConnectSchedulerClear()144 public void testHealthConnectSchedulerClear() { 145 assertThat(mInternalTaskSchedulerCompletedJobs).isEqualTo(0); 146 assertThat(mControllerTaskSchedulerCompletedJobs).isEqualTo(0); 147 assertThat(mForegroundTaskSchedulerCompletedJobs).isEqualTo(0); 148 assertThat(mBackgroundTaskSchedulerCompletedJobs).isEqualTo(0); 149 } 150 151 @Test testScheduleAfterTheSchedulersAreShutdown_expectNoException()152 public void testScheduleAfterTheSchedulersAreShutdown_expectNoException() { 153 mHealthConnectThreadScheduler.shutdownThreadPools(); 154 155 mHealthConnectThreadScheduler.schedule(mContext, () -> {}, Process.myUid(), false); 156 mHealthConnectThreadScheduler.schedule(mContext, () -> {}, Process.myUid(), true); 157 mHealthConnectThreadScheduler.scheduleInternalTask(() -> {}); 158 mHealthConnectThreadScheduler.scheduleControllerTask(() -> {}); 159 } 160 161 @Test testInternalSchedulerThreadName()162 public void testInternalSchedulerThreadName() throws Exception { 163 Future<String> name = mInternalTaskScheduler.submit(() -> Thread.currentThread().getName()); 164 assertThat(name.get()).isEqualTo("hc-int-bg-0"); 165 } 166 167 @Test testControllerSchedulerThreadName()168 public void testControllerSchedulerThreadName() throws Exception { 169 Future<String> name = 170 mControllerTaskScheduler.submit(() -> Thread.currentThread().getName()); 171 assertThat(name.get()).startsWith("hc-ctrl-"); 172 } 173 174 @Test testForegroundSchedulerThreadName()175 public void testForegroundSchedulerThreadName() throws Exception { 176 Future<String> name = 177 mForegroundTaskScheduler.submit(() -> Thread.currentThread().getName()); 178 assertThat(name.get()).startsWith("hc-fg-0"); 179 } 180 181 @Test testBackgroundSchedulerThreadName()182 public void testBackgroundSchedulerThreadName() throws Exception { 183 Future<String> name = 184 mBackgroundTaskScheduler.submit(() -> Thread.currentThread().getName()); 185 assertThat(name.get()).isEqualTo("hc-bg-0"); 186 } 187 mockCurrentProcessImportance(int importance)188 private void mockCurrentProcessImportance(int importance) { 189 when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); 190 ActivityManager.RunningAppProcessInfo runningAppProcessInfo = 191 new ActivityManager.RunningAppProcessInfo(); 192 runningAppProcessInfo.uid = Process.myUid(); 193 runningAppProcessInfo.importance = importance; 194 when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(runningAppProcessInfo)); 195 } 196 } 197