fix: snapshot infrared fake calls

This commit is contained in:
2026-08-24 02:44:05 +08:00
parent c21ae4e0d9
commit bdd3779899
2 changed files with 78 additions and 7 deletions
@@ -12,8 +12,8 @@ import com.flagship.abox.manager.domain.SocketPowerState
import com.flagship.abox.manager.domain.SocketStatus import com.flagship.abox.manager.domain.SocketStatus
import com.flagship.abox.manager.domain.TemperatureHumidityGateway import com.flagship.abox.manager.domain.TemperatureHumidityGateway
import com.flagship.abox.manager.domain.TemperatureHumidityReading import com.flagship.abox.manager.domain.TemperatureHumidityReading
import java.util.Collections
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
class FakeSocketGateway : SocketGateway { class FakeSocketGateway : SocketGateway {
private val states = ConcurrentHashMap<DeviceKey, SocketPowerState>() private val states = ConcurrentHashMap<DeviceKey, SocketPowerState>()
@@ -78,10 +78,9 @@ class FakeTemperatureHumidityGateway : TemperatureHumidityGateway {
} }
class FakeInfraredGateway : InfraredGateway { class FakeInfraredGateway : InfraredGateway {
val sentKeys: MutableList<InfraredCall> = Collections.synchronizedList(mutableListOf()) val sentKeys = CopyOnWriteArrayList<InfraredCall>()
val learnedKeys: MutableList<InfraredCall> = Collections.synchronizedList(mutableListOf()) val learnedKeys = CopyOnWriteArrayList<InfraredCall>()
val downloadCalls: MutableList<InfraredDownloadCall> = val downloadCalls = CopyOnWriteArrayList<InfraredDownloadCall>()
Collections.synchronizedList(mutableListOf())
private val sendResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>() private val sendResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>()
private val learnResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>() private val learnResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>()
@@ -134,9 +133,12 @@ class FakeInfraredGateway : InfraredGateway {
profileId: ProviderProfileId, profileId: ProviderProfileId,
codes: List<InfraredCode>, codes: List<InfraredCode>,
): DomainResult<List<InfraredCodeDownloadResult>> { ): DomainResult<List<InfraredCodeDownloadResult>> {
val codesSnapshot = codes.toList()
downloadError?.let { return DomainResult.Failure(it) } downloadError?.let { return DomainResult.Failure(it) }
downloadCalls += InfraredDownloadCall(profileId, codes) downloadCalls += InfraredDownloadCall(profileId, codesSnapshot)
return DomainResult.Success(codes.map { InfraredCodeDownloadResult(it, successful = true) }) return DomainResult.Success(
codesSnapshot.map { InfraredCodeDownloadResult(it, successful = true) },
)
} }
} }
@@ -4,8 +4,14 @@ import com.flagship.abox.manager.domain.DeviceName
import com.flagship.abox.manager.domain.DomainError import com.flagship.abox.manager.domain.DomainError
import com.flagship.abox.manager.domain.DomainResult import com.flagship.abox.manager.domain.DomainResult
import com.flagship.abox.manager.domain.InfraredCode import com.flagship.abox.manager.domain.InfraredCode
import com.flagship.abox.manager.domain.InfraredCodeDownloadResult
import com.flagship.abox.manager.domain.ProviderProfileId import com.flagship.abox.manager.domain.ProviderProfileId
import com.flagship.abox.manager.domain.SocketPowerState import com.flagship.abox.manager.domain.SocketPowerState
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.CountDownLatch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@@ -55,6 +61,69 @@ class FakeDeviceGatewaysTest {
assertTrue(download is DomainResult.Success) assertTrue(download is DomainResult.Success)
} }
@Test
fun infraredDownloadRecordsAndReturnsAnInputSnapshot() = runTest {
val gateway = FakeInfraredGateway()
val code = InfraredCode(DeviceName("ir-1"), "power", "code-value")
val mutableCodes = mutableListOf(code)
val result = gateway.downloadCodes(profileOne, mutableCodes)
mutableCodes.clear()
assertEquals(
InfraredDownloadCall(profileOne, listOf(code)),
gateway.downloadCalls.single(),
)
assertEquals(
DomainResult.Success(listOf(InfraredCodeDownloadResult(code, successful = true))),
result,
)
}
@Test
fun infraredCallLogCanBeIteratedSafelyAroundConcurrentWrites() = runTest {
val gateway = FakeInfraredGateway()
val deviceName = DeviceName("ir-1")
gateway.sendKey(profileOne, deviceName, "initial")
val staleIterators = List(10) { gateway.sentKeys.iterator() }
val start = CountDownLatch(1)
val failures = ConcurrentLinkedQueue<Throwable>()
val writerCount = 4
val callsPerWriter = 100
val writers = List(writerCount) { writerIndex ->
launch(Dispatchers.Default) {
start.await()
repeat(callsPerWriter) { callIndex ->
gateway.sendKey(profileOne, deviceName, "$writerIndex-$callIndex")
}
}
}
val reader = launch(Dispatchers.Default) {
start.await()
repeat(100) {
try {
gateway.sentKeys.forEach { call -> call.key.length }
} catch (failure: Throwable) {
failures += failure
}
}
}
start.countDown()
(writers + reader).joinAll()
staleIterators.forEach { iterator ->
try {
iterator.forEachRemaining { call -> call.key.length }
} catch (failure: Throwable) {
failures += failure
}
}
assertTrue(failures.toList().joinToString(), failures.isEmpty())
assertEquals(1 + writerCount * callsPerWriter, gateway.sentKeys.size)
}
@Test @Test
fun infraredLearnResultsCanDifferByProfileDeviceAndKey() = runTest { fun infraredLearnResultsCanDifferByProfileDeviceAndKey() = runTest {
val gateway = FakeInfraredGateway() val gateway = FakeInfraredGateway()