1 /* 2 * Copyright (C) 2024 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.settingslib.ipc 18 19 import android.app.Application 20 import android.app.Service 21 import android.content.ComponentName 22 import android.content.Intent 23 import android.os.Build 24 import android.os.Looper 25 import androidx.test.core.app.ApplicationProvider 26 import kotlinx.coroutines.Dispatchers 27 import kotlinx.coroutines.runBlocking 28 import kotlinx.coroutines.withContext 29 import org.junit.rules.TestWatcher 30 import org.junit.runner.Description 31 import org.robolectric.Robolectric 32 import org.robolectric.Shadows 33 import org.robolectric.android.controller.ServiceController 34 35 /** Rule for messenger service testing. */ 36 open class MessengerServiceRule<C : MessengerServiceClient>( 37 private val serviceClass: Class<out MessengerService>, 38 val client: C, 39 ) : TestWatcher() { 40 val application: Application = ApplicationProvider.getApplicationContext() 41 val isRobolectric = Build.FINGERPRINT.contains("robolectric") 42 43 private var serviceController: ServiceController<out Service>? = null 44 startingnull45 override fun starting(description: Description) { 46 if (isRobolectric) { 47 runBlocking { setupRobolectricService() } 48 } 49 } 50 finishednull51 override fun finished(description: Description) { 52 client.close() 53 if (isRobolectric) { 54 runBlocking { 55 withContext(Dispatchers.Main) { serviceController?.run { unbind().destroy() } } 56 } 57 } 58 } 59 setupRobolectricServicenull60 private suspend fun setupRobolectricService() { 61 if (Thread.currentThread() == Looper.getMainLooper().thread) { 62 throw IllegalStateException( 63 "To avoid deadlock, run test with @LooperMode(LooperMode.Mode.INSTRUMENTATION_TEST)" 64 ) 65 } 66 withContext(Dispatchers.Main) { 67 serviceController = Robolectric.buildService(serviceClass) 68 val service = serviceController!!.create().get() 69 Shadows.shadowOf(application).apply { 70 setComponentNameAndServiceForBindService( 71 ComponentName(application, serviceClass), 72 service.onBind(Intent(application, serviceClass)), 73 ) 74 setBindServiceCallsOnServiceConnectedDirectly(true) 75 setUnbindServiceCallsOnServiceDisconnected(false) 76 } 77 } 78 } 79 } 80