First device API draft.
This commit is contained in:
79
src/test/java/li/cil/oc2/bus/DeviceBusTests.java
Normal file
79
src/test/java/li/cil/oc2/bus/DeviceBusTests.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package li.cil.oc2.bus;
|
||||
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.api.device.Device;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.vm.DeviceBusControllerImpl;
|
||||
import li.cil.sedna.api.device.serial.SerialDevice;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class DeviceBusTests {
|
||||
@Mock
|
||||
private static Capability<DeviceBusElement> busElementCapability;
|
||||
|
||||
private World world;
|
||||
private SerialDevice serialDevice;
|
||||
private DeviceBusControllerImpl controller;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY = busElementCapability;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setupEach() {
|
||||
world = mock(World.class);
|
||||
serialDevice = mock(SerialDevice.class);
|
||||
controller = new DeviceBusControllerImpl(serialDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanPendingWhenTileEntityNotLoaded() {
|
||||
Assertions.assertEquals(DeviceBusControllerImpl.State.SCAN_PENDING,
|
||||
controller.scan(world, new BlockPos(0, 0, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanCompletesWhenNoNeighbors() {
|
||||
when(world.chunkExists(anyInt(), anyInt())).thenReturn(true);
|
||||
Assertions.assertEquals(DeviceBusControllerImpl.State.READY,
|
||||
controller.scan(world, new BlockPos(0, 0, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanSuccessfulWithLocalElement() {
|
||||
when(world.chunkExists(anyInt(), anyInt())).thenReturn(true);
|
||||
|
||||
final TileEntity tileEntity = mock(TileEntity.class);
|
||||
when(world.getTileEntity(eq(new BlockPos(0, 0, 0)))).thenReturn(tileEntity);
|
||||
|
||||
final DeviceBusElement busElement = mock(DeviceBusElement.class);
|
||||
when(tileEntity.getCapability(eq(busElementCapability), any())).thenReturn(LazyOptional.of(() -> busElement));
|
||||
|
||||
final Device device = mock(Device.class);
|
||||
when(busElement.getDevices()).thenReturn(Collections.singletonList(device));
|
||||
|
||||
when(device.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
|
||||
Assertions.assertEquals(DeviceBusControllerImpl.State.READY,
|
||||
controller.scan(world, new BlockPos(0, 0, 0)));
|
||||
|
||||
verify(busElement).setController(controller);
|
||||
Assertions.assertTrue(controller.getDevices().contains(device));
|
||||
}
|
||||
}
|
||||
54
src/test/java/li/cil/oc2/vm/AbstractTestMethod.java
Normal file
54
src/test/java/li/cil/oc2/vm/AbstractTestMethod.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package li.cil.oc2.vm;
|
||||
|
||||
import li.cil.oc2.api.device.DeviceMethod;
|
||||
import li.cil.oc2.api.device.DeviceMethodParameter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
abstract class AbstractTestMethod implements DeviceMethod {
|
||||
private final Class<?> returnType;
|
||||
private final DeviceMethodParameter[] parameters;
|
||||
|
||||
protected AbstractTestMethod(final Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
this.returnType = returnType;
|
||||
parameters = new DeviceMethodParameter[parameterTypes.length];
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
parameters[i] = new TestParameter("arg" + i, parameterTypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceMethodParameter[] getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static final class TestParameter implements DeviceMethodParameter {
|
||||
private final String name;
|
||||
private final Class<?> type;
|
||||
|
||||
public TestParameter(final String name, final Class<?> type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getName() {
|
||||
return Optional.ofNullable(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
256
src/test/java/li/cil/oc2/vm/ObjectDeviceProtocolTests.java
Normal file
256
src/test/java/li/cil/oc2/vm/ObjectDeviceProtocolTests.java
Normal file
@@ -0,0 +1,256 @@
|
||||
package li.cil.oc2.vm;
|
||||
|
||||
import com.google.gson.*;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.api.device.AbstractDevice;
|
||||
import li.cil.oc2.api.device.Device;
|
||||
import li.cil.oc2.api.device.DeviceMethod;
|
||||
import li.cil.oc2.api.device.object.Callback;
|
||||
import li.cil.oc2.api.device.object.ObjectDevice;
|
||||
import li.cil.oc2.api.device.object.Parameter;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.vm.DeviceBusControllerImpl;
|
||||
import li.cil.oc2.common.vm.DeviceBusElementImpl;
|
||||
import li.cil.sedna.api.device.serial.SerialDevice;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ObjectDeviceProtocolTests {
|
||||
private static final BlockPos CONTROLLER_POS = new BlockPos(0, 0, 0);
|
||||
|
||||
@Mock private Capability<DeviceBusElement> busElementCapability;
|
||||
private World world;
|
||||
private TestSerialDevice serialDevice;
|
||||
private DeviceBusControllerImpl controller;
|
||||
private DeviceBusElement busElement;
|
||||
|
||||
@BeforeEach
|
||||
public void setupEach() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY = busElementCapability;
|
||||
|
||||
serialDevice = new TestSerialDevice();
|
||||
controller = new DeviceBusControllerImpl(serialDevice);
|
||||
busElement = new DeviceBusElementImpl();
|
||||
|
||||
world = mock(World.class);
|
||||
when(world.chunkExists(anyInt(), anyInt())).thenReturn(true);
|
||||
|
||||
final TileEntity tileEntity = mock(TileEntity.class);
|
||||
when(world.getTileEntity(any())).thenReturn(tileEntity);
|
||||
|
||||
when(tileEntity.getCapability(eq(busElementCapability), any())).thenReturn(LazyOptional.of(() -> busElement));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetAndReadDescriptor() {
|
||||
final VoidIntMethod method = new VoidIntMethod();
|
||||
|
||||
busElement.addDevice(new TestDevice(method));
|
||||
controller.scan(world, CONTROLLER_POS);
|
||||
|
||||
final JsonObject request = new JsonObject();
|
||||
request.addProperty("type", "status");
|
||||
serialDevice.putAsVM(request.toString());
|
||||
controller.step(0); // process message
|
||||
|
||||
final String message = serialDevice.readMessageAsVM();
|
||||
Assertions.assertNotNull(message);
|
||||
|
||||
final JsonObject json = new JsonParser().parse(message).getAsJsonObject();
|
||||
|
||||
final JsonArray devices = json.getAsJsonArray("data");
|
||||
Assertions.assertEquals(1, devices.size());
|
||||
|
||||
final JsonObject device = devices.get(0).getAsJsonObject();
|
||||
|
||||
final JsonArray methods = device.getAsJsonArray("methods");
|
||||
Assertions.assertEquals(1, methods.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleMethod() {
|
||||
final VoidIntMethod method = new VoidIntMethod();
|
||||
final TestDevice device = new TestDevice(method);
|
||||
|
||||
busElement.addDevice(device);
|
||||
controller.scan(world, CONTROLLER_POS);
|
||||
|
||||
invokeMethod(device, method.getName(), 0xdeadbeef);
|
||||
|
||||
Assertions.assertEquals(0xdeadbeef, method.passedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returningMethod() {
|
||||
final IntLongMethod method = new IntLongMethod();
|
||||
final TestDevice device = new TestDevice(method);
|
||||
|
||||
busElement.addDevice(device);
|
||||
controller.scan(world, CONTROLLER_POS);
|
||||
|
||||
final JsonElement result = invokeMethod(device, method.getName(), 0xdeadbeefcafebabeL);
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result.isJsonPrimitive());
|
||||
Assertions.assertEquals(0xcafebabe, result.getAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedObject() {
|
||||
final SimpleObject object = new SimpleObject();
|
||||
final ObjectDevice device = new ObjectDevice(object);
|
||||
|
||||
busElement.addDevice(device);
|
||||
controller.scan(world, CONTROLLER_POS);
|
||||
|
||||
Assertions.assertEquals(42 + 23, invokeMethod(device, "add", 42, 23).getAsInt());
|
||||
}
|
||||
|
||||
private JsonElement invokeMethod(final Device device, final String name, final Object... parameters) {
|
||||
final JsonObject request = new JsonObject();
|
||||
request.addProperty("type", "invoke");
|
||||
final JsonObject methodInvocation = new JsonObject();
|
||||
methodInvocation.addProperty("deviceId", device.getUniqueId().toString());
|
||||
methodInvocation.addProperty("name", name);
|
||||
final JsonArray parametersJson = new JsonArray();
|
||||
methodInvocation.add("parameters", parametersJson);
|
||||
for (final Object parameter : parameters) {
|
||||
parametersJson.add(new Gson().toJson(parameter));
|
||||
}
|
||||
request.add("data", methodInvocation);
|
||||
serialDevice.putAsVM(request.toString());
|
||||
|
||||
controller.step(0);
|
||||
|
||||
final String result = serialDevice.readMessageAsVM();
|
||||
Assertions.assertNotNull(result);
|
||||
final JsonObject resultJson = new JsonParser().parse(result).getAsJsonObject();
|
||||
Assertions.assertEquals("result", resultJson.get("type").getAsString());
|
||||
return resultJson.get("data");
|
||||
}
|
||||
|
||||
private static final class VoidIntMethod extends AbstractTestMethod {
|
||||
public int passedValue;
|
||||
|
||||
VoidIntMethod() {
|
||||
super(void.class, int.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final Object... parameters) {
|
||||
passedValue = (int) parameters[0];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class IntLongMethod extends AbstractTestMethod {
|
||||
public long passedValue;
|
||||
|
||||
IntLongMethod() {
|
||||
super(int.class, long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final Object... parameters) {
|
||||
passedValue = (long) parameters[0];
|
||||
return (int) passedValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SimpleObject {
|
||||
@Callback
|
||||
public int add(@Parameter("a") final int a,
|
||||
@Parameter("b") final int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
@Callback
|
||||
public int div(@Parameter("a") final long a,
|
||||
@Parameter("b") final long b) {
|
||||
return (int) (a / b);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestSerialDevice implements SerialDevice {
|
||||
private final ByteArrayFIFOQueue transmit = new ByteArrayFIFOQueue();
|
||||
private final ByteArrayFIFOQueue receive = new ByteArrayFIFOQueue();
|
||||
|
||||
public void putAsVM(final String data) {
|
||||
final byte[] bytes = data.getBytes();
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
transmit.enqueue(bytes[i]);
|
||||
}
|
||||
transmit.enqueue((byte) 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String readMessageAsVM() {
|
||||
final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
while (!receive.isEmpty()) {
|
||||
final byte value = receive.dequeueByte();
|
||||
|
||||
if (value == 0) {
|
||||
if (bytes.size() == 0) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bytes.write(value);
|
||||
}
|
||||
|
||||
if (bytes.size() > 0) {
|
||||
return new String(bytes.toByteArray());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return transmit.isEmpty() ? -1 : transmit.dequeueByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPutByte() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByte(final byte value) {
|
||||
receive.enqueue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestDevice extends AbstractDevice {
|
||||
private final DeviceMethod method;
|
||||
|
||||
public TestDevice(final DeviceMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceMethod> getMethods() {
|
||||
return Collections.singletonList(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/test/java/li/cil/oc2/vm/package-info.java
Normal file
7
src/test/java/li/cil/oc2/vm/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.vm;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
Reference in New Issue
Block a user