|
| 1 | +package io.ably.lib.objects |
| 2 | + |
| 3 | +import java.lang.reflect.Field |
| 4 | +import kotlinx.coroutines.Dispatchers |
| 5 | +import kotlinx.coroutines.delay |
| 6 | +import kotlinx.coroutines.suspendCancellableCoroutine |
| 7 | +import kotlinx.coroutines.withContext |
| 8 | +import kotlinx.coroutines.withTimeout |
| 9 | + |
| 10 | +suspend fun assertWaiter(timeoutInMs: Long = 10_000, block: suspend () -> Boolean) { |
| 11 | + withContext(Dispatchers.Default) { |
| 12 | + withTimeout(timeoutInMs) { |
| 13 | + do { |
| 14 | + val success = block() |
| 15 | + delay(100) |
| 16 | + } while (!success) |
| 17 | + } |
| 18 | + } |
| 19 | +} |
| 20 | + |
| 21 | +fun Any.setPrivateField(name: String, value: Any?) { |
| 22 | + val valueField = javaClass.findField(name) |
| 23 | + valueField.isAccessible = true |
| 24 | + valueField.set(this, value) |
| 25 | +} |
| 26 | + |
| 27 | +fun <T>Any.getPrivateField(name: String): T { |
| 28 | + val valueField = javaClass.findField(name) |
| 29 | + valueField.isAccessible = true |
| 30 | + @Suppress("UNCHECKED_CAST") |
| 31 | + return valueField.get(this) as T |
| 32 | +} |
| 33 | + |
| 34 | +private fun Class<*>.findField(name: String): Field { |
| 35 | + var result = kotlin.runCatching { getDeclaredField(name) } |
| 36 | + var currentClass = this |
| 37 | + while (result.isFailure && currentClass.superclass != null) // stop when we got field or reached top of class hierarchy |
| 38 | + { |
| 39 | + currentClass = currentClass.superclass!! |
| 40 | + result = kotlin.runCatching { currentClass.getDeclaredField(name) } |
| 41 | + } |
| 42 | + if (result.isFailure) { |
| 43 | + throw result.exceptionOrNull() as Exception |
| 44 | + } |
| 45 | + return result.getOrNull() as Field |
| 46 | +} |
| 47 | + |
| 48 | +suspend fun <T> Any.invokePrivateSuspendMethod(methodName: String, vararg args: Any?): T = suspendCancellableCoroutine { cont -> |
| 49 | + val suspendMethod = javaClass.declaredMethods.find { it.name == methodName } |
| 50 | + ?: error("Method '$methodName' not found") |
| 51 | + suspendMethod.isAccessible = true |
| 52 | + suspendMethod.invoke(this, *args, cont) |
| 53 | +} |
| 54 | + |
| 55 | +fun <T> Any.invokePrivateMethod(methodName: String, vararg args: Any?): T { |
| 56 | + val method = javaClass.declaredMethods.find { it.name == methodName } ?: error("Method '$methodName' not found") |
| 57 | + method.isAccessible = true |
| 58 | + @Suppress("UNCHECKED_CAST") |
| 59 | + return method.invoke(this, *args) as T |
| 60 | +} |
0 commit comments