Add controlled host fixtures for Android network listeners

This commit is contained in:
evgeny committed 2026-10-27 15:04:27 +01:00
1 parent 799a71d361
commit 8a22afc42d
3 files changed
+62 -5

No files matched your search

@@ -1,14 +1,21 @@
package android.content
import android.media.AudioManager
import android.net.ConnectivityManager
/** Только host-тест: один управляемый AudioManager, без устройств/разрешений ОС. */
/** Только host-тест: управляемые AudioManager/ConnectivityManager, без устройств/разрешений ОС. */
class Context {
companion object { const val AUDIO_SERVICE = "audio" }
var permissionGranted = true
var connectivityManager: ConnectivityManager? = ConnectivityManager()
val applicationContext: Context get() = this
fun getSystemService(name: String): Any {
check(name == AUDIO_SERVICE)
return AudioManager.shared
}
fun <T> getSystemService(type: Class<T>): T? {
check(type == ConnectivityManager::class.java)
return type.cast(connectivityManager)
}
fun checkSelfPermission(name: String): Int = if (permissionGranted) 0 else -1
}
@@ -0,0 +1,35 @@
package android.net
/** Только host-тест: listener принадлежит конкретному manager; старые callbacks тест может доставить после удаления. */
class ConnectivityManager {
fun interface OnNetworkActiveListener { fun onNetworkActive() }
val listeners = mutableListOf<OnNetworkActiveListener>()
val history = mutableListOf<OnNetworkActiveListener>()
var addAttempts = 0
var removeAttempts = 0
var failAdd = false
var failRemove = false
var failQuery = false
var active = false
fun addDefaultNetworkActiveListener(listener: OnNetworkActiveListener) {
addAttempts++
if (failAdd) throw SecurityException("network listener registration denied")
check(listener !in listeners)
listeners.add(listener)
history.add(listener)
}
fun removeDefaultNetworkActiveListener(listener: OnNetworkActiveListener) {
removeAttempts++
if (failRemove) throw IllegalStateException("network listener removal failed")
check(listeners.remove(listener)) { "listener belongs to another manager or was already removed" }
}
val isDefaultNetworkActive: Boolean get() {
if (failQuery) throw IllegalStateException("network activity query failed")
return active
}
fun deliver(index: Int) { history[index].onNetworkActive() }
}
@@ -1,7 +1,22 @@
package android.os
/** Host-модель API36; callbacks доставляет тест явно, все времена монотонные. */
object Build { object VERSION { const val SDK_INT = 36 }; object VERSION_CODES { const val S = 31 } }
class Looper { companion object { fun getMainLooper() = Looper() } }
class Handler(val looper: Looper)
import java.util.concurrent.ConcurrentLinkedQueue
/** Host-модель SDK/главной очереди: callbacks и их доставку тест контролирует явно, без настоящего Android Looper. */
object Build { object VERSION { var SDK_INT = 36 }; object VERSION_CODES { const val P = 28; const val S = 31 } }
class Looper private constructor() {
private val tasks = ConcurrentLinkedQueue<Runnable>()
internal fun post(task: Runnable): Boolean { tasks.add(task); return true }
fun dispatchAll() {
check(myLooper() === this)
while (true) (tasks.poll() ?: return).run()
}
companion object {
private val main = Looper()
private val owner = Thread.currentThread()
fun getMainLooper() = main
fun myLooper(): Looper? = if (Thread.currentThread() === owner) main else null
}
}
class Handler(val looper: Looper) { fun post(task: Runnable): Boolean = looper.post(task) }
object SystemClock { fun elapsedRealtime(): Long = System.nanoTime() / 1_000_000 }