Add AbstractKeepAliveNetworkHandlerSelectorTest

This commit is contained in:
Him188 2021-04-16 12:27:06 +08:00
parent 382c351e08
commit 90f5e27b5f
6 changed files with 149 additions and 17 deletions

View File

@ -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

View File

@ -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<H : NetworkHandler> {
internal abstract class AbstractKeepAliveNetworkHandlerSelector<H : NetworkHandler> : NetworkHandlerSelector<H> {
private val current = atomic<H?>(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<H : NetworkHandl
val current = getResumedInstance()
return if (current != null) {
when (current.state) {
NetworkHandler.State.OK -> 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 {

View File

@ -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()

View File

@ -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")

View File

@ -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<NetworkHandler>() {
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())
}
}

View File

@ -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<Unit>()
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<OutgoingPacket>()
override suspend fun sendPacketImpl(packet: OutgoingPacket) {
sendPacket.add(packet)
}
}