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