Я написал модульный тест, в котором используется провайдер фиктивного местоположения. Тест проходит, когда получено обновление местоположения, или не проходит, когда истекает время ожидания.
Тест успешно проходит на эмулированных пикселях 3, Android M и Android O. Время ожидания истекает на Android P и Q. Я также тестировал физический пиксель 3 с Q, но все равно не проходит.
Я уже некоторое время бьюсь головой об этом и не могу понять, что происходит.
Тест:
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.location.Location
import android.location.LocationManager
import androidx.test.rule.GrantPermissionRule
import design.inhale.androidtestutils.InstrumentedTest
import design.inhale.datasource.observer.NullDataSourceListener
import design.inhale.testutils.Latch
import design.inhale.utils.locationManager
import design.inhale.utils.mock
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.core.Is
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
class DeviceLocationSourceTest: InstrumentedTest() {
private val mockProviderName = "MockProvider"
private val locationManager: LocationManager
get() = appContext.locationManager
@get:Rule
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
@Before
fun init() {
addMockLocationProvider()
}
@After
fun deinit() {
instrumentation.waitForIdleSync()
removeMockLocationProvider()
}
@Test(timeout = 10_000)
fun receiveLocationUpdate() {
val latch = Latch()
val listener = object: LocationSourceListener {
override fun onDataUpdated(data: Location) {
with(data) {
assertThat(latitude, Is(equalTo(0.0)))
assertThat(longitude, Is(equalTo(0.0)))
}
latch.release()
}
}
mockLocationSource(listener).start()
instrumentation.waitForIdleSync() // in case we're hitting race conditions?
updateMockLocation(0.0, 0.0)
latch.await()
}
@Suppress("SameParameterValue")
private fun updateMockLocation(latitude: Double, longitude: Double) {
val location = Location(mockProviderName).mock(latitude, longitude)
locationManager.setTestProviderLocation(mockProviderName, location)
}
private fun mockLocationSource(listener: LocationSourceListener = NullDataSourceListener()) =
DeviceLocationSource(appContext, mockProviderName, listener)
private fun addMockLocationProvider() {
with(locationManager) {
try {
addTestProvider(
mockProviderName,
false,
false,
false,
false,
true,
true,
true,
0,
5)
} catch (e: IllegalArgumentException) {
// If this is caught, the mock provider already exists
}
setTestProviderEnabled(mockProviderName, true)
}
}
private fun removeMockLocationProvider() = locationManager.removeTestProvider(mockProviderName)
}
Манифест:
<manifest package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
androidTest / Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
</manifest>
Location.mock ():
@SuppressLint("ObsoleteSdkInt")
fun Location.mock(latitude: Double = 0.0, longitude: Double = 0.0, accuracy: Float = 0f): Location {
this.latitude = latitude
this.longitude = longitude
this.accuracy = accuracy
this.time = currentTimeMillis()
if (SDK_INT > JELLY_BEAN) this.elapsedRealtimeNanos = elapsedRealtimeNanos()
return this
}