[go: nahoru, domu]

blob: 1a4a39dc40aa19b9e9031e500b93fc42669de61b [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.health.services.client
import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.os.Looper
import android.os.RemoteException
import androidx.health.services.client.data.DataPointContainer
import androidx.health.services.client.data.DataType.Companion.CALORIES_DAILY
import androidx.health.services.client.data.DataType.Companion.CALORIES_TOTAL
import androidx.health.services.client.data.DataType.Companion.DISTANCE
import androidx.health.services.client.data.DataType.Companion.STEPS
import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
import androidx.health.services.client.data.HealthEvent
import androidx.health.services.client.data.PassiveGoal
import androidx.health.services.client.data.PassiveListenerConfig
import androidx.health.services.client.data.PassiveMonitoringCapabilities
import androidx.health.services.client.data.UserActivityInfo
import androidx.health.services.client.data.UserActivityState
import androidx.health.services.client.impl.IPassiveListenerCallback
import androidx.health.services.client.impl.IPassiveMonitoringApiService
import androidx.health.services.client.impl.ServiceBackedPassiveMonitoringClient
import androidx.health.services.client.impl.internal.IStatusCallback
import androidx.health.services.client.impl.ipc.internal.ConnectionManager
import androidx.health.services.client.impl.request.CapabilitiesRequest
import androidx.health.services.client.impl.request.FlushRequest
import androidx.health.services.client.impl.request.PassiveListenerCallbackRegistrationRequest
import androidx.health.services.client.impl.request.PassiveListenerServiceRegistrationRequest
import androidx.health.services.client.impl.response.PassiveMonitoringCapabilitiesResponse
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth
import java.util.concurrent.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows
@RunWith(RobolectricTestRunner::class)
class PassiveMonitoringClientTest {
private lateinit var client: PassiveMonitoringClient
private lateinit var service: FakeServiceStub
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
private fun TestScope.advanceMainLooperIdle() =
launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
private fun CoroutineScope.advanceMainLooperIdle() =
launch { Shadows.shadowOf(Looper.getMainLooper()).idle() }
@Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Application>()
client = ServiceBackedPassiveMonitoringClient(
context, ConnectionManager(context, context.mainLooper)
)
service = FakeServiceStub()
val packageName =
ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.servicePackageName
val action = ServiceBackedPassiveMonitoringClient.CLIENT_CONFIGURATION.bindAction
Shadows.shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
Intent().setPackage(packageName).setAction(action),
ComponentName(packageName, ServiceBackedPassiveMonitoringClient.CLIENT),
service
)
}
@After
fun tearDown() {
runBlocking {
launch { client.clearPassiveListenerCallback() }
advanceMainLooperIdle()
launch { client.clearPassiveListenerService() }
advanceMainLooperIdle()
}
}
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Test
fun registersPassiveListenerServiceSynchronously() = runTest {
launch {
val config = PassiveListenerConfig(
dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
shouldUserActivityInfoBeRequested = true,
dailyGoals = setOf(),
healthEventTypes = setOf()
)
client.setPassiveListenerService(FakeListenerService::class.java, config)
val request = service.registerServiceRequests[0]
Truth.assertThat(service.registerServiceRequests).hasSize(1)
Truth.assertThat(request.passiveListenerConfig.dataTypes).containsExactly(
STEPS_DAILY, CALORIES_DAILY
)
Truth.assertThat(request.passiveListenerConfig.shouldUserActivityInfoBeRequested)
.isTrue()
Truth.assertThat(request.packageName).isEqualTo("androidx.health.services.client.test")
}
advanceMainLooperIdle()
}
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Test
fun flushSynchronously() = runTest {
launch {
val config = PassiveListenerConfig(
dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
shouldUserActivityInfoBeRequested = true,
dailyGoals = setOf(),
healthEventTypes = setOf()
)
val callback = FakeCallback()
client.setPassiveListenerCallback(config, callback)
client.flush()
Truth.assertThat(service.registerFlushRequests).hasSize(1)
}
advanceMainLooperIdle()
}
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Test
fun getCapabilitiesSynchronously() = runTest {
launch {
val config = PassiveListenerConfig(
dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
shouldUserActivityInfoBeRequested = true,
dailyGoals = setOf(),
healthEventTypes = setOf()
)
val callback = FakeCallback()
client.setPassiveListenerCallback(config, callback)
val passiveMonitoringCapabilities = client.getCapabilities()
Truth.assertThat(service.registerGetCapabilitiesRequests).hasSize(1)
Truth.assertThat(passiveMonitoringCapabilities).isNotNull()
Truth.assertThat(service.getTestCapabilities().toString())
.isEqualTo(passiveMonitoringCapabilities.toString())
}
advanceMainLooperIdle()
}
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Test
fun getCapabilitiesSynchronously_cancelled() = runTest {
val config = PassiveListenerConfig(
dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
shouldUserActivityInfoBeRequested = true,
dailyGoals = setOf(),
healthEventTypes = setOf()
)
val callback = FakeCallback()
client.setPassiveListenerCallback(config, callback)
var isCancellationException = false
val deferred = async {
client.getCapabilities()
}
val cancellationDeferred = async {
deferred.cancel(CancellationException())
}
try {
deferred.await()
} catch (e: CancellationException) {
isCancellationException = true
}
cancellationDeferred.await()
Truth.assertThat(isCancellationException).isTrue()
}
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Test
fun getCapabilitiesSynchronously_Exception() = runTest {
val config = PassiveListenerConfig(
dataTypes = setOf(STEPS_DAILY, CALORIES_DAILY),
shouldUserActivityInfoBeRequested = true,
dailyGoals = setOf(),
healthEventTypes = setOf()
)
val callback = FakeCallback()
client.setPassiveListenerCallback(config, callback)
var isExceptionCaught = false
val deferred = async {
service.setException()
try {
client.getCapabilities()
} catch (e: RemoteException) {
isExceptionCaught = true
}
}
advanceMainLooperIdle()
deferred.await()
Truth.assertThat(isExceptionCaught).isTrue()
}
class FakeListenerService : PassiveListenerService()
internal class FakeCallback : PassiveListenerCallback {
private var onRegisteredCalls = 0
private val onRegistrationFailedThrowables = mutableListOf<Throwable>()
private val dataPointsReceived = mutableListOf<DataPointContainer>()
private val userActivityInfosReceived = mutableListOf<UserActivityInfo>()
private val completedGoals = mutableListOf<PassiveGoal>()
private val healthEventsReceived = mutableListOf<HealthEvent>()
var onPermissionLostCalls = 0
override fun onRegistered() {
onRegisteredCalls++
}
override fun onRegistrationFailed(throwable: Throwable) {
onRegistrationFailedThrowables += throwable
}
override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
dataPointsReceived += dataPoints
}
override fun onUserActivityInfoReceived(info: UserActivityInfo) {
userActivityInfosReceived += info
}
override fun onGoalCompleted(goal: PassiveGoal) {
completedGoals += goal
}
override fun onHealthEventReceived(event: HealthEvent) {
healthEventsReceived += event
}
override fun onPermissionLost() {
onPermissionLostCalls++
}
}
internal class FakeServiceStub : IPassiveMonitoringApiService.Stub() {
@JvmField
var apiVersion = 42
private var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
val registerServiceRequests = mutableListOf<PassiveListenerServiceRegistrationRequest>()
private val registerCallbackRequests =
mutableListOf<PassiveListenerCallbackRegistrationRequest>()
val registerFlushRequests = mutableListOf<FlushRequest>()
val registerGetCapabilitiesRequests = mutableListOf<CapabilitiesRequest>()
private val registeredCallbacks = mutableListOf<IPassiveListenerCallback>()
private val unregisterServicePackageNames = mutableListOf<String>()
private val unregisterCallbackPackageNames = mutableListOf<String>()
private var throwExcepotion = false
override fun getApiVersion() = 42
override fun getCapabilities(
request: CapabilitiesRequest
): PassiveMonitoringCapabilitiesResponse {
if (throwExcepotion) {
throw RemoteException("Remote Exception")
}
registerGetCapabilitiesRequests.add(request)
val capabilities = getTestCapabilities()
return PassiveMonitoringCapabilitiesResponse(capabilities)
}
override fun flush(request: FlushRequest, statusCallback: IStatusCallback?) {
registerFlushRequests.add(request)
statusCallbackAction.invoke(statusCallback)
}
override fun registerPassiveListenerService(
request: PassiveListenerServiceRegistrationRequest,
statusCallback: IStatusCallback
) {
registerServiceRequests += request
statusCallbackAction.invoke(statusCallback)
}
override fun registerPassiveListenerCallback(
request: PassiveListenerCallbackRegistrationRequest,
callback: IPassiveListenerCallback,
statusCallback: IStatusCallback
) {
registerCallbackRequests += request
registeredCallbacks += callback
statusCallbackAction.invoke(statusCallback)
}
override fun unregisterPassiveListenerService(
packageName: String,
statusCallback: IStatusCallback
) {
unregisterServicePackageNames += packageName
statusCallbackAction.invoke(statusCallback)
}
override fun unregisterPassiveListenerCallback(
packageName: String,
statusCallback: IStatusCallback
) {
unregisterCallbackPackageNames += packageName
statusCallbackAction.invoke(statusCallback)
}
fun getTestCapabilities(): PassiveMonitoringCapabilities {
return PassiveMonitoringCapabilities(
supportedDataTypesPassiveMonitoring = setOf(STEPS, DISTANCE),
supportedDataTypesPassiveGoals = setOf(CALORIES_TOTAL),
supportedHealthEventTypes = setOf(HealthEvent.Type.FALL_DETECTED),
supportedUserActivityStates = setOf(UserActivityState.USER_ACTIVITY_PASSIVE)
)
}
fun setException() {
throwExcepotion = true
}
}
}