diff --git a/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java b/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java index 2c853079..fe103fa1 100644 --- a/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java +++ b/core/sdk/src/main/java/com/viam/sdk/core/resource/ResourceManager.java @@ -2,6 +2,7 @@ import com.viam.common.v1.Common; import com.viam.common.v1.Common.ResourceName; +import com.viam.component.base.v1.BaseServiceGrpc; import com.viam.component.arm.v1.ArmServiceGrpc; import com.viam.component.board.v1.BoardServiceGrpc; import com.viam.component.camera.v1.CameraServiceGrpc; @@ -13,6 +14,7 @@ import com.viam.component.movementsensor.v1.MovementSensorServiceGrpc; import com.viam.component.powersensor.v1.PowerSensorServiceGrpc; import com.viam.component.sensor.v1.SensorServiceGrpc; +import com.viam.sdk.core.component.base.*; import com.viam.sdk.core.component.arm.*; import com.viam.component.servo.v1.ServoServiceGrpc; import com.viam.sdk.core.component.board.Board; @@ -69,6 +71,18 @@ public class ResourceManager implements Closeable { static { // register well-known subtypes // COMPONENTS + Registry.registerSubtype(new ResourceRegistration<>( + Arm.SUBTYPE, + ArmServiceGrpc.SERVICE_NAME, + ArmRPCService::new, + ArmRPCClient::new + )); + Registry.registerSubtype(new ResourceRegistration<>( + Base.SUBTYPE, + BaseServiceGrpc.SERVICE_NAME, + BaseRPCService::new, + BaseRPCClient::new + )); Registry.registerSubtype(new ResourceRegistration<>( Board.SUBTYPE, BoardServiceGrpc.SERVICE_NAME, @@ -81,6 +95,18 @@ public class ResourceManager implements Closeable { CameraRPCService::new, CameraRPCClient::new )); + Registry.registerSubtype(new ResourceRegistration<>( + Encoder.SUBTYPE, + EncoderServiceGrpc.SERVICE_NAME, + EncoderRPCService::new, + EncoderRPCClient::new + )); + Registry.registerSubtype(new ResourceRegistration<>( + Gantry.SUBTYPE, + GantryServiceGrpc.SERVICE_NAME, + GantryRPCService::new, + GantryRPCClient::new + )); Registry.registerSubtype(new ResourceRegistration<>( Generic.SUBTYPE, GenericServiceGrpc.SERVICE_NAME, @@ -105,18 +131,6 @@ public class ResourceManager implements Closeable { MovementSensorRPCService::new, MovementSensorRPCClient::new )); - Registry.registerSubtype(new ResourceRegistration<>( - Sensor.SUBTYPE, - SensorServiceGrpc.SERVICE_NAME, - SensorRPCService::new, - SensorRPCClient::new - )); - Registry.registerSubtype(new ResourceRegistration<>( - Encoder.SUBTYPE, - EncoderServiceGrpc.SERVICE_NAME, - EncoderRPCService::new, - EncoderRPCClient::new - )); Registry.registerSubtype(new ResourceRegistration<>( PowerSensor.SUBTYPE, PowerSensorServiceGrpc.SERVICE_NAME, @@ -124,10 +138,10 @@ public class ResourceManager implements Closeable { PowerSensorRPCClient::new )); Registry.registerSubtype(new ResourceRegistration<>( - Arm.SUBTYPE, - ArmServiceGrpc.SERVICE_NAME, - ArmRPCService::new, - ArmRPCClient::new + Sensor.SUBTYPE, + SensorServiceGrpc.SERVICE_NAME, + SensorRPCService::new, + SensorRPCClient::new )); Registry.registerSubtype(new ResourceRegistration<>( Servo.SUBTYPE, @@ -136,13 +150,6 @@ public class ResourceManager implements Closeable { ServoRPCClient::new )); - Registry.registerSubtype(new ResourceRegistration<>( - Gantry.SUBTYPE, - GantryServiceGrpc.SERVICE_NAME, - GantryRPCService::new, - GantryRPCClient::new - )); - // SERVICES Registry.registerSubtype(new ResourceRegistration<>( DataManager.SUBTYPE, diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/Base.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/Base.kt new file mode 100644 index 00000000..7fd98228 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/Base.kt @@ -0,0 +1,163 @@ +package com.viam.sdk.core.component.base + +import com.google.protobuf.Struct +import com.viam.common.v1.Common.ResourceName +import com.viam.common.v1.Common.Vector3 +import com.viam.component.base.v1.Base.GetPropertiesResponse +import com.viam.sdk.core.component.Component +import com.viam.sdk.core.resource.Resource +import com.viam.sdk.core.resource.Subtype +import com.viam.sdk.core.robot.RobotClient + +typealias Properties = GetPropertiesResponse + +/** + * Base represents a physical base of a robot. + */ +abstract class Base(name: String) : Component(SUBTYPE, named(name)) { + + + companion object { + @JvmField + val SUBTYPE = Subtype(Subtype.NAMESPACE_RDK, Subtype.RESOURCE_TYPE_COMPONENT, "base") + + /** + * Get the ResourceName of the component + * @param name the name of the component + * @return the component's ResourceName + */ + @JvmStatic + fun named(name: String): ResourceName { + return Resource.named(SUBTYPE, name) + } + + /** + * Get the component with the provided name from the provided robot. + * @param robot the RobotClient + * @param name the name of the component + * @return the component + */ + @JvmStatic + fun fromRobot(robot: RobotClient, name: String): Base { + return robot.getResource(Base::class.java, named(name)) + } + } + /** + * Move the base in a straight line the given ``distance``, expressed in millimeters, + * at the given ``velocity``, expressed in millimeters per second. + * When ``distance`` or ``velocity`` is 0, the base will stop. + * This method blocks until completed or cancelled. + * @param distance The distance (in millimeters) to move. Negative implies backwards. + * @param velocity The velocity (in millimeters per second) to move. + * Negative implies backwards. + */ + abstract fun moveStraight(distance: Long, velocity: Double, extra: Struct) + + + /** + * Move the base in a straight line the given ``distance``, expressed in millimeters, + * at the given ``velocity``, expressed in millimeters per second. + * When ``distance`` or ``velocity`` is 0, the base will stop. + * This method blocks until completed or cancelled. + * @param distance The distance (in millimeters) to move. Negative implies backwards. + * @param velocity The velocity (in millimeters per second) to move. + * Negative implies backwards. + */ + fun moveStraight(distance: Long, velocity: Double) { + moveStraight(distance, velocity, Struct.getDefaultInstance()) + } + + + /** + * Spin the base in place ``angle`` degrees, at the given angular ``velocity``, + * expressed in degrees per second. + * When ``velocity`` is 0, the base will stop. + * This method blocks until completed or cancelled. + * @param angle The angle (in degrees) to spin. + * @param velocity The angular velocity (in degrees per second) to spin. + * Given a positive angle and a positive velocity, the base will turn to the left. + */ + abstract fun spin(angle: Double, velocity: Double, extra: Struct) + + /** + * Spin the base in place ``angle`` degrees, at the given angular ``velocity``, + * expressed in degrees per second. + * When ``velocity`` is 0, the base will stop. + * This method blocks until completed or cancelled. + * @param angle The angle (in degrees) to spin. + * @param velocity The angular velocity (in degrees per second) to spin. + * Given a positive angle and a positive velocity, the base will turn to the left. + */ + fun spin(angle: Double, velocity: Double) { + spin(angle, velocity, Struct.getDefaultInstance()) + } + + /** + * Set the linear and angular velocity of the Base + * When ``linear`` is 0, the the base will spin. + * When ``angular`` is 0, the the base will move in a straight line. + * When both ``linear`` and ``angular`` are 0, the base will stop. + * When ``linear`` and ``angular`` are both nonzero, the base will move in an arc, with a tighter radius if angular + * power is greater than linear power. + * @param linear The linear component. Only the Y component is used for wheeled base. Positive implies forwards. + * @param angular The angular component. Only the Z component is used + * for wheeled base. Positive turns left; negative turns right. + */ + abstract fun setPower(linear: Vector3, angular: Vector3, extra: Struct) + + /** + * Set the linear and angular velocity of the Base + * When ``linear`` is 0, the the base will spin. + * When ``angular`` is 0, the the base will move in a straight line. + * When both ``linear`` and ``angular`` are 0, the base will stop. + * When ``linear`` and ``angular`` are both nonzero, the base will move in an arc, with a tighter radius if angular + * power is greater than linear power. + * @param linear The linear component. Only the Y component is used for wheeled base. Positive implies forwards. + * @param angular The angular component. Only the Z component is used + * for wheeled base. Positive turns left; negative turns right. + */ + fun setPower(linear: Vector3, angular: Vector3) { + setPower(linear, angular, Struct.getDefaultInstance()) + } + + /** + * Set the linear and angular velocities of the base. + * @param linear The velocity in mm/sec + * @param angular The velocity in mm/sec + */ + abstract fun setVelocity(linear: Vector3, angular: Vector3, extra: Struct) + + /** + * Set the linear and angular velocities of the base. + * @param linear The velocity in mm/sec + * @param angular The velocity in mm/sec + */ + fun setVelocity(linear: Vector3, angular: Vector3) { + setVelocity(linear, angular, Struct.getDefaultInstance()) + } + + /** + * Stops the base. + */ + abstract fun stop(extra: Struct) + + /** + * Stops the base. + */ + fun stop(){ + stop(Struct.getDefaultInstance()) + } + + /** + * Get if the base is currently moving. + * @return Whether the base is moving. + */ + abstract fun isMoving(): Boolean + + /** + * Get the base width and turning radius + * @return the properties of the base + */ + abstract fun getProperties(): Properties + +} \ No newline at end of file diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCClient.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCClient.kt new file mode 100644 index 00000000..837d59e4 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCClient.kt @@ -0,0 +1,74 @@ +package com.viam.sdk.core.component.base + +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.component.base.v1.BaseServiceGrpc +import com.viam.component.base.v1.BaseServiceGrpc.BaseServiceBlockingStub +import com.viam.component.base.v1.Base.* +import com.viam.sdk.core.rpc.Channel +import java.util.* +import kotlin.jvm.optionals.getOrDefault + +class BaseRPCClient(name: String, channel: Channel) : Base(name) { + private val client: BaseServiceBlockingStub + + init { + val client = BaseServiceGrpc.newBlockingStub(channel) + if (channel.callCredentials.isPresent) { + this.client = client.withCallCredentials(channel.callCredentials.get()) + } else { + this.client = client + } + } + + override fun moveStraight(distance: Long, velocity: Double, extra: Struct) { + val request = MoveStraightRequest.newBuilder().setName(this.name.name).setExtra(extra).setDistanceMm(distance).setMmPerSec(velocity).build() + client.moveStraight(request) + } + + override fun spin(angle: Double, velocity: Double, extra: Struct) { + val request = SpinRequest.newBuilder().setName(this.name.name).setAngleDeg(angle).setDegsPerSec(velocity).setExtra(extra).build() + client.spin(request) + } + + override fun setPower(linear: Common.Vector3, angular: Common.Vector3, extra: Struct) { + val request = SetPowerRequest.newBuilder().setName(this.name.name).setLinear(linear).setAngular(angular).setExtra(extra).build() + client.setPower(request) + } + + override fun setVelocity(linear: Common.Vector3, angular: Common.Vector3, extra: Struct) { + val request = SetVelocityRequest.newBuilder().setName(this.name.name).setLinear(linear).setAngular(angular).setExtra(extra).build() + client.setVelocity(request) + } + + override fun stop(extra: Struct) { + val request = StopRequest.newBuilder().setName(this.name.name).setExtra(extra).build() + this.client.stop(request) + } + override fun isMoving(): Boolean { + val request = IsMovingRequest.newBuilder().setName(this.name.name).build() + val response = this.client.isMoving(request) + return response.isMoving + } + + override fun getProperties(): Properties { + val request = GetPropertiesRequest.newBuilder().setName(this.name.name).build() + return client.getProperties(request) + } + + override fun doCommand(command: Map?): Struct { + val request = Common.DoCommandRequest.newBuilder().setName(this.name.name) + .setCommand(Struct.newBuilder().putAllFields(command).build()).build() + val response = this.client.doCommand(request) + return response.result + } + + override fun getGeometries(extra: Optional): List { + val request = GetGeometriesRequest.newBuilder().setName(this.name.name) + .setExtra(extra.getOrDefault(Struct.getDefaultInstance())).build() + val response = this.client.getGeometries(request) + return response.geometriesList + } +} diff --git a/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCService.kt b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCService.kt new file mode 100644 index 00000000..a9211fc5 --- /dev/null +++ b/core/sdk/src/main/kotlin/com/viam/sdk/core/component/base/BaseRPCService.kt @@ -0,0 +1,97 @@ +package com.viam.sdk.core.component.base + +import com.viam.common.v1.Common.* +import com.viam.component.base.v1.BaseServiceGrpc +import com.viam.component.base.v1.Base.* +import com.viam.sdk.core.resource.ResourceManager +import com.viam.sdk.core.resource.ResourceRPCService +import io.grpc.stub.StreamObserver +import java.util.* + + +internal class BaseRPCService(private val manager: ResourceManager) : BaseServiceGrpc.BaseServiceImplBase(), + ResourceRPCService { + + override fun moveStraight(request: MoveStraightRequest, responseObserver: StreamObserver) { + val base = getResource(Base.named(request.name)) + base.moveStraight(request.distanceMm, request.mmPerSec, request.extra) + responseObserver.onNext(MoveStraightResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun spin(request: SpinRequest, responseObserver: StreamObserver) { + val base = getResource(Base.named(request.name)) + base.spin(request.angleDeg, request.degsPerSec, request.extra) + responseObserver.onNext(SpinResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun setPower(request: SetPowerRequest, responseObserver: StreamObserver) { + val base = getResource(Base.named(request.name)) + base.setPower(request.linear, request.angular, request.extra) + responseObserver.onNext(SetPowerResponse.newBuilder().build()) + responseObserver.onCompleted() + } + override fun setVelocity(request: SetVelocityRequest, responseObserver: StreamObserver) { + val base = getResource(Base.named(request.name)) + base.setVelocity(request.linear, request.angular, request.extra) + responseObserver.onNext(SetVelocityResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun stop( + request: StopRequest, responseObserver: StreamObserver + ) { + val base = getResource(Base.named(request.name)) + base.stop(request.extra) + responseObserver.onNext(StopResponse.newBuilder().build()) + responseObserver.onCompleted() + } + + override fun isMoving( + request: IsMovingRequest, responseObserver: StreamObserver + ) { + val base = getResource(Base.named(request.name)) + val isMoving = base.isMoving() + responseObserver.onNext( + IsMovingResponse.newBuilder().setIsMoving(isMoving).build() + ) + responseObserver.onCompleted() + } + + override fun getProperties( + request: GetPropertiesRequest, + responseObserver: StreamObserver + ) { + val base = getResource(Base.named(request.name)) + val result = base.getProperties() + responseObserver.onNext(GetPropertiesResponse.newBuilder().setWidthMeters(result.widthMeters).setTurningRadiusMeters(result.turningRadiusMeters).setWheelCircumferenceMeters(result.wheelCircumferenceMeters).build()) + responseObserver.onCompleted() + } + + override fun doCommand( + request: DoCommandRequest, responseObserver: StreamObserver + ) { + val base = getResource(Base.named(request.name)) + val result = base.doCommand(request.command.fieldsMap) + responseObserver.onNext(DoCommandResponse.newBuilder().setResult(result).build()) + responseObserver.onCompleted() + } + + override fun getGeometries( + request: GetGeometriesRequest, responseObserver: StreamObserver + ) { + val base = getResource(Base.named(request.name)) + val result = base.getGeometries(Optional.of(request.extra)) + responseObserver.onNext(GetGeometriesResponse.newBuilder().addAllGeometries(result).build()) + responseObserver.onCompleted() + } + + override fun getResourceClass(): Class { + return Base::class.java + } + + override fun getManager(): ResourceManager { + return this.manager + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCClientTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCClientTest.kt new file mode 100644 index 00000000..b5c2ed88 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCClientTest.kt @@ -0,0 +1,116 @@ +package com.viam.sdk.core.component.base + +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.component.base.v1.Base.* +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.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class BaseRPCClientTest { + + private lateinit var base: Base + private lateinit var client: BaseRPCClient + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + base = mock( + Base::class.java, withSettings().useConstructor("mock-base").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + val resourceManager = ResourceManager(listOf(base)) + val service = BaseRPCService(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 = BaseRPCClient("mock-base", BasicManagedChannel(channel)) + } + + @Test + fun moveStraight(){ + val extra = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + client.moveStraight(22, 3.0, extra) + verify(base).moveStraight(22, 3.0, extra) + } + + @Test + fun spin(){ + client.spin(22.2, 33.3) + verify(base).spin(22.2, 33.3, Struct.getDefaultInstance()) + } + + @Test + fun setPower(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + client.setPower(linear, angular) + verify(base).setPower(linear,angular, Struct.getDefaultInstance()) + } + + @Test + fun setVelocity(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + client.setVelocity(linear, angular) + verify(base).setVelocity(linear,angular, Struct.getDefaultInstance()) + } + + @Test + fun getProperties(){ + val properties = GetPropertiesResponse.newBuilder().setWheelCircumferenceMeters(1.2).setWidthMeters(2.3).setTurningRadiusMeters(3.4).build() + `when`(base.getProperties()).thenReturn(properties) + val result = client.getProperties() + verify(base).getProperties() + assertEquals(properties, result) + + } + + @Test + fun stop() { + client.stop() + verify(base).stop(Struct.getDefaultInstance()) + } + + @Test + fun isMoving() { + `when`(base.isMoving()).thenReturn(false) + val isMoving = client.isMoving() + verify(base).isMoving() + assertFalse(isMoving) + } + + @Test + fun doCommand() { + val command = mapOf("foo" to Value.newBuilder().setStringValue("bar").build()) + doReturn(Struct.newBuilder().putAllFields(command).build()).`when`(base).doCommand(anyMap()) + val response = client.doCommand(command) + verify(base).doCommand(command) + assertEquals(command, response.fieldsMap) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(base).getGeometries(any()) + client.getGeometries(Optional.empty()) + verify(base).getGeometries(any()) + } + +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCServiceTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCServiceTest.kt new file mode 100644 index 00000000..2fa8d48b --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseRPCServiceTest.kt @@ -0,0 +1,128 @@ +package com.viam.sdk.core.component.base + +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.component.base.v1.BaseServiceGrpc +import com.viam.component.base.v1.BaseServiceGrpc.BaseServiceBlockingStub +import com.viam.component.base.v1.Base.* +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.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import java.util.* + +class BaseRPCServiceTest { + private lateinit var base: Base + private lateinit var client: BaseServiceBlockingStub + + @JvmField + @Rule + val grpcCleanupRule: GrpcCleanupRule = GrpcCleanupRule() + + @BeforeEach + fun setup() { + base = mock( + Base::class.java, withSettings().useConstructor("mock-base").defaultAnswer( + CALLS_REAL_METHODS + ) + ) + + val resourceManager = ResourceManager(listOf(base)) + val service = BaseRPCService(resourceManager) + val serviceName = InProcessServerBuilder.generateName() + grpcCleanupRule.register( + InProcessServerBuilder.forName(serviceName).directExecutor().addService(service).build().start() + ) + client = BaseServiceGrpc.newBlockingStub( + grpcCleanupRule.register( + InProcessChannelBuilder.forName(serviceName).build() + ) + ) + } + + @Test + fun moveStraight(){ + val request = MoveStraightRequest.newBuilder().setName(base.name.name).setDistanceMm(22).setMmPerSec(3.0).build() + client.moveStraight(request) + verify(base).moveStraight(22, 3.0, Struct.getDefaultInstance()) + } + + @Test + fun spin(){ + val request = SpinRequest.newBuilder().setName(base.name.name).setAngleDeg(22.2).setDegsPerSec(33.3).build() + client.spin(request) + verify(base).spin(22.2, 33.3, Struct.getDefaultInstance()) + } + + @Test + fun setPower(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + val request = SetPowerRequest.newBuilder().setName(base.name.name).setLinear(linear).setAngular(angular).build() + client.setPower(request) + verify(base).setPower(linear,angular, Struct.getDefaultInstance()) + } + + @Test + fun setVelocity(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + val request = SetVelocityRequest.newBuilder().setName(base.name.name).setLinear(linear).setAngular(angular).build() + client.setVelocity(request) + verify(base).setVelocity(linear,angular, Struct.getDefaultInstance()) + } + + @Test + fun getProperties(){ + val properties = GetPropertiesResponse.newBuilder().setWheelCircumferenceMeters(1.2).setWidthMeters(2.3).setTurningRadiusMeters(3.4).build() + `when`(base.getProperties()).thenReturn(properties) + val request = GetPropertiesRequest.newBuilder().setName(base.name.name).build() + val result = client.getProperties(request) + verify(base).getProperties() + assertEquals(properties, result) + + } + + + @Test + fun stop() { + val request = StopRequest.newBuilder().setName(base.name.name).build() + client.stop(request) + verify(base).stop(Struct.getDefaultInstance()) + } + + @Test + fun isMoving() { + `when`(base.isMoving()).thenReturn(false) + val request = IsMovingRequest.newBuilder().setName(base.name.name).build() + val response = client.isMoving(request) + verify(base).isMoving() + assertFalse(response.isMoving) + } + + @Test + fun doCommand() { + val command = + Struct.newBuilder().putAllFields(mapOf("foo" to Value.newBuilder().setStringValue("bar").build())).build() + doReturn(command).`when`(base).doCommand(anyMap()) + val request = Common.DoCommandRequest.newBuilder().setName(base.name.name).setCommand(command).build() + val response = client.doCommand(request) + verify(base).doCommand(command.fieldsMap) + assertEquals(command, response.result) + } + + @Test + fun getGeometries() { + doReturn(listOf()).`when`(base).getGeometries(any()) + val request = Common.GetGeometriesRequest.newBuilder().setName(base.name.name).build() + client.getGeometries(request) + verify(base).getGeometries(Optional.of(Struct.getDefaultInstance())) + } +} \ No newline at end of file diff --git a/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseTest.kt b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseTest.kt new file mode 100644 index 00000000..58d35890 --- /dev/null +++ b/core/sdk/src/test/kotlin/com/viam/sdk/core/component/base/BaseTest.kt @@ -0,0 +1,74 @@ +package com.viam.sdk.core.component.base + +import com.google.protobuf.Struct +import com.viam.common.v1.Common +import com.viam.component.base.v1.Base.GetPropertiesResponse +import com.viam.sdk.core.component.base.Base +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Answers +import org.mockito.Mockito.* + +class BaseTest { + private lateinit var base: Base + + @BeforeEach + fun setup() { + base = mock(Base::class.java, Answers.CALLS_REAL_METHODS) + } + + @Test + fun moveStraight(){ + base.moveStraight(22, 3.0) + verify(base).moveStraight(22, 3.0, Struct.getDefaultInstance()) + } + + @Test + fun spin(){ + base.spin(22.2, 33.3) + verify(base).spin(22.2, 33.3, Struct.getDefaultInstance()) + } + + @Test + fun setPower(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + base.setPower(linear,angular) + verify(base).setPower(linear,angular) + } + + @Test + fun setVelocity(){ + val linear = Common.Vector3.newBuilder().setX(22.0).setY(22.0).setZ(22.0).build() + val angular = Common.Vector3.newBuilder().setX(11.0).setY(11.0).setZ(11.0).build() + base.setVelocity(linear, angular) + verify(base).setVelocity(linear,angular) + } + + @Test + fun stop(){ + base.stop() + verify(base).stop() + + } + + @Test + fun isMoving(){ + `when`(base.isMoving()).thenReturn(false) + val isMoving = base.isMoving() + verify(base).isMoving() + assertFalse(isMoving) + } + + @Test + fun getProperties(){ + val properties = GetPropertiesResponse.newBuilder().setWheelCircumferenceMeters(1.2).setWidthMeters(2.3).setTurningRadiusMeters(3.4).build() + + `when`(base.getProperties()).thenReturn(properties) + val result = base.getProperties() + verify(base).getProperties() + assertEquals(properties, result) + + } +}