From 90f5e27b5fd828d69456ef55f79e5d0f46e59bcc Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 16 Apr 2021 12:27:06 +0800 Subject: [PATCH] Add AbstractKeepAliveNetworkHandlerSelectorTest --- .../kotlin/network/handler/NetworkHandler.kt | 3 +- .../network/handler/SelectorNetworkHandler.kt | 25 +++++--- .../kotlin/network/handler/ServerList.kt | 3 +- .../network/net/protocol/SsoController.kt | 15 ++--- ...ractKeepAliveNetworkHandlerSelectorTest.kt | 60 +++++++++++++++++++ .../kotlin/network/handler/testUtils.kt | 60 +++++++++++++++++++ 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 mirai-core/src/commonTest/kotlin/network/handler/AbstractKeepAliveNetworkHandlerSelectorTest.kt create mode 100644 mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt index 33f173ae9..aae54607e 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt @@ -27,7 +27,8 @@ import java.util.concurrent.CancellationException * Immutable context for [NetworkHandler] */ internal interface NetworkHandlerContext { - val bot: QQAndroidBot + val bot: QQAndroidBot // // TODO: 2021/4/16 this is bad, making it difficult to write unit tests. + // however migration requires a major change. val logger: MiraiLogger val ssoContext: SsoContext diff --git a/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt index e6e4dffff..890df70b6 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/SelectorNetworkHandler.kt @@ -10,7 +10,9 @@ package net.mamoe.mirai.internal.network.handler import kotlinx.atomicfu.atomic +import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import org.jetbrains.annotations.TestOnly /** * A proxy to [NetworkHandler] that delegates calls to instance returned by [NetworkHandlerSelector.awaitResumeInstance]. @@ -26,8 +28,8 @@ internal class SelectorNetworkHandler( ) : NetworkHandler { private suspend inline fun instance(): NetworkHandler = selector.awaitResumeInstance() - override val state: NetworkHandler.State - get() = selector.getResumedInstance()?.state ?: NetworkHandler.State.INITIALIZED + override val state: State + get() = selector.getResumedInstance()?.state ?: State.INITIALIZED override suspend fun resumeConnection() { instance() // the selector will resume connection for us. @@ -80,6 +82,11 @@ internal interface NetworkHandlerSelector { internal abstract class AbstractKeepAliveNetworkHandlerSelector : NetworkHandlerSelector { private val current = atomic(null) + @TestOnly + internal fun setCurrent(h: H) { + current.value = h + } + protected abstract fun createInstance(): H final override fun getResumedInstance(): H? = current.value @@ -89,14 +96,16 @@ internal abstract class AbstractKeepAliveNetworkHandlerSelector current - NetworkHandler.State.CLOSED -> { + State.CLOSED -> { this.current.compareAndSet(current, null) // invalidate the instance and try again. - awaitResumeInstance() + awaitResumeInstance() // will create new instance. } - else -> { - current.resumeConnection() // try to advance state. - awaitResumeInstance() + State.CONNECTING, + State.CONNECTION_LOST, + State.INITIALIZED, + State.OK -> { + current.resumeConnection() + return current } } } else { diff --git a/mirai-core/src/commonMain/kotlin/network/handler/ServerList.kt b/mirai-core/src/commonMain/kotlin/network/handler/ServerList.kt index 1cffc180b..b78757616 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/ServerList.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/ServerList.kt @@ -77,7 +77,8 @@ internal class ServerList( |157.255.13.77:14000, 120.232.18.27:443, |183.3.235.162:14000, 163.177.89.195:443, 183.232.94.44:80, |203.205.255.224:8080, 203.205.255.221:8080""".trimMargin() - .split(", ", "\n").filterNot(String::isBlank) + .splitToSequence(",").filterNot(String::isBlank) + .map { it.trim() } .map { val host = it.substringBefore(':') val port = it.substringAfter(':').toInt() diff --git a/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt index fba8c9758..23e8bdccf 100644 --- a/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt +++ b/mirai-core/src/commonMain/kotlin/network/net/protocol/SsoController.kt @@ -25,15 +25,15 @@ import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect import net.mamoe.mirai.internal.utils.crypto.TEA import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.network.* +import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol -import net.mamoe.mirai.utils.DeviceInfo -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.info -import net.mamoe.mirai.utils.withExceptionCollector import java.io.File internal interface SsoContext { var client: QQAndroidClient + val configuration: BotConfiguration + val loginSessionAware: LoginSessionAware get() = client + val accountSecrets: AccountSecrets get() = client } internal class SsoController( @@ -42,7 +42,7 @@ internal class SsoController( ) { @Throws(LoginFailedException::class) suspend fun login() = withExceptionCollector { - if (bot.client.wLoginSigInfoInitialized) { + if (ssoContext.accountSecrets.wLoginSigInfoInitialized) { kotlin.runCatching { fastLogin() }.onFailure { e -> @@ -181,6 +181,7 @@ internal class SsoController( } + @Suppress("unused") // false positive internal fun initClient() { val device = configuration.deviceInfo?.invoke(bot) ?: DeviceInfo.random() ssoContext.client = QQAndroidClient( @@ -188,7 +189,7 @@ internal class SsoController( device = device, accountSecrets = loadSecretsFromCacheOrCreate(device) ).apply { - _bot = bot + _bot = this@SsoController.bot } } @@ -201,7 +202,7 @@ internal class SsoController( // TODO: 2021/4/14 extract a cache service private val cacheDir: File by lazy { - configuration.workingDir.resolve(bot.configuration.cacheDir).apply { mkdirs() } + configuration.workingDir.resolve(ssoContext.configuration.cacheDir).apply { mkdirs() } } private val accountSecretsFile: File by lazy { cacheDir.resolve("account.secrets") diff --git a/mirai-core/src/commonTest/kotlin/network/handler/AbstractKeepAliveNetworkHandlerSelectorTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/AbstractKeepAliveNetworkHandlerSelectorTest.kt new file mode 100644 index 000000000..f288b0409 --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/network/handler/AbstractKeepAliveNetworkHandlerSelectorTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import net.mamoe.mirai.internal.network.handler.NetworkHandler.State +import net.mamoe.mirai.internal.test.runBlockingUnit +import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.* +import kotlin.time.seconds + +private class TestSelector(val createInstance0: () -> NetworkHandler) : + AbstractKeepAliveNetworkHandlerSelector() { + val createInstanceCount = AtomicInteger(0) + override fun createInstance(): NetworkHandler { + createInstanceCount.incrementAndGet() + return this.createInstance0() + } +} + +internal class AbstractKeepAliveNetworkHandlerSelectorTest { + + private fun createHandler() = TestNetworkHandler(TestNetworkHandlerContext()) + + @Test + fun `can initialize instance`() { + val selector = TestSelector { createHandler() } + runBlockingUnit(timeout = 3.seconds) { selector.awaitResumeInstance() } + assertNotNull(selector.getResumedInstance()) + } + + @Test + fun `no redundant initialization`() { + val selector = TestSelector { + fail("initialize called") + } + val handler = createHandler() + selector.setCurrent(handler) + assertSame(handler, selector.getResumedInstance()) + } + + @Test + fun `initialize another when closed`() { + val selector = TestSelector { + createHandler() + } + val handler = createHandler() + selector.setCurrent(handler) + assertSame(handler, selector.getResumedInstance()) + handler.setState(State.CLOSED) + runBlockingUnit(timeout = 3.seconds) { selector.awaitResumeInstance() } + assertEquals(1, selector.createInstanceCount.get()) + } +} \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt b/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt new file mode 100644 index 000000000..970574074 --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/network/handler/testUtils.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import kotlinx.coroutines.CompletableDeferred +import net.mamoe.mirai.internal.MockBot +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.network.handler.impl.NetworkHandlerSupport +import net.mamoe.mirai.internal.network.net.protocol.SsoContext +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.MiraiLogger +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.atomic.AtomicInteger + + +internal class TestNetworkHandlerContext( + override val bot: QQAndroidBot = MockBot(), + override val logger: MiraiLogger = MiraiLogger.create("Test"), + override val ssoContext: SsoContext = bot, + override val configuration: BotConfiguration = bot.configuration +) : NetworkHandlerContext + +internal open class TestNetworkHandler( + context: NetworkHandlerContext, +) : NetworkHandlerSupport(context) { + @Suppress("EXPOSED_SUPER_CLASS") + internal open inner class TestState( + correspondingState: NetworkHandler.State + ) : BaseStateImpl(correspondingState) { + val resumeDeferred = CompletableDeferred() + val resumeCount = AtomicInteger(0) + val onResume get() = resumeDeferred.onJoin + + override suspend fun resumeConnection() { + resumeCount.incrementAndGet() + resumeDeferred.complete(Unit) + } + } + + fun setState(correspondingState: NetworkHandler.State) { + setState { TestState(correspondingState) } + } + + private val initialState = TestState(NetworkHandler.State.INITIALIZED) + override fun initialState(): BaseStateImpl = initialState + + val sendPacket get() = ConcurrentLinkedQueue() + + override suspend fun sendPacketImpl(packet: OutgoingPacket) { + sendPacket.add(packet) + } +}