Fix bizzare NPE in context generation for DeviceClient.

Add test for remote client
This commit is contained in:
Alexander Nozik 2024-02-15 21:04:59 +03:00
parent b1121d61cb
commit 8bd9bcc6a6
3 changed files with 91 additions and 3 deletions

View File

@ -12,6 +12,7 @@ description = """
kscience { kscience {
jvm() jvm()
js() js()
useCoroutines("1.8.0")
useSerialization { useSerialization {
json() json()
} }

View File

@ -1,8 +1,8 @@
package space.kscience.controls.client package space.kscience.controls.client
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.newCoroutineContext
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import space.kscience.controls.api.* import space.kscience.controls.api.*
@ -28,8 +28,8 @@ public class DeviceClient(
private val send: suspend (DeviceMessage) -> Unit, private val send: suspend (DeviceMessage) -> Unit,
) : CachingDevice { ) : CachingDevice {
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
override val coroutineContext: CoroutineContext = newCoroutineContext(context.coroutineContext) override val coroutineContext: CoroutineContext = context.coroutineContext + Job(context.coroutineContext[Job])
private val mutex = Mutex() private val mutex = Mutex()

View File

@ -0,0 +1,87 @@
package space.kscience.controls.client
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import space.kscience.controls.api.Device
import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.manager.install
import space.kscience.controls.manager.respondMessage
import space.kscience.controls.spec.*
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Factory
import space.kscience.dataforge.context.request
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.names.Name
import space.kscience.magix.api.MagixEndpoint
import space.kscience.magix.api.MagixMessage
import space.kscience.magix.api.MagixMessageFilter
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertContains
import kotlin.time.Duration.Companion.milliseconds
public suspend fun <T> Device.readUnsafe(propertySpec: DevicePropertySpec<*, T>): T =
propertySpec.converter.metaToObject(readProperty(propertySpec.name)) ?: error("Property read result is not valid")
internal class RemoteDeviceConnect {
class TestDevice(context: Context, meta: Meta) : DeviceBySpec<TestDevice>(TestDevice, context, meta) {
private val rng = Random(meta["seed"].int ?: 0)
private val randomValue get() = rng.nextDouble()
companion object : DeviceSpec<TestDevice>(), Factory<TestDevice> {
override fun build(context: Context, meta: Meta): TestDevice = TestDevice(context, meta)
val value by doubleProperty { randomValue }
override suspend fun TestDevice.onOpen() {
doRecurring((meta["delay"].int ?: 10).milliseconds) {
read(value)
}
}
}
}
@Test
fun wrapper() = runTest {
val context = Context {
plugin(DeviceManager)
}
val device = context.request(DeviceManager).install("test", TestDevice)
val virtualMagixEndpoint = object : MagixEndpoint {
override fun subscribe(filter: MagixMessageFilter): Flow<MagixMessage> = device.messageFlow.map {
MagixMessage(
format = DeviceManager.magixFormat.defaultFormat,
payload = MagixEndpoint.magixJson.encodeToJsonElement(DeviceManager.magixFormat.serializer, it),
sourceEndpoint = "test",
)
}
override suspend fun broadcast(message: MagixMessage) {
device.respondMessage(
Name.EMPTY,
Json.decodeFromJsonElement(DeviceManager.magixFormat.serializer, message.payload)
)
}
override fun close() {
//
}
}
val remoteDevice = virtualMagixEndpoint.remoteDevice(context, "test", Name.EMPTY)
assertContains(0.0..1.0, remoteDevice.readUnsafe(TestDevice.value))
}
}