Add property recoverable to HeartbeatFailedException, to control recoverability.

Fix `SelectorRecoveryTest`
This commit is contained in:
Him188 2021-07-02 00:38:21 +08:00
parent 8799d653e6
commit b90f86258c
3 changed files with 40 additions and 11 deletions

View File

@ -10,11 +10,15 @@
package net.mamoe.mirai.internal.network.impl.netty
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
import net.mamoe.mirai.utils.unwrap
import net.mamoe.mirai.utils.unwrapCancellationException
import java.io.IOException
internal class HeartbeatFailedException(
private val name: String, // kind of HB
override val cause: Throwable? = null,
) : NetworkException(true) {
override val cause: Throwable,
recoverable: Boolean = cause.unwrapCancellationException() is IOException || cause is NetworkException && cause.recoverable,
) : NetworkException(recoverable) {
override val message: String = "Exception in $name job"
override fun toString(): String = "HeartbeatFailedException: $name, cause=$cause"
override fun toString(): String = "HeartbeatFailedException: $name, recoverable=$recoverable, cause=$cause"
}

View File

@ -146,6 +146,13 @@ internal sealed class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abstr
assertEquals(state, network.state)
}
fun assertState(vararg accepted: State) {
val s = network.state
if (s !in accepted) {
throw AssertionError("Expected: ${accepted.joinToString()}, actual: $s")
}
}
fun NetworkHandler.assertState(state: State) {
assertEquals(state, this.state)
}

View File

@ -16,8 +16,10 @@ import net.mamoe.mirai.internal.network.components.HeartbeatFailureHandler
import net.mamoe.mirai.internal.network.components.HeartbeatScheduler
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTestWithSelector
import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException
import net.mamoe.mirai.internal.network.impl.netty.NettyChannelException
import net.mamoe.mirai.internal.test.runBlockingUnit
import org.junit.jupiter.api.Test
import java.io.IOException
import kotlin.test.assertFails
internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() {
@ -33,16 +35,36 @@ internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() {
* @see HeartbeatFailedException
*/
@Test
fun `can recover on heartbeat failure`() = runBlockingUnit {
testRecover { HeartbeatFailedException("test", null) } // NetworkException
fun `can recover on heartbeat failure with IOException`() = runBlockingUnit {
// We allow IOException to cause a reconnect.
testRecoverWhenHeartbeatFailWith { IOException("test IO ex") }
// BotOfflineMonitor immediately launches a recovery which is UNDISPATCHED, so connection is immediately recovered.
assertState(NetworkHandler.State.CONNECTING, NetworkHandler.State.OK)
}
/**
* Emulates system hibernation and network failure.
* @see HeartbeatFailedException
*/
@Test
fun `can recover on heartbeat failure with NettyChannelException`() = runBlockingUnit {
// We allow IOException to cause a reconnect.
testRecoverWhenHeartbeatFailWith { NettyChannelException("test IO ex") }
// BotOfflineMonitor immediately launches a recovery which is UNDISPATCHED, so connection is immediately recovered.
assertState(NetworkHandler.State.CONNECTING, NetworkHandler.State.OK)
}
@Test
fun `cannot recover on other failures`() = runBlockingUnit {
testRecover { IllegalStateException() }
// ISE is considered as an internal error (bug).
testRecoverWhenHeartbeatFailWith { IllegalStateException() }
assertState(NetworkHandler.State.CLOSED)
}
private suspend fun testRecover(exception: () -> Exception) {
private suspend fun testRecoverWhenHeartbeatFailWith(exception: () -> Exception) {
val heartbeatScheduler = object : HeartbeatScheduler {
lateinit var onHeartFailure: HeartbeatFailureHandler
override fun launchJobsIn(
@ -61,9 +83,5 @@ internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() {
assertState(NetworkHandler.State.OK)
heartbeatScheduler.onHeartFailure("Test", exception())
assertState(NetworkHandler.State.CLOSED)
bot.network.resumeConnection() // in real, this is called by BotOnlineWatchdog in SelectorNetworkHandler
assertState(NetworkHandler.State.OK)
}
}