From 4b9b921df9d71bc124955d472595bdcb5034b059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 13 Feb 2022 17:01:47 +0100 Subject: [PATCH] Update tests. --- .../li/cil/oc2/common/bus/BlockBusTests.java | 160 ------ .../bus/BlockDeviceBusControllerTests.java | 521 ++++++++++++++++++ ...va => CommonDeviceBusControllerTests.java} | 2 +- ...sts.java => RPCDeviceBusAdapterTests.java} | 10 +- ...ests.java => VMDeviceBusAdapterTests.java} | 21 +- 5 files changed, 541 insertions(+), 173 deletions(-) delete mode 100644 src/test/java/li/cil/oc2/common/bus/BlockBusTests.java create mode 100644 src/test/java/li/cil/oc2/common/bus/BlockDeviceBusControllerTests.java rename src/test/java/li/cil/oc2/common/bus/{DeviceBusTests.java => CommonDeviceBusControllerTests.java} (98%) rename src/test/java/li/cil/oc2/common/bus/{RPCDeviceTests.java => RPCDeviceBusAdapterTests.java} (95%) rename src/test/java/li/cil/oc2/common/bus/{VMDeviceTests.java => VMDeviceBusAdapterTests.java} (96%) diff --git a/src/test/java/li/cil/oc2/common/bus/BlockBusTests.java b/src/test/java/li/cil/oc2/common/bus/BlockBusTests.java deleted file mode 100644 index f290cd63..00000000 --- a/src/test/java/li/cil/oc2/common/bus/BlockBusTests.java +++ /dev/null @@ -1,160 +0,0 @@ -/* SPDX-License-Identifier: MIT */ - -package li.cil.oc2.common.bus; - -import li.cil.oc2.api.bus.DeviceBusElement; -import li.cil.oc2.common.capabilities.Capabilities; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; - -import java.util.HashMap; -import java.util.HashSet; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.*; - -public class BlockBusTests { - @SuppressWarnings("unchecked") private static final Capability DEVICE_BUS_ELEMENT_CAPABILITY = (Capability) mock(Capability.class); - - private FakeLevel fakeLevel; - private LevelAccessor level; - private MockedStatic capabilitiesMock; - - @BeforeEach - public void setupEach() { - fakeLevel = new FakeLevel(); - level = fakeLevel.getLevel(); - capabilitiesMock = mockStatic(Capabilities.class); - capabilitiesMock.when(Capabilities::deviceBusElement).thenReturn(DEVICE_BUS_ELEMENT_CAPABILITY); - } - - @AfterEach - public void teardownEach() { - capabilitiesMock.close(); - } - - @Test - public void busTouchingUnloadedChunkStaysIncomplete() { - final BlockDeviceBusController busController = createBusController(new BlockPos(0, 0, 0)); - - busController.scan(); - - assertEquals(CommonDeviceBusController.BusState.INCOMPLETE, busController.getState()); - } - - @Test - public void busNotTouchingUnloadedChunkCompletes() { - final BlockDeviceBusController busController = createBusController(new BlockPos(8, 0, 8)); - - busController.scan(); - - assertEquals(CommonDeviceBusController.BusState.READY, busController.getState()); - } - - @Test - public void busControllerDetectsBusElements() { - final BlockDeviceBusController busController = createBusController(new BlockPos(8, 0, 8)); - - final DeviceBusElement busElement = createBusElement(new BlockPos(9, 0, 8)); - - busController.scan(); - - assertEquals(CommonDeviceBusController.BusState.READY, busController.getState()); - - assertTrue(busElement.getControllers().contains(busController)); - assertTrue(busController.getElements().contains(busElement)); - } - - private BlockDeviceBusController createBusController(final BlockPos pos) { - final TestBlockDeviceBusElement busElement = new TestBlockDeviceBusElement(level, pos); - final BlockEntity blockEntity = mock(BlockEntity.class); - when(blockEntity.getBlockPos()).thenReturn(pos); - when(blockEntity.getCapability(any(), any())).thenReturn(LazyOptional.empty()); - final BlockDeviceBusController busController = new BlockDeviceBusController(busElement, 0, blockEntity); - - fakeLevel.addBlockEntity(blockEntity); - fakeLevel.setChunkLoaded(new ChunkPos(pos), true); - - return busController; - } - - private AbstractBlockDeviceBusElement createBusElement(final BlockPos pos) { - final TestBlockDeviceBusElement busElement = new TestBlockDeviceBusElement(level, pos); - final BlockEntity blockEntity = mock(BlockEntity.class); - when(blockEntity.getBlockPos()).thenReturn(pos); - when(blockEntity.getCapability(any(), any())).thenReturn(LazyOptional.empty()); - when(blockEntity.getCapability(eq(Capabilities.deviceBusElement()), any())).thenReturn(LazyOptional.of(() -> busElement)); - - fakeLevel.addBlockEntity(blockEntity); - fakeLevel.setChunkLoaded(new ChunkPos(pos), true); - - return busElement; - } - - private static final class FakeLevel { - private final LevelAccessor level = mock(LevelAccessor.class); - private final HashMap blockEntities = new HashMap<>(); - private final HashSet loadedChunks = new HashSet<>(); - - public FakeLevel() { - when(level.getBlockEntity(any())).then(a -> blockEntities.get(a.getArgument(0))); - - when(level.hasChunk(anyInt(), anyInt())).then(a -> { - final int chunkX = a.getArgument(0); - final int chunkZ = a.getArgument(1); - return loadedChunks.contains(new ChunkPos(chunkX, chunkZ)); - }); - } - - public LevelAccessor getLevel() { - return level; - } - - public void addBlockEntity(final BlockEntity blockEntity) { - blockEntities.put(blockEntity.getBlockPos(), blockEntity); - } - - public void removeBlockEntity(final BlockPos pos) { - blockEntities.remove(pos); - } - - public void setChunkLoaded(final ChunkPos chunkPos, final boolean loaded) { - if (loaded) { - loadedChunks.add(chunkPos); - } else { - loadedChunks.remove(chunkPos); - } - } - } - - private static final class TestBlockDeviceBusElement extends AbstractBlockDeviceBusElement { - private final LevelAccessor level; - private final BlockPos blockPos; - - public TestBlockDeviceBusElement(final LevelAccessor level, final BlockPos blockPos) { - this.level = level; - this.blockPos = blockPos; - } - - @Override - public LevelAccessor getLevel() { - return level; - } - - @Override - public BlockPos getPosition() { - return blockPos; - } - } -} diff --git a/src/test/java/li/cil/oc2/common/bus/BlockDeviceBusControllerTests.java b/src/test/java/li/cil/oc2/common/bus/BlockDeviceBusControllerTests.java new file mode 100644 index 00000000..a1f7e58b --- /dev/null +++ b/src/test/java/li/cil/oc2/common/bus/BlockDeviceBusControllerTests.java @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.bus; + +import li.cil.oc2.api.API; +import li.cil.oc2.api.bus.DeviceBusElement; +import li.cil.oc2.api.bus.device.Device; +import li.cil.oc2.api.bus.device.ItemDevice; +import li.cil.oc2.api.bus.device.object.Callback; +import li.cil.oc2.api.bus.device.object.ObjectDevice; +import li.cil.oc2.api.bus.device.provider.BlockDeviceProvider; +import li.cil.oc2.api.bus.device.provider.BlockDeviceQuery; +import li.cil.oc2.api.bus.device.provider.ItemDeviceProvider; +import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; +import li.cil.oc2.api.util.Invalidatable; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.bus.device.provider.Providers; +import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.util.LevelUtils; +import li.cil.sedna.api.device.serial.SerialDevice; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.registries.ForgeRegistryEntry; +import net.minecraftforge.registries.IForgeRegistry; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.stubbing.OngoingStubbing; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.*; + +public class BlockDeviceBusControllerTests { + public static final IForgeRegistry BLOCK_DEVICE_PROVIDER_REGISTRY = createBlockDeviceProviderRegistry(); + public static final IForgeRegistry ITEM_DEVICE_PROVIDER_REGISTRY = createItemDeviceProviderRegistry(); + + private static final Set> REGISTERED_CAPABILITIES = new HashSet<>(); + + private static MockedStatic capabilitiesMock; + private static MockedStatic providersMock; + private static MockedStatic levelUtilsMock; + + private FakeLevel fakeLevel; + private LevelAccessor level; + + /////////////////////////////////////////////////////////////////// + + @SuppressWarnings("ResultOfMethodCallIgnored") + @BeforeAll + public static void setup() { + capabilitiesMock = mockStatic(Capabilities.class); + registerCapability(capabilitiesMock.when(Capabilities::energyStorage)); + registerCapability(capabilitiesMock.when(Capabilities::fluidHandler)); + registerCapability(capabilitiesMock.when(Capabilities::itemHandler)); + registerCapability(capabilitiesMock.when(Capabilities::deviceBusElement)); + registerCapability(capabilitiesMock.when(Capabilities::device)); + registerCapability(capabilitiesMock.when(Capabilities::redstoneEmitter)); + registerCapability(capabilitiesMock.when(Capabilities::networkInterface)); + registerCapability(capabilitiesMock.when(Capabilities::terminalUserProvider)); + registerCapability(capabilitiesMock.when(Capabilities::robot)); + Capabilities.registerCapabilities(type -> assertTrue(REGISTERED_CAPABILITIES.contains(type))); + + providersMock = mockStatic(Providers.class); + providersMock.when(Providers::blockDeviceProviderRegistry).thenReturn(BLOCK_DEVICE_PROVIDER_REGISTRY); + providersMock.when(Providers::itemDeviceProviderRegistry).thenReturn(ITEM_DEVICE_PROVIDER_REGISTRY); + + levelUtilsMock = mockStatic(LevelUtils.class); + levelUtilsMock.when(() -> LevelUtils.getBlockName(any(), any())).thenReturn("test_block"); + } + + @SuppressWarnings("unchecked") + private static void registerCapability(final OngoingStubbing> stubbing) { + final Capability capability = mock(Capability.class); + stubbing.thenReturn(capability); + } + + @AfterAll + public static void teardown() { + REGISTERED_CAPABILITIES.clear(); + + capabilitiesMock.close(); + providersMock.close(); + levelUtilsMock.close(); + } + + @BeforeEach + public void setupEach() { + fakeLevel = new FakeLevel(); + level = fakeLevel.getLevel(); + } + + @Test + public void busTouchingUnloadedChunkStaysIncomplete() { + final BlockPos posAtChunkEdge = new BlockPos(0, 0, 0); + final BlockDeviceBusController busController = new TestBusControllerBlockEntity(posAtChunkEdge).getBusController(); + + busController.scan(); + + assertEquals(CommonDeviceBusController.BusState.INCOMPLETE, busController.getState()); + } + + @Test + public void busNotTouchingUnloadedChunkCompletes() { + final BlockPos posInsideChunk = new BlockPos(8, 0, 8); + final BlockDeviceBusController busController = new TestBusControllerBlockEntity(posInsideChunk).getBusController(); + + busController.scan(); + + assertEquals(CommonDeviceBusController.BusState.READY, busController.getState()); + } + + @Test + public void busControllerIgnoresNonAccessibleBusElements() { + final BlockPos controllerPos = new BlockPos(8, 0, 8); + final TestBusControllerBlockEntity busController = new TestBusControllerBlockEntity(controllerPos); + + final BlockPos elementPos = controllerPos.east(); + final TestBusElementBlockEntity busElement = new TestBusElementBlockEntity(elementPos); + busElement.setSideEnabled(Direction.WEST, false); + + busController.getBusController().scan(); + + assertEquals(CommonDeviceBusController.BusState.READY, busController.getBusController().getState()); + + assertFalse(busElement.getBusElement().getControllers().contains(busController.getBusController())); + assertFalse(busController.getBusController().getElements().contains(busElement.getBusElement())); + } + + @Test + public void busControllerDetectsBusElements() { + final BlockPos controllerPos = new BlockPos(8, 0, 8); + final BlockDeviceBusController busController = new TestBusControllerBlockEntity(controllerPos).getBusController(); + + final BlockPos elementPos = controllerPos.east(); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + final DeviceBusElement busElement = busElementInfo.getBusElement(); + + busController.scan(); + + assertTrue(busElement.getControllers().contains(busController)); + assertTrue(busController.getElements().contains(busElement)); + } + + @Test + public void devicesInUnloadedChunksAreMarkedAsUnloaded() { + final BlockPos elementPos = new BlockPos(0, 0, 0); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + final TestBlockDeviceBusElement busElement = busElementInfo.getBusElement(); + + busElement.updateDevicesForNeighbor(Direction.WEST); + verify(busElement, atLeastOnce()).setEntriesForGroupUnloaded(Direction.WEST.get3DDataValue()); + } + + @Test + public void devicesInLoadedChunksAreCollected() { + final BlockPos elementPos = new BlockPos(0, 0, 0); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + final TestBlockDeviceBusElement busElement = busElementInfo.getBusElement(); + + final BlockPos devicePos = elementPos.east(); + final TestDeviceBlockEntity deviceBlockEntity = new TestDeviceBlockEntity(devicePos); + + busElement.updateDevicesForNeighbor(Direction.EAST); + verify(busElement, atLeastOnce()).setEntriesForGroup(eq(Direction.EAST.get3DDataValue()), any()); + assertTrue(busElement.getDevices().contains(deviceBlockEntity.getObjectDevice())); + } + + @Test + public void equalDevicesAreIgnored() { + final BlockPos elementPos = new BlockPos(0, 0, 0); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + final TestBlockDeviceBusElement busElement = busElementInfo.getBusElement(); + + final BlockPos devicePos = elementPos.east(); + final TestDeviceBlockEntity deviceBlockEntity = new TestDeviceBlockEntity(devicePos); + + busElement.updateDevicesForNeighbor(Direction.EAST); + + assertTrue(busElement.getDevices().contains(deviceBlockEntity.getObjectDevice())); + + final ObjectDevice equalDevice = new ObjectDevice(deviceBlockEntity.getTestDevice()); + deviceBlockEntity.setObjectDevice(equalDevice); + + busElement.updateDevicesForNeighbor(Direction.EAST); + + assertTrue(busElement.getDevices().contains(deviceBlockEntity.getObjectDevice())); + assertNotSame(busElement.getDevices().stream().findFirst().orElseThrow(), equalDevice); + } + + @Test + public void busControllerDetectsDevices() { + final BlockPos controllerPos = new BlockPos(8, 0, 8); + final BlockDeviceBusController busController = new TestBusControllerBlockEntity(controllerPos).getBusController(); + + final BlockPos elementPos = controllerPos.east(); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + when(busElementInfo.getBlockEntity().getCapability(eq(Capabilities.deviceBusElement()), any())).thenReturn(LazyOptional.of(busElementInfo::getBusElement)); + + final BlockPos devicePos = elementPos.east(); + final TestDeviceBlockEntity deviceBlockEntity = new TestDeviceBlockEntity(devicePos); + + busElementInfo.getBusElement().updateDevicesForNeighbor(Direction.EAST); + busController.scan(); + + assertTrue(busController.getDevices().contains(deviceBlockEntity.getObjectDevice())); + } + + @Test + public void devicesGetSerializedWhenUnloadedAndDeserializedWhenLoaded() { + final BlockPos controllerPos = new BlockPos(1, 0, 8); + final BlockDeviceBusController busController = new TestBusControllerBlockEntity(controllerPos).getBusController(); + + final BlockPos elementPos = controllerPos.west(); + final TestBusElementBlockEntity busElementInfo = new TestBusElementBlockEntity(elementPos); + when(busElementInfo.getBlockEntity().getCapability(eq(Capabilities.deviceBusElement()), any())).thenReturn(LazyOptional.of(busElementInfo::getBusElement)); + + final BlockPos devicePos = elementPos.west(); + final TestDeviceBlockEntity deviceBlockEntity = new TestDeviceBlockEntity(devicePos); + + fakeLevel.setChunkLoaded(new ChunkPos(devicePos), false); + busController.scheduleBusScan(); + + final RPCDeviceBusAdapter rpcDeviceBusAdapter = new RPCDeviceBusAdapter(mock(SerialDevice.class)); + busController.onBeforeDeviceScan.add(rpcDeviceBusAdapter::pause); + busController.onAfterDeviceScan.add(event -> rpcDeviceBusAdapter.resume(busController, event.didDevicesChange())); + + busController.scan(); + + // Reminder: missing chunk -> bus scan cannot complete. + assertEquals(CommonDeviceBusController.BusState.INCOMPLETE, busController.getState()); + + final ObjectDevice objectDevice = spy(deviceBlockEntity.getObjectDevice()); + deviceBlockEntity.setObjectDevice(objectDevice); + + // Initialize with unloaded chunk. + busElementInfo.getBusElement().updateDevicesForNeighbor(Direction.WEST); + + assertFalse(busController.getDevices().contains(objectDevice)); + + verify(objectDevice, never()).mount(); + verify(objectDevice, never()).unmount(); + verify(objectDevice, never()).dispose(); + verify(objectDevice, never()).serializeNBT(); + verify(objectDevice, never()).deserializeNBT(any()); + + // Load device chunk. + fakeLevel.setChunkLoaded(new ChunkPos(devicePos), true); + busController.scheduleBusScan(); + busElementInfo.getBusElement().updateDevicesForNeighbor(Direction.WEST); + + busController.scan(); + assertEquals(CommonDeviceBusController.BusState.READY, busController.getState()); + + assertTrue(busController.getDevices().contains(objectDevice)); + + rpcDeviceBusAdapter.mountDevices(); + + verify(objectDevice, times(1)).mount(); + verify(objectDevice, never()).unmount(); + verify(objectDevice, never()).dispose(); + verify(objectDevice, never()).serializeNBT(); + verify(objectDevice, never()).deserializeNBT(any()); // no state to deserialize + + // Unload device chunk. + fakeLevel.setChunkLoaded(new ChunkPos(devicePos), false); + busController.scheduleBusScan(); + busElementInfo.getBusElement().updateDevicesForNeighbor(Direction.WEST); + + busController.scan(); + assertEquals(CommonDeviceBusController.BusState.INCOMPLETE, busController.getState()); + + assertFalse(busController.getDevices().contains(objectDevice)); + + verify(objectDevice, times(1)).mount(); + verify(objectDevice, times(1)).unmount(); + verify(objectDevice, never()).dispose(); + verify(objectDevice, times(1)).serializeNBT(); + verify(objectDevice, never()).deserializeNBT(any()); + } + + // TODO device in loaded chunk -> unloaded chunk + saved -> loaded chunk + loaded + + /////////////////////////////////////////////////////////////////// + + @SuppressWarnings("unchecked") + private static IForgeRegistry createBlockDeviceProviderRegistry() { + final IForgeRegistry registry = mock(IForgeRegistry.class); + + final Map blockDeviceProviders = new HashMap<>(); + blockDeviceProviders.put(new ResourceLocation(API.MOD_ID, "test"), new TestBlockDeviceProvider()); + + when(registry.getValues()).thenReturn(blockDeviceProviders.values()); + when(registry.getValue(notNull())).then(a -> blockDeviceProviders.get(a.getArgument(0))); + + return registry; + } + + @SuppressWarnings("unchecked") + private static IForgeRegistry createItemDeviceProviderRegistry() { + final IForgeRegistry registry = mock(IForgeRegistry.class); + + final Map itemDeviceProviders = new HashMap<>(); + itemDeviceProviders.put(new ResourceLocation(API.MOD_ID, "test"), new TestItemDeviceProvider()); + + when(registry.getValues()).thenReturn(itemDeviceProviders.values()); + when(registry.getValue(notNull())).then(a -> itemDeviceProviders.get(a.getArgument(0))); + + return registry; + } + + /////////////////////////////////////////////////////////////////// + + private static final class FakeLevel { + private final LevelAccessor level = mock(LevelAccessor.class); + private final HashMap blockEntities = new HashMap<>(); + private final HashSet loadedChunks = new HashSet<>(); + + public FakeLevel() { + when(level.getBlockEntity(any())).then(a -> blockEntities.get(a.getArgument(0))); + + when(level.hasChunk(anyInt(), anyInt())).then(a -> { + final int chunkX = a.getArgument(0); + final int chunkZ = a.getArgument(1); + return loadedChunks.contains(new ChunkPos(chunkX, chunkZ)); + }); + } + + public LevelAccessor getLevel() { + return level; + } + + public void addBlockEntity(final BlockEntity blockEntity) { + blockEntities.put(blockEntity.getBlockPos(), blockEntity); + } + + public void removeBlockEntity(final BlockPos pos) { + blockEntities.remove(pos); + } + + public void setChunkLoaded(final ChunkPos chunkPos, final boolean loaded) { + if (loaded) { + loadedChunks.add(chunkPos); + } else { + loadedChunks.remove(chunkPos); + } + } + } + + private class TestBlockEntity { + private final BlockEntity blockEntity; + + public TestBlockEntity(final BlockPos pos) { + blockEntity = mock(BlockEntity.class); + when(blockEntity.getBlockPos()).thenReturn(pos); + when(blockEntity.getCapability(any(), any())).thenReturn(LazyOptional.empty()); + when(blockEntity.getCapability(any())).thenCallRealMethod(); + + fakeLevel.addBlockEntity(blockEntity); + fakeLevel.setChunkLoaded(new ChunkPos(pos), true); + } + + public BlockEntity getBlockEntity() { + return blockEntity; + } + } + + private class TestBusElementBlockEntity extends TestBlockEntity { + private final TestBlockDeviceBusElement busElement; + private final boolean[] enabledSides = new boolean[Constants.BLOCK_FACE_COUNT]; + + public TestBusElementBlockEntity(final BlockPos pos) { + super(pos); + busElement = spy(new TestBlockDeviceBusElement(level, pos)); + when(getBlockEntity().getCapability(eq(Capabilities.deviceBusElement()), any())).then(a -> { + if (enabledSides[a.getArgument(1).get3DDataValue()]) { + return LazyOptional.of(() -> busElement); + } else { + return LazyOptional.empty(); + } + }); + Arrays.fill(enabledSides, true); + } + + public TestBlockDeviceBusElement getBusElement() { + return busElement; + } + + public void setSideEnabled(final Direction side, final boolean value) { + enabledSides[side.get3DDataValue()] = value; + } + } + + private class TestBusControllerBlockEntity extends TestBusElementBlockEntity { + private final BlockDeviceBusController busController; + + public TestBusControllerBlockEntity(final BlockPos pos) { + super(pos); + busController = new BlockDeviceBusController(getBusElement(), 0, getBlockEntity()); + } + + public BlockDeviceBusController getBusController() { + return busController; + } + } + + private class TestDeviceBlockEntity extends TestBlockEntity { + private final TestDevice testDevice; + private ObjectDevice objectDevice; + + public TestDeviceBlockEntity(final BlockPos pos) { + super(pos); + testDevice = new TestDevice(); + objectDevice = new ObjectDevice(testDevice); + when(getBlockEntity().getCapability(eq(Capabilities.device()), any())).thenReturn(LazyOptional.of(() -> objectDevice)); + } + + public TestDevice getTestDevice() { + return testDevice; + } + + public ObjectDevice getObjectDevice() { + return objectDevice; + } + + public void setObjectDevice(final ObjectDevice device) { + this.objectDevice = device; + } + } + + private static final class TestBlockDeviceBusElement extends AbstractBlockDeviceBusElement { + private final LevelAccessor level; + private final BlockPos blockPos; + + public TestBlockDeviceBusElement(final LevelAccessor level, final BlockPos blockPos) { + this.level = level; + this.blockPos = blockPos; + } + + @Override + public LevelAccessor getLevel() { + return level; + } + + @Override + public BlockPos getPosition() { + return blockPos; + } + } + + private static class TestBlockDeviceProvider implements BlockDeviceProvider { + @Override + public BlockDeviceProvider setRegistryName(final ResourceLocation name) { + return this; + } + + @Nullable + @Override + public ResourceLocation getRegistryName() { + return new ResourceLocation(API.MOD_ID, "test"); + } + + @Override + public Class getRegistryType() { + return BlockDeviceProvider.class; + } + + @Override + public Invalidatable getDevice(final BlockDeviceQuery query) { + final LevelAccessor level = query.getLevel(); + final BlockEntity blockEntity = level.getBlockEntity(query.getQueryPosition()); + if (blockEntity != null) { + final Optional optional = blockEntity.getCapability(Capabilities.device()).resolve(); + return optional.map(Invalidatable::of).orElseGet(Invalidatable::empty); + } + return Invalidatable.empty(); + } + } + + private static class TestItemDeviceProvider implements ItemDeviceProvider { + @Override + public ItemDeviceProvider setRegistryName(final ResourceLocation name) { + return this; + } + + @Nullable + @Override + public ResourceLocation getRegistryName() { + return new ResourceLocation(API.MOD_ID, "test"); + } + + @Override + public Class getRegistryType() { + return ItemDeviceProvider.class; + } + + @Override + public Optional getDevice(final ItemDeviceQuery query) { + return Optional.empty(); + } + } + + public static class TestDevice { + @Callback + public int test() { + return 42; + } + } +} diff --git a/src/test/java/li/cil/oc2/common/bus/DeviceBusTests.java b/src/test/java/li/cil/oc2/common/bus/CommonDeviceBusControllerTests.java similarity index 98% rename from src/test/java/li/cil/oc2/common/bus/DeviceBusTests.java rename to src/test/java/li/cil/oc2/common/bus/CommonDeviceBusControllerTests.java index 66e86111..ea2eed4a 100644 --- a/src/test/java/li/cil/oc2/common/bus/DeviceBusTests.java +++ b/src/test/java/li/cil/oc2/common/bus/CommonDeviceBusControllerTests.java @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.*; -public class DeviceBusTests { +public class CommonDeviceBusControllerTests { private CommonDeviceBusController busController; private DeviceBusElement busControllerBusElement; diff --git a/src/test/java/li/cil/oc2/common/bus/RPCDeviceTests.java b/src/test/java/li/cil/oc2/common/bus/RPCDeviceBusAdapterTests.java similarity index 95% rename from src/test/java/li/cil/oc2/common/bus/RPCDeviceTests.java rename to src/test/java/li/cil/oc2/common/bus/RPCDeviceBusAdapterTests.java index a69f1107..385d76c3 100644 --- a/src/test/java/li/cil/oc2/common/bus/RPCDeviceTests.java +++ b/src/test/java/li/cil/oc2/common/bus/RPCDeviceBusAdapterTests.java @@ -13,7 +13,7 @@ import java.util.*; import static org.mockito.Mockito.*; -public final class RPCDeviceTests { +public final class RPCDeviceBusAdapterTests { private RPCDeviceBusAdapter adapter; private Set busDevices; private Map> deviceIdentifiers; @@ -56,7 +56,7 @@ public final class RPCDeviceTests { } @Test - public void mountedDevicesAreUnmountedAndDisposedWhenRemoved() { + public void mountedDevicesAreUnmountedWhenRemoved() { final RPCDevice device = addDevice(); adapter.resume(controller, true); adapter.mountDevices(); @@ -64,18 +64,18 @@ public final class RPCDeviceTests { removeDevice(device); adapter.resume(controller, true); verify(device).unmount(); - verify(device).dispose(); + verify(device, never()).dispose(); } @Test - public void unmountedDevicesAreDisposedWhenRemoved() { + public void unmountedDevicesAreSilentlyRemoved() { final RPCDevice device = addDevice(); adapter.resume(controller, true); removeDevice(device); adapter.resume(controller, true); verify(device, never()).unmount(); - verify(device).dispose(); + verify(device, never()).dispose(); } @Test diff --git a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java b/src/test/java/li/cil/oc2/common/bus/VMDeviceBusAdapterTests.java similarity index 96% rename from src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java rename to src/test/java/li/cil/oc2/common/bus/VMDeviceBusAdapterTests.java index cd9bd1e0..e33ecae9 100644 --- a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java +++ b/src/test/java/li/cil/oc2/common/bus/VMDeviceBusAdapterTests.java @@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -public final class VMDeviceTests { +public final class VMDeviceBusAdapterTests { private MemoryMap memoryMap; private InterruptController interruptController; private R5MemoryRangeAllocationStrategy allocationStrategy; @@ -106,7 +106,7 @@ public final class VMDeviceTests { } @Test - public void mountedDevicesAreUnmountedAndDisposedIfOtherMountFails() { + public void mountedDevicesAreUnmountedIfOtherMountFails() { final VMDevice device1 = mock(VMDevice.class); final VMDevice device2 = mock(VMDevice.class); when(device1.mount(any())).thenReturn(VMDeviceLoadResult.success()); @@ -119,11 +119,11 @@ public final class VMDeviceTests { verify(device1).mount(any()); verify(device2).mount(any()); verify(device1).unmount(); - verify(device1).dispose(); + verify(device1, never()).dispose(); } @Test - public void mountedDevicesAreUnmountedAndDisposedWhenRemoved() { + public void mountedDevicesAreUnmountedWhenRemoved() { final VMDevice device = mock(VMDevice.class); when(device.mount(any())).thenReturn(VMDeviceLoadResult.success()); @@ -132,17 +132,24 @@ public final class VMDeviceTests { adapter.removeDevices(Collections.singleton(device)); verify(device).unmount(); - verify(device).dispose(); + verify(device, never()).dispose(); } @Test - public void unmountedDevicesAreDisposedWhenRemoved() { + public void unmountedDevicesAreSilentlyRemoved() { final VMDevice device = mock(VMDevice.class); + when(device.mount(any())).thenReturn(VMDeviceLoadResult.success()); adapter.addDevices(Collections.singleton(device)); + adapter.mountDevices(); + verify(device).mount(any()); + + adapter.unmountDevices(); + verify(device).unmount(); adapter.removeDevices(Collections.singleton(device)); - verify(device).dispose(); + + verify(device, never()).dispose(); } @Test