From f131f20e424bafee1ea20021400a631c39d93392 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Jun 2026 17:47:57 +0000 Subject: [PATCH] RSDK-11037 Add geometries to sensor component The sensor component was the only component with a GetGeometries RPC in the API that did not expose getGeometries through its RPC client and RPC service. Implement the client and server handlers following the existing pattern (e.g. movement sensor) and add unit tests. --- .../component/sensor/SensorRPCClient.java | 10 +++ .../component/sensor/SensorRPCService.java | 11 +++ .../component/sensor/SensorRPCClientTest.kt | 68 ++++++++++++++++ .../component/sensor/SensorRPCServiceTest.kt | 79 +++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCClientTest.kt create mode 100644 core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCServiceTest.kt diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCClient.java b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCClient.java index e48701c5b..a656b82fb 100644 --- a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCClient.java +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCClient.java @@ -3,12 +3,14 @@ import com.google.protobuf.Struct; import com.google.protobuf.Value; import com.viam.common.v1.Common; +import com.viam.common.v1.Common.GetGeometriesRequest; import com.viam.common.v1.Common.GetReadingsRequest; import com.viam.common.v1.Common.GetReadingsResponse; import com.viam.component.sensor.v1.SensorServiceGrpc; import com.viam.sdk.core.rpc.Channel; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,4 +42,12 @@ public GetReadingsResponse getReadings(final Optional extra) { extra.ifPresent(builder::setExtra); return client.getReadings(builder.build()); } + + @Override + public List getGeometries(final Optional extra) { + final GetGeometriesRequest.Builder builder = Common.GetGeometriesRequest.newBuilder(). + setName(getName().getName()); + extra.ifPresent(builder::setExtra); + return client.getGeometries(builder.build()).getGeometriesList(); + } } diff --git a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCService.java b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCService.java index d81052f3b..9ec26ac14 100644 --- a/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCService.java +++ b/core/sdk/src/main/java/com/viam/sdk/core/component/sensor/SensorRPCService.java @@ -65,6 +65,17 @@ public void getReadings(GetReadingsRequest request, responseObserver.onCompleted(); } + @Override + public void getGeometries(Common.GetGeometriesRequest request, + StreamObserver responseObserver) { + final com.viam.sdk.core.component.sensor.Sensor sensor = getResource( + com.viam.sdk.core.component.sensor.Sensor.named(request.getName()) + ); + final List result = sensor.getGeometries(Optional.of(request.getExtra())); + responseObserver.onNext(GetGeometriesResponse.newBuilder().addAllGeometries(result).build()); + responseObserver.onCompleted(); + } + @Override public Class getResourceClass() { return Sensor.class; diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCClientTest.kt new file mode 100644 index 000000000..ea57334ea --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCClientTest.kt @@ -0,0 +1,68 @@ +package com.viam.sdk.core.component.sensor + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common.Geometry +import com.viam.common.v1.Common.GetReadingsResponse +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.rpc.BasicManagedChannel +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class SensorRPCClientTest { + private lateinit var sensor: Sensor + private lateinit var client: SensorRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + sensor = mock( + Sensor::class.java, withSettings().useConstructor("mock-sensor").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(sensor)) + val service = SensorRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + val channel = grpcCleanupRule.register(InProcessChannelBuilder.forName(serviceName).directExecutor().build()) + client = SensorRPCClient("mock-sensor", BasicManagedChannel(channel)) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(sensor).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(sensor).getGeometries(any()) + } + + @Test + fun getReadings() { + val readings = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(GetReadingsResponse.newBuilder().putAllReadings(readings).build()).`when`(sensor).getReadings(any()) + val response = client.getReadings(Optional.empty()) + verify(sensor).getReadings(any()) + assertEquals(readings, response.readingsMap) + } + + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(sensor).doCommand(anyMap()) + val response = client.doCommand(command) + verify(sensor).doCommand(command) + assertEquals(command, response.fieldsMap) + } +} diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCServiceTest.kt new file mode 100644 index 000000000..ac34965a1 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/sensor/SensorRPCServiceTest.kt @@ -0,0 +1,79 @@ +package com.viam.sdk.core.component.sensor + +import com.google.protobuf.Struct +import com.google.protobuf.Value +import com.viam.common.v1.Common +import com.viam.common.v1.Common.Geometry +import com.viam.common.v1.Common.GetReadingsRequest +import com.viam.common.v1.Common.GetReadingsResponse +import com.viam.component.sensor.v1.SensorServiceGrpc +import com.viam.component.sensor.v1.SensorServiceGrpc.SensorServiceBlockingStub +import com.viam.sdk.core.resource.ResourceManager +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.grpc.testing.GrpcCleanupRule +import org.junit.Rule +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class SensorRPCServiceTest { + private lateinit var sensor: Sensor + private lateinit var client: SensorServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + sensor = mock( + Sensor::class.java, withSettings().useConstructor("mock-sensor").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(sensor)) + val service = SensorRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = SensorServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(sensor).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(sensor.name.name).build() + client.getGeometries(request) + verify(sensor).getGeometries(Optional.of(Struct.getDefaultInstance())) + } + + @Test + fun getReadings() { + val readings = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(GetReadingsResponse.newBuilder().putAllReadings(readings).build()).`when`(sensor).getReadings(any()) + val request = GetReadingsRequest.newBuilder().setName(sensor.name.name).build() + val response = client.getReadings(request) + verify(sensor).getReadings(Optional.of(Struct.getDefaultInstance())) + assertEquals(readings, response.readingsMap) + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(sensor).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(sensor.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(sensor).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } +}