[go: nahoru, domu]

blob: f42c82c796fcb2b410873c4fdf5308bfbe702386 [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.impl
import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.os.Looper
import androidx.health.services.client.MeasureCallback
import androidx.health.services.client.data.Availability
import androidx.health.services.client.data.DataPointContainer
import androidx.health.services.client.data.DataPoints
import androidx.health.services.client.data.DataType
import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
import androidx.health.services.client.data.DataTypeAvailability.Companion.AVAILABLE
import androidx.health.services.client.data.DeltaDataType
import androidx.health.services.client.data.HeartRateAccuracy
import androidx.health.services.client.data.HeartRateAccuracy.SensorStatus.Companion.ACCURACY_HIGH
import androidx.health.services.client.data.MeasureCapabilities
import androidx.health.services.client.impl.event.MeasureCallbackEvent
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.MeasureRegistrationRequest
import androidx.health.services.client.impl.request.MeasureUnregistrationRequest
import androidx.health.services.client.impl.response.AvailabilityResponse
import androidx.health.services.client.impl.response.DataPointsResponse
import androidx.health.services.client.impl.response.MeasureCapabilitiesResponse
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import java.time.Duration
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.shadowOf
@RunWith(RobolectricTestRunner::class)
class ServiceBackedMeasureClientTest {
private val callback = FakeCallback()
private lateinit var client: ServiceBackedMeasureClient
private lateinit var fakeService: FakeServiceStub
@Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Application>()
client =
ServiceBackedMeasureClient(context, ConnectionManager(context, context.mainLooper))
fakeService = FakeServiceStub()
val packageName = ServiceBackedMeasureClient.CLIENT_CONFIGURATION.servicePackageName
val action = ServiceBackedMeasureClient.CLIENT_CONFIGURATION.bindAction
shadowOf(context).setComponentNameAndServiceForBindServiceForIntent(
Intent().setPackage(packageName).setAction(action),
ComponentName(packageName, ServiceBackedMeasureClient.CLIENT),
fakeService
)
}
@After
fun tearDown() {
client.unregisterMeasureCallbackAsync(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
}
@Test
fun registerCallbackReachesService() {
client.registerMeasureCallback(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
assertThat(callback.onRegisteredInvocationCount).isEqualTo(1)
assertThat(fakeService.registerEvents).hasSize(1)
assertThat(fakeService.registerEvents[0].request.dataType).isEqualTo(HEART_RATE_BPM)
assertThat(fakeService.registerEvents[0].request.packageName)
.isEqualTo("androidx.health.services.client.test")
}
@Test
fun registerCallbackFailureReachesClient() {
fakeService.statusCallbackAction = { it.onFailure("Measure twice, cut once.") }
client.registerMeasureCallback(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
assertThat(callback.registrationFailureThrowables).hasSize(1)
assertThat(callback.registrationFailureThrowables[0].message)
.isEqualTo("Measure twice, cut once.")
}
@Test
fun unregisterCallbackReachesService() {
client.registerMeasureCallback(HEART_RATE_BPM, callback)
client.unregisterMeasureCallbackAsync(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
assertThat(fakeService.unregisterEvents).hasSize(1)
assertThat(fakeService.unregisterEvents[0].request.dataType).isEqualTo(HEART_RATE_BPM)
assertThat(fakeService.unregisterEvents[0].request.packageName)
.isEqualTo("androidx.health.services.client.test")
}
@Test
fun dataPointsReachAppCallback() {
val event = MeasureCallbackEvent.createDataPointsUpdateEvent(
DataPointsResponse(
listOf(
DataPoints.heartRate(
50.0,
Duration.ofSeconds(42),
HeartRateAccuracy(ACCURACY_HIGH)
)
)
)
)
client.registerMeasureCallback(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
fakeService.registerEvents[0].callback.onMeasureCallbackEvent(event)
shadowOf(Looper.getMainLooper()).idle()
assertThat(callback.dataReceivedEvents).hasSize(1)
val dataEvent = callback.dataReceivedEvents[0]
assertThat(dataEvent.data.getData(HEART_RATE_BPM)).hasSize(1)
val dataPoint = dataEvent.data.getData(HEART_RATE_BPM)[0]
assertThat(dataPoint.value).isEqualTo(50.0)
assertThat((dataPoint.accuracy as HeartRateAccuracy).sensorStatus).isEqualTo(ACCURACY_HIGH)
assertThat(dataPoint.timeDurationFromBoot.seconds).isEqualTo(42)
}
@Test
fun availabilityReachesAppCallback() {
val event = MeasureCallbackEvent.createAvailabilityUpdateEvent(
AvailabilityResponse(HEART_RATE_BPM, AVAILABLE)
)
client.registerMeasureCallback(HEART_RATE_BPM, callback)
shadowOf(Looper.getMainLooper()).idle()
fakeService.registerEvents[0].callback.onMeasureCallbackEvent(event)
shadowOf(Looper.getMainLooper()).idle()
assertThat(callback.availabilityChangeEvents).hasSize(1)
val availabilityEvent = callback.availabilityChangeEvents[0]
assertThat(availabilityEvent.dataType).isEqualTo(HEART_RATE_BPM)
assertThat(availabilityEvent.availability).isEqualTo(AVAILABLE)
}
@Test
fun capabilitiesReturnsCorrectValue() {
fakeService.supportedDataTypes = setOf(HEART_RATE_BPM)
val capabilitiesFuture = client.getCapabilitiesAsync()
shadowOf(Looper.getMainLooper()).idle()
val capabilities = capabilitiesFuture.get()
assertThat(capabilities.supportedDataTypesMeasure).hasSize(1)
assertThat(capabilities.supportedDataTypesMeasure).containsExactly(HEART_RATE_BPM)
}
class FakeCallback : MeasureCallback {
data class AvailabilityChangeEvent(
val dataType: DataType<*, *>,
val availability: Availability
)
data class DataReceivedEvent(val data: DataPointContainer)
val availabilityChangeEvents = mutableListOf<AvailabilityChangeEvent>()
val dataReceivedEvents = mutableListOf<DataReceivedEvent>()
var onRegisteredInvocationCount = 0
var registrationFailureThrowables = mutableListOf<Throwable>()
override fun onRegistered() {
onRegisteredInvocationCount++
}
override fun onRegistrationFailed(throwable: Throwable) {
registrationFailureThrowables += throwable
}
override fun onAvailabilityChanged(
dataType: DeltaDataType<*, *>,
availability: Availability
) {
availabilityChangeEvents += AvailabilityChangeEvent(dataType, availability)
}
override fun onDataReceived(data: DataPointContainer) {
dataReceivedEvents += DataReceivedEvent(data)
}
}
class FakeServiceStub : IMeasureApiService.Stub() {
class RegisterEvent(
val request: MeasureRegistrationRequest,
val callback: IMeasureCallback,
val statusCallback: IStatusCallback
)
class UnregisterEvent(
val request: MeasureUnregistrationRequest,
val callback: IMeasureCallback,
val statusCallback: IStatusCallback
)
var statusCallbackAction: (IStatusCallback) -> Unit = { it.onSuccess() }
var supportedDataTypes = setOf(HEART_RATE_BPM)
val registerEvents = mutableListOf<RegisterEvent>()
val unregisterEvents = mutableListOf<UnregisterEvent>()
override fun getApiVersion() = 42
override fun registerCallback(
request: MeasureRegistrationRequest,
callback: IMeasureCallback,
statusCallback: IStatusCallback
) {
registerEvents += RegisterEvent(request, callback, statusCallback)
statusCallbackAction.invoke(statusCallback)
}
override fun unregisterCallback(
request: MeasureUnregistrationRequest,
callback: IMeasureCallback,
statusCallback: IStatusCallback
) {
unregisterEvents += UnregisterEvent(request, callback, statusCallback)
statusCallbackAction.invoke(statusCallback)
}
override fun getCapabilities(request: CapabilitiesRequest): MeasureCapabilitiesResponse {
return MeasureCapabilitiesResponse(MeasureCapabilities(supportedDataTypes))
}
}
}