1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-09 18:03:08 +00:00

Split CC:T into common and forge projects

After several weeks of carefully arranging ribbons, we pull the string
and end up with, ... a bit of a messy bow. There were still some things
I'd missed.

 - Split the mod into a common (vanilla-only) project and Forge-specific
   project. This gives us room to add Fabric support later on.

 - Split the project into main/client source sets. This is not currently
   statically checked: we'll do that soon.

 - Rename block/item/tile entities to use suffixes rather than prefixes.
This commit is contained in:
Jonathan Coates
2022-11-09 23:58:56 +00:00
parent bdf590fa30
commit f04acdc199
992 changed files with 2294 additions and 2125 deletions

View File

@@ -0,0 +1,263 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft;
import com.google.auto.service.AutoService;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.impl.AbstractComputerCraftAPI;
import dan200.computercraft.impl.ComputerCraftAPIService;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.platform.*;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandlerModifiable;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
@Override
public <T> Registries.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
throw new UnsupportedOperationException("Cannot query registry inside tests");
}
@Override
public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T>> registry) {
throw new UnsupportedOperationException("Cannot query registry inside tests");
}
@Override
public <K> ResourceLocation getRegistryKey(ResourceKey<Registry<K>> registry, K object) {
throw new UnsupportedOperationException("Cannot query registry inside tests");
}
@Override
public <K> K getRegistryObject(ResourceKey<Registry<K>> registry, ResourceLocation id) {
throw new UnsupportedOperationException("Cannot query registry inside tests");
}
@Override
public <T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> factory, Block block) {
throw new UnsupportedOperationException("Cannot create BlockEntityType inside tests");
}
@Override
public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) {
throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests");
}
@Override
public void sendToPlayer(NetworkMessage<ClientNetworkContext> message, ServerPlayer player) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToPlayers(NetworkMessage<ClientNetworkContext> message, Collection<ServerPlayer> players) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllPlayers(NetworkMessage<ClientNetworkContext> message, MinecraftServer server) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllAround(NetworkMessage<ClientNetworkContext> message, ServerLevel level, Vec3 pos, float distance) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public void sendToAllTracking(NetworkMessage<ClientNetworkContext> message, LevelChunk chunk) {
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public CreativeModeTab getCreativeTab() {
throw new UnsupportedOperationException("Cannot get creative tab inside tests");
}
@Override
public List<TagKey<Item>> getDyeTags() {
throw new UnsupportedOperationException("Cannot query tags inside tests");
}
@Override
public <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> createMenuType(Function<FriendlyByteBuf, T> reader, ContainerData.Factory<C, T> factory) {
throw new UnsupportedOperationException("Cannot create MenuType inside tests");
}
@Override
public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
throw new UnsupportedOperationException("Cannot open menu inside tests");
}
@Override
public ComponentAccess<IPeripheral> createPeripheralAccess(Consumer<Direction> invalidate) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public ComponentAccess<IWiredElement> createWiredElementAccess(Consumer<Direction> invalidate) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public boolean hasWiredElementIn(Level level, BlockPos pos, Direction direction) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public boolean onNotifyNeighbour(Level level, BlockPos pos, BlockState block, Direction direction) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public Collection<CreativeModeTab> getCreativeTabs(ItemStack stack) {
throw new UnsupportedOperationException("Cannot get creative tabs inside tests");
}
@Override
public RecipeIngredients getRecipeIngredients() {
throw new UnsupportedOperationException("Cannot query recipes inside tests");
}
@Override
public int getBurnTime(ItemStack stack) {
throw new UnsupportedOperationException("Cannot get burn time inside tests");
}
@Override
public ItemStack getCraftingRemainingItem(ItemStack stack) {
return new ItemStack(stack.getItem().getCraftingRemainingItem());
}
@Override
public ServerPlayer createFakePlayer(ServerLevel world, GameProfile name) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public boolean hasToolUsage(ItemStack stack) {
throw new UnsupportedOperationException("Cannot query item properties inside tests");
}
@Override
public InteractionResult canAttackEntity(ServerPlayer player, Entity entity) {
throw new UnsupportedOperationException("Cannot get burn time inside tests");
}
@Override
public boolean interactWithEntity(ServerPlayer player, Entity entity, Vec3 hitPos) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public ContainerTransfer.Slotted wrapContainer(Container container) {
throw new UnsupportedOperationException("Cannot wrap container");
}
@Nullable
@Override
public ContainerTransfer getContainer(ServerLevel level, BlockPos pos, Direction side) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public List<ItemStack> getRecipeRemainingItems(ServerPlayer player, Recipe<CraftingContainer> recipe, CraftingContainer container) {
throw new UnsupportedOperationException("Cannot query recipes inside tests");
}
@Override
public void onItemCrafted(ServerPlayer player, CraftingContainer container, ItemStack stack) {
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Override
public String getInstalledVersion() {
return "1.0";
}
@Nullable
@Override
public IMount createResourceMount(String domain, String subPath) {
throw new UnsupportedOperationException("Cannot create resource mount");
}
@Override
public void registerPeripheralProvider(IPeripheralProvider provider) {
throw new UnsupportedOperationException("Cannot register peripheral provider");
}
@Override
public void registerGenericCapability(Capability<?> capability) {
throw new UnsupportedOperationException("Cannot register generic capability");
}
@Override
public LazyOptional<IWiredElement> getWiredElementAt(BlockGetter world, BlockPos pos, Direction side) {
throw new UnsupportedOperationException("Cannot get wired element");
}
@Nullable
@Override
public <T> T tryGetRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id) {
throw new UnsupportedOperationException("Cannot query registries");
}
@Override
public IItemHandlerModifiable wrapContainerToItemHandler(Container container) {
throw new UnsupportedOperationException("Cannot wrap item handler ");
}
}

View File

@@ -0,0 +1,32 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.sound;
import io.netty.buffer.ByteBufAllocator;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DfpwmStreamTest {
@Test
public void testDecodesBytes() {
var stream = new DfpwmStream();
var input = ByteBufAllocator.DEFAULT.buffer();
input.writeBytes(new byte[]{ 43, -31, 33, 44, 30, -16, -85, 23, -3, -55, 46, -70, 68, -67, 74, -96, -68, 16, 94, -87, -5, 87, 11, -16, 19, 92, 85, -71, 126, 5, -84, 64, 17, -6, 85, -11, -1, -87, -12, 1, 85, -56, 33, -80, 82, 104, -93, 17, 126, 23, 91, -30, 37, -32, 117, -72, -58, 11, -76, 19, -108, 86, -65, -10, -1, -68, -25, 10, -46, 85, 124, -54, 15, -24, 43, -94, 117, 63, -36, 15, -6, 88, 87, -26, -83, 106, 41, 13, -28, -113, -10, -66, 119, -87, -113, 68, -55, 40, -107, 62, 20, 72, 3, -96, 114, -87, -2, 39, -104, 30, 20, 42, 84, 24, 47, 64, 43, 61, -35, 95, -65, 42, 61, 42, -50, 4, -9, 81 });
stream.push(input);
var buffer = stream.read(1024 + 1);
assertEquals(1024, buffer.remaining(), "Must have read 1024 bytes");
var decoded = new byte[]{ 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, -1, -2, -2, -1, 0, 1, 0, -1, -3, -5, -5, -5, -7, -9, -11, -11, -9, -9, -9, -9, -10, -12, -12, -10, -8, -6, -6, -8, -10, -12, -14, -16, -18, -17, -15, -12, -9, -6, -3, -2, -2, -2, -2, -2, -2, 0, 3, 6, 7, 7, 7, 4, 1, 1, 1, 1, 3, 5, 7, 9, 12, 15, 15, 12, 12, 12, 9, 9, 11, 12, 12, 14, 16, 17, 17, 17, 14, 11, 11, 11, 10, 12, 14, 14, 13, 13, 10, 9, 9, 7, 5, 4, 4, 4, 4, 4, 6, 8, 10, 10, 10, 10, 10, 10, 10, 9, 8, 8, 8, 7, 6, 4, 2, 0, 0, 0, 0, 0, -1, -1, 0, 1, 3, 3, 3, 3, 2, 0, -2, -2, -2, -3, -5, -7, -7, -5, -3, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 9, 8, 7, 6, 4, 2, 0, 0, 2, 4, 6, 8, 10, 10, 8, 7, 7, 5, 3, 1, -1, 0, 2, 4, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 9, 9, 9, 9, 9, 8, 7, 6, 5, 3, 1, 1, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -3, -3, -3, -3, -2, -3, -4, -4, -3, -4, -5, -6, -6, -5, -5, -4, -3, -2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 20, 17, 16, 16, 15, 15, 15, 15, 13, 13, 13, 13, 14, 15, 16, 18, 18, 16, 14, 12, 10, 8, 5, 5, 5, 4, 4, 4, 4, 4, 4, 2, 0, -2, -2, -2, -4, -4, -2, 0, 0, -2, -4, -6, -6, -6, -8, -10, -12, -14, -16, -15, -13, -12, -11, -11, -11, -11, -13, -13, -13, -13, -13, -14, -16, -18, -18, -18, -18, -16, -16, -16, -14, -13, -14, -15, -15, -14, -14, -12, -11, -12, -13, -13, -12, -13, -14, -15, -15, -13, -11, -9, -7, -5, -5, -5, -3, -1, -1, -1, -1, -3, -5, -5, -3, -3, -3, -1, -1, -1, -1, -3, -3, -3, -4, -6, -6, -4, -2, 0, 0, 0, 0, -2, -2, -2, -3, -5, -7, -9, -11, -13, -13, -11, -9, -7, -6, -6, -6, -6, -4, -2, -2, -4, -6, -8, -7, -5, -3, -2, -2, -2, -2, 0, 0, -2, -4, -4, -2, 0, 2, 2, 1, 1, -1, -3, -5, -7, -10, -10, -10, -10, -8, -7, -7, -5, -3, -2, -4, -4, -4, -6, -8, -10, -12, -12, -12, -12, -12, -14, -13, -13, -13, -11, -11, -11, -11, -11, -11, -11, -9, -7, -5, -3, -1, -1, -1, -1, -1, 1, 1, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 22, 19, 18, 20, 22, 24, 23, 22, 24, 26, 28, 27, 24, 23, 25, 28, 28, 28, 27, 26, 26, 23, 20, 17, 14, 14, 14, 11, 11, 11, 11, 13, 15, 16, 16, 16, 15, 15, 14, 14, 12, 10, 9, 11, 13, 15, 17, 17, 14, 13, 13, 12, 12, 10, 9, 11, 13, 15, 17, 19, 19, 16, 13, 10, 7, 4, 1, 1, 2, 2, 4, 7, 10, 13, 13, 13, 12, 12, 12, 9, 6, 6, 6, 3, 0, 0, 0, 0, 2, 3, 3, 3, 3, 5, 7, 7, 7, 9, 11, 13, 15, 18, 18, 15, 12, 9, 8, 10, 13, 13, 13, 15, 18, 21, 24, 27, 27, 23, 19, 15, 11, 10, 9, 9, 12, 16, 19, 22, 23, 19, 14, 13, 16, 16, 15, 15, 14, 17, 20, 20, 19, 19, 18, 17, 14, 13, 15, 15, 12, 11, 13, 16, 19, 19, 18, 20, 20, 19, 18, 18, 17, 17, 16, 16, 16, 15, 17, 17, 16, 16, 13, 12, 12, 11, 11, 9, 9, 9, 9, 11, 11, 9, 7, 5, 3, 1, 1, 1, -1, -1, 1, 3, 5, 7, 9, 11, 12, 9, 6, 6, 6, 6, 8, 8, 7, 9, 11, 13, 13, 12, 14, 16, 18, 20, 20, 20, 22, 24, 26, 25, 25, 27, 29, 28, 27, 26, 23, 22, 22, 21, 21, 20, 22, 24, 26, 28, 27, 24, 21, 21, 21, 18, 17, 17, 14, 11, 11, 11, 10, 10, 7, 6, 6, 4, 3, 5, 5, 3, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 0, 0, 1, 2, 3, 4, 3, 1, -1, -3, -3, -3, -3, -2, -3, -4, -6, -8, -10, -10, -10, -12, -12, -12, -12, -10, -10, -11, -12, -14, -16, -18, -20, -22, -24, -26, -28, -27, -27, -26, -26, -25, -25, -27, -26, -24, -22, -22, -22, -22, -24, -24, -24, -24, -23, -23, -22, -22, -21, -20, -19, -17, -15, -13, -11, -9, -7, -7, -9, -9, -9, -11, -13, -15, -17, -16, -14, -13, -15, -14, -14, -14, -12, -10, -8, -7, -9, -11, -13, -15, -14, -14, -13, -13, -15, -17, -19, -18, -18, -17, -17, -16, -16, -18, -20, -22, -21, -21, -21, -21, -21, -20, -21, -22, -24, -24, -22, -22, -24, -26, -25, -23, -21, -19, -18, -17, -17, -19, -21, -23, -25, -27, -29, -31, -30, -29, -28, -26, -25, -24, -24, -23, -23, -25, -24, -24, -24, -22, -20, -18, -18, -20, -20, -20, -20, -18, -16, -16, -16, -14, -12, -10, -8, -6, -4, -4, -4, -4, -4, -2, 0, 2, 4, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 4, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, -1, -2, -3, -4, -4, -2, 0, 0, 0, 1, 3, 5, 7, 7, 5, 3, 3, 3, 3, 3 };
for (var i = 0; i < 1024; i++) {
assertEquals((byte) (decoded[i] ^ 0x80), buffer.get(), "Bad element at " + i);
}
assertEquals(0, buffer.remaining(), "Must have read all bytes");
}
}

View File

@@ -0,0 +1,416 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.impl.network.wired;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNetwork;
import dan200.computercraft.api.network.wired.IWiredNetworkChange;
import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import static org.junit.jupiter.api.Assertions.*;
public class NetworkTest {
@Test
public void testConnect() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
IWiredNode
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
assertNotEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must be different");
assertNotEquals(aN.getNetwork(), cN.getNetwork(), "A's and C's network must be different");
assertNotEquals(bN.getNetwork(), cN.getNetwork(), "B's and C's network must be different");
assertTrue(aN.getNetwork().connect(aN, bN), "Must be able to add connection");
assertFalse(aN.getNetwork().connect(aN, bN), "Cannot add connection twice");
assertEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal");
assertEquals(Sets.newHashSet(aN, bN), nodes(aN.getNetwork()), "A's network should be A and B");
assertEquals(Sets.newHashSet("a", "b"), aE.allPeripherals().keySet(), "A's peripheral set should be A, B");
assertEquals(Sets.newHashSet("a", "b"), bE.allPeripherals().keySet(), "B's peripheral set should be A, B");
aN.getNetwork().connect(aN, cN);
assertEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal");
assertEquals(aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal");
assertEquals(Sets.newHashSet(aN, bN, cN), nodes(aN.getNetwork()), "A's network should be A, B and C");
assertEquals(Sets.newHashSet(bN, cN), neighbours(aN), "A's neighbour set should be B, C");
assertEquals(Sets.newHashSet(aN), neighbours(bN), "B's neighbour set should be A");
assertEquals(Sets.newHashSet(aN), neighbours(cN), "C's neighbour set should be A");
assertEquals(Sets.newHashSet("a", "b", "c"), aE.allPeripherals().keySet(), "A's peripheral set should be A, B, C");
assertEquals(Sets.newHashSet("a", "b", "c"), bE.allPeripherals().keySet(), "B's peripheral set should be A, B, C");
assertEquals(Sets.newHashSet("a", "b", "c"), cE.allPeripherals().keySet(), "C's peripheral set should be A, B, C");
}
@Test
public void testDisconnectNoChange() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
IWiredNode
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, bN);
aN.getNetwork().connect(aN, cN);
aN.getNetwork().connect(bN, cN);
aN.getNetwork().disconnect(aN, bN);
assertEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal");
assertEquals(aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal");
assertEquals(Sets.newHashSet(aN, bN, cN), nodes(aN.getNetwork()), "A's network should be A, B and C");
assertEquals(Sets.newHashSet("a", "b", "c"), aE.allPeripherals().keySet(), "A's peripheral set should be A, B, C");
assertEquals(Sets.newHashSet("a", "b", "c"), bE.allPeripherals().keySet(), "B's peripheral set should be A, B, C");
assertEquals(Sets.newHashSet("a", "b", "c"), cE.allPeripherals().keySet(), "C's peripheral set should be A, B, C");
}
@Test
public void testDisconnectLeaf() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
IWiredNode
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, bN);
aN.getNetwork().connect(aN, cN);
aN.getNetwork().disconnect(aN, bN);
assertNotEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal");
assertEquals(aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal");
assertEquals(Sets.newHashSet(aN, cN), nodes(aN.getNetwork()), "A's network should be A and C");
assertEquals(Sets.newHashSet(bN), nodes(bN.getNetwork()), "B's network should be B");
assertEquals(Sets.newHashSet("a", "c"), aE.allPeripherals().keySet(), "A's peripheral set should be A, C");
assertEquals(Sets.newHashSet("b"), bE.allPeripherals().keySet(), "B's peripheral set should be B");
assertEquals(Sets.newHashSet("a", "c"), cE.allPeripherals().keySet(), "C's peripheral set should be A, C");
}
@Test
public void testDisconnectSplit() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
aaE = new NetworkElement(null, null, "a_"),
bE = new NetworkElement(null, null, "b"),
bbE = new NetworkElement(null, null, "b_");
IWiredNode
aN = aE.getNode(),
aaN = aaE.getNode(),
bN = bE.getNode(),
bbN = bbE.getNode();
aN.getNetwork().connect(aN, aaN);
bN.getNetwork().connect(bN, bbN);
aN.getNetwork().connect(aN, bN);
aN.getNetwork().disconnect(aN, bN);
assertNotEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal");
assertEquals(aN.getNetwork(), aaN.getNetwork(), "A's and A_'s network must be equal");
assertEquals(bN.getNetwork(), bbN.getNetwork(), "B's and B_'s network must be equal");
assertEquals(Sets.newHashSet(aN, aaN), nodes(aN.getNetwork()), "A's network should be A and A_");
assertEquals(Sets.newHashSet(bN, bbN), nodes(bN.getNetwork()), "B's network should be B and B_");
assertEquals(Sets.newHashSet("a", "a_"), aE.allPeripherals().keySet(), "A's peripheral set should be A and A_");
assertEquals(Sets.newHashSet("b", "b_"), bE.allPeripherals().keySet(), "B's peripheral set should be B and B_");
}
@Test
public void testRemoveSingle() {
var aE = new NetworkElement(null, null, "a");
var aN = aE.getNode();
var network = aN.getNetwork();
assertFalse(aN.remove(), "Cannot remove node from an empty network");
assertEquals(network, aN.getNetwork(), "Networks are same before and after");
}
@Test
public void testRemoveLeaf() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
IWiredNode
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, bN);
aN.getNetwork().connect(aN, cN);
assertTrue(aN.getNetwork().remove(bN), "Must be able to remove node");
assertFalse(aN.getNetwork().remove(bN), "Cannot remove a second time");
assertNotEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal");
assertEquals(aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal");
assertEquals(Sets.newHashSet(aN, cN), nodes(aN.getNetwork()), "A's network should be A and C");
assertEquals(Sets.newHashSet(bN), nodes(bN.getNetwork()), "B's network should be B");
assertEquals(Sets.newHashSet("a", "c"), aE.allPeripherals().keySet(), "A's peripheral set should be A, C");
assertEquals(Sets.newHashSet(), bE.allPeripherals().keySet(), "B's peripheral set should be empty");
assertEquals(Sets.newHashSet("a", "c"), cE.allPeripherals().keySet(), "C's peripheral set should be A, C");
}
@Test
public void testRemoveSplit() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
aaE = new NetworkElement(null, null, "a_"),
bE = new NetworkElement(null, null, "b"),
bbE = new NetworkElement(null, null, "b_"),
cE = new NetworkElement(null, null, "c");
IWiredNode
aN = aE.getNode(),
aaN = aaE.getNode(),
bN = bE.getNode(),
bbN = bbE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, aaN);
bN.getNetwork().connect(bN, bbN);
cN.getNetwork().connect(aN, cN);
cN.getNetwork().connect(bN, cN);
cN.getNetwork().remove(cN);
assertNotEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal");
assertEquals(aN.getNetwork(), aaN.getNetwork(), "A's and A_'s network must be equal");
assertEquals(bN.getNetwork(), bbN.getNetwork(), "B's and B_'s network must be equal");
assertEquals(Sets.newHashSet(aN, aaN), nodes(aN.getNetwork()), "A's network should be A and A_");
assertEquals(Sets.newHashSet(bN, bbN), nodes(bN.getNetwork()), "B's network should be B and B_");
assertEquals(Sets.newHashSet(cN), nodes(cN.getNetwork()), "C's network should be C");
assertEquals(Sets.newHashSet("a", "a_"), aE.allPeripherals().keySet(), "A's peripheral set should be A and A_");
assertEquals(Sets.newHashSet("b", "b_"), bE.allPeripherals().keySet(), "B's peripheral set should be B and B_");
assertEquals(Sets.newHashSet(), cE.allPeripherals().keySet(), "C's peripheral set should be empty");
}
private static final int BRUTE_SIZE = 16;
private static final int TOGGLE_CONNECTION_TIMES = 5;
private static final int TOGGLE_NODE_TIMES = 5;
@Test
@Disabled("Takes a long time to run, mostly for stress testing")
public void testLarge() {
var grid = new Grid<IWiredNode>(BRUTE_SIZE);
grid.map((existing, pos) -> new NetworkElement(null, null, "n_" + pos).getNode());
// Test connecting
{
var start = System.nanoTime();
grid.forEach((existing, pos) -> {
for (var facing : DirectionUtil.FACINGS) {
var offset = pos.relative(facing);
if (offset.getX() > BRUTE_SIZE / 2 == pos.getX() > BRUTE_SIZE / 2) {
var other = grid.get(offset);
if (other != null) existing.getNetwork().connect(existing, other);
}
}
});
var end = System.nanoTime();
System.out.printf("Connecting %s³ nodes took %s seconds\n", BRUTE_SIZE, (end - start) * 1e-9);
}
// Test toggling
{
var left = grid.get(new BlockPos(BRUTE_SIZE / 2, 0, 0));
var right = grid.get(new BlockPos(BRUTE_SIZE / 2 + 1, 0, 0));
assertNotEquals(left.getNetwork(), right.getNetwork());
var start = System.nanoTime();
for (var i = 0; i < TOGGLE_CONNECTION_TIMES; i++) {
left.getNetwork().connect(left, right);
left.getNetwork().disconnect(left, right);
}
var end = System.nanoTime();
System.out.printf("Toggling connection %s times took %s seconds\n", TOGGLE_CONNECTION_TIMES, (end - start) * 1e-9);
}
{
var left = grid.get(new BlockPos(BRUTE_SIZE / 2, 0, 0));
var right = grid.get(new BlockPos(BRUTE_SIZE / 2 + 1, 0, 0));
var centre = new NetworkElement(null, null, "c").getNode();
assertNotEquals(left.getNetwork(), right.getNetwork());
var start = System.nanoTime();
for (var i = 0; i < TOGGLE_NODE_TIMES; i++) {
left.getNetwork().connect(left, centre);
right.getNetwork().connect(right, centre);
left.getNetwork().remove(centre);
}
var end = System.nanoTime();
System.out.printf("Toggling node %s times took %s seconds\n", TOGGLE_NODE_TIMES, (end - start) * 1e-9);
}
}
private static final class NetworkElement implements IWiredElement {
private final Level world;
private final Vec3 position;
private final String id;
private final IWiredNode node;
private final Map<String, IPeripheral> localPeripherals = Maps.newHashMap();
private final Map<String, IPeripheral> remotePeripherals = Maps.newHashMap();
private NetworkElement(Level world, Vec3 position, String id) {
this.world = world;
this.position = position;
this.id = id;
this.node = ComputerCraftAPI.createWiredNodeForElement(this);
this.addPeripheral(id);
}
@Override
public Level getLevel() {
return world;
}
@Override
public Vec3 getPosition() {
return position;
}
@Override
public String getSenderID() {
return id;
}
@Override
public String toString() {
return "NetworkElement{" + id + "}";
}
@Override
public IWiredNode getNode() {
return node;
}
@Override
public void networkChanged(IWiredNetworkChange change) {
remotePeripherals.keySet().removeAll(change.peripheralsRemoved().keySet());
remotePeripherals.putAll(change.peripheralsAdded());
}
public NetworkElement addPeripheral(String name) {
localPeripherals.put(name, new NetworkPeripheral());
getNode().updatePeripherals(localPeripherals);
return this;
}
public Map<String, IPeripheral> allPeripherals() {
return remotePeripherals;
}
}
private static class NetworkPeripheral implements IPeripheral {
@Override
public String getType() {
return "test";
}
@Override
public boolean equals(@Nullable IPeripheral other) {
return this == other;
}
}
private static class Grid<T> {
private final int size;
private final T[] box;
@SuppressWarnings("unchecked")
Grid(int size) {
this.size = size;
this.box = (T[]) new Object[size * size * size];
}
public T get(BlockPos pos) {
int x = pos.getX(), y = pos.getY(), z = pos.getZ();
return x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size
? box[x * size * size + y * size + z]
: null;
}
public void forEach(BiConsumer<T, BlockPos> transform) {
for (var x = 0; x < size; x++) {
for (var y = 0; y < size; y++) {
for (var z = 0; z < size; z++) {
transform.accept(box[x * size * size + y * size + z], new BlockPos(x, y, z));
}
}
}
}
public void map(BiFunction<T, BlockPos, T> transform) {
for (var x = 0; x < size; x++) {
for (var y = 0; y < size; y++) {
for (var z = 0; z < size; z++) {
box[x * size * size + y * size + z] = transform.apply(box[x * size * size + y * size + z], new BlockPos(x, y, z));
}
}
}
}
}
private static Set<WiredNode> nodes(IWiredNetwork network) {
return ((WiredNetwork) network).nodes;
}
private static Set<WiredNode> neighbours(IWiredNode node) {
return ((WiredNode) node).neighbours;
}
}

View File

@@ -0,0 +1,77 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.core;
import dan200.computercraft.api.filesystem.IMount;
import net.minecraft.Util;
import net.minecraft.server.packs.FolderPackResources;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.util.Unit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.*;
public class ResourceMountTest {
private IMount mount;
@BeforeEach
public void before() {
var manager = new ReloadableResourceManager(PackType.SERVER_DATA);
var done = new CompletableFuture<Unit>();
manager.createReload(Util.backgroundExecutor(), Util.backgroundExecutor(), done, List.of(
new FolderPackResources(new File("../core/src/main/resources"))
));
mount = ResourceMount.get("computercraft", "lua/rom", manager);
}
@Test
public void testList() throws IOException {
List<String> files = new ArrayList<>();
mount.list("", files);
files.sort(Comparator.naturalOrder());
assertEquals(
Arrays.asList("apis", "autorun", "help", "modules", "motd.txt", "programs", "startup.lua"),
files
);
}
@Test
public void testExists() throws IOException {
assertTrue(mount.exists(""));
assertTrue(mount.exists("startup.lua"));
assertTrue(mount.exists("programs/fun/advanced/paint.lua"));
assertFalse(mount.exists("programs/fun/advance/paint.lua"));
assertFalse(mount.exists("programs/fun/advanced/paint.lu"));
}
@Test
public void testIsDir() throws IOException {
assertTrue(mount.isDirectory(""));
}
@Test
public void testIsFile() throws IOException {
assertFalse(mount.isDirectory("startup.lua"));
}
@Test
public void testSize() throws IOException {
assertNotEquals(mount.getSize("startup.lua"), 0);
}
}

View File

@@ -0,0 +1,108 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.terminal;
import dan200.computercraft.api.lua.LuaValues;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.test.core.CallCounter;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import org.junit.jupiter.api.Test;
import static dan200.computercraft.test.core.terminal.TerminalMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
class NetworkedTerminalTest {
@Test
void testPacketBufferRoundtrip() {
var writeTerminal = new NetworkedTerminal(2, 1, true);
blit(writeTerminal, "hi", "11", "ee");
writeTerminal.setCursorPos(2, 5);
writeTerminal.setTextColour(3);
writeTerminal.setBackgroundColour(5);
var packetBuffer = new FriendlyByteBuf(Unpooled.buffer());
writeTerminal.write(packetBuffer);
var callCounter = new CallCounter();
var readTerminal = new NetworkedTerminal(2, 1, true, callCounter);
packetBuffer.writeBytes(packetBuffer);
readTerminal.read(packetBuffer);
assertThat(readTerminal, allOf(
textMatches(new String[]{ "hi", }),
textColourMatches(new String[]{ "11", }),
backgroundColourMatches(new String[]{ "ee", })
));
assertEquals(2, readTerminal.getCursorX());
assertEquals(5, readTerminal.getCursorY());
assertEquals(3, readTerminal.getTextColour());
assertEquals(5, readTerminal.getBackgroundColour());
callCounter.assertCalledTimes(1);
}
@Test
void testNbtRoundtrip() {
var writeTerminal = new NetworkedTerminal(10, 5, true);
blit(writeTerminal, "hi", "11", "ee");
writeTerminal.setCursorPos(2, 5);
writeTerminal.setTextColour(3);
writeTerminal.setBackgroundColour(5);
var nbt = new CompoundTag();
writeTerminal.writeToNBT(nbt);
var callCounter = new CallCounter();
var readTerminal = new NetworkedTerminal(2, 1, true, callCounter);
readTerminal.readFromNBT(nbt);
assertThat(readTerminal, allOf(
textMatches(new String[]{ "hi", }),
textColourMatches(new String[]{ "11", }),
backgroundColourMatches(new String[]{ "ee", })
));
assertEquals(2, readTerminal.getCursorX());
assertEquals(5, readTerminal.getCursorY());
assertEquals(3, readTerminal.getTextColour());
assertEquals(5, readTerminal.getBackgroundColour());
callCounter.assertCalledTimes(1);
}
@Test
void testReadWriteNBTEmpty() {
var terminal = new NetworkedTerminal(0, 0, true);
var nbt = new CompoundTag();
terminal.writeToNBT(nbt);
var callCounter = new CallCounter();
terminal = new NetworkedTerminal(0, 1, true, callCounter);
terminal.readFromNBT(nbt);
assertThat(terminal, allOf(
textMatches(new String[]{ "", }),
textColourMatches(new String[]{ "", }),
backgroundColourMatches(new String[]{ "", })
));
assertEquals(0, terminal.getCursorX());
assertEquals(0, terminal.getCursorY());
assertEquals(0, terminal.getTextColour());
assertEquals(15, terminal.getBackgroundColour());
callCounter.assertCalledTimes(1);
}
private static void blit(Terminal terminal, String text, String fg, String bg) {
terminal.blit(LuaValues.encode(text), LuaValues.encode(fg), LuaValues.encode(bg));
}
}

View File

@@ -0,0 +1,75 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.terminal;
import dan200.computercraft.core.terminal.Terminal;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import org.junit.jupiter.api.RepeatedTest;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests {@link TerminalState} round tripping works as expected.
*/
public class TerminalStateTest {
@RepeatedTest(5)
public void testCompressed() {
var terminal = randomTerminal();
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
new TerminalState(terminal, true).write(buffer);
checkEqual(terminal, read(buffer));
assertEquals(0, buffer.readableBytes());
}
@RepeatedTest(5)
public void testUncompressed() {
var terminal = randomTerminal();
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
new TerminalState(terminal, false).write(buffer);
checkEqual(terminal, read(buffer));
assertEquals(0, buffer.readableBytes());
}
private static NetworkedTerminal randomTerminal() {
var random = new Random();
var terminal = new NetworkedTerminal(10, 5, true);
for (var y = 0; y < terminal.getHeight(); y++) {
var buffer = terminal.getLine(y);
for (var x = 0; x < buffer.length(); x++) buffer.setChar(x, (char) (random.nextInt(26) + 65));
}
return terminal;
}
private static void checkEqual(Terminal expected, Terminal actual) {
assertNotNull(expected, "Expected cannot be null");
assertNotNull(actual, "Actual cannot be null");
assertEquals(expected.getHeight(), actual.getHeight(), "Heights must match");
assertEquals(expected.getWidth(), actual.getWidth(), "Widths must match");
for (var y = 0; y < expected.getHeight(); y++) {
assertEquals(expected.getLine(y).toString(), actual.getLine(y).toString());
}
}
private static NetworkedTerminal read(FriendlyByteBuf buffer) {
var state = new TerminalState(buffer);
assertTrue(state.colour);
if (!state.hasTerminal()) return null;
var other = new NetworkedTerminal(state.width, state.height, true);
state.apply(other);
return other;
}
}

View File

@@ -0,0 +1,143 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.test.core.ArbitraryByteBuffer;
import dan200.computercraft.test.shared.FakeContainer;
import dan200.computercraft.test.shared.WithMinecraft;
import io.netty.buffer.Unpooled;
import net.jqwik.api.*;
import net.minecraft.network.FriendlyByteBuf;
import org.hamcrest.Matcher;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static dan200.computercraft.shared.network.server.UploadFileMessage.*;
import static dan200.computercraft.test.core.ByteBufferMatcher.bufferEqual;
import static dan200.computercraft.test.core.ContramapMatcher.contramap;
import static dan200.computercraft.test.core.CustomMatchers.containsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@WithMinecraft
public class UploadFileMessageTest {
/**
* Sends packets on a roundtrip, ensuring that their contents are reassembled on the other end.
*
* @param sentFiles The files to send.
*/
@Property(tries = 200)
@Tag("slow")
public void testRoundTrip(@ForAll("fileUploads") List<FileUpload> sentFiles) {
WithMinecraft.Setup.bootstrap(); // @Property doesn't run test lifecycle methods.
var receivedFiles = receive(roundtripPackets(send(sentFiles)));
assertThat(receivedFiles, containsWith(sentFiles, UploadFileMessageTest::uploadEqual));
}
/**
* "Send" our file uploads, converting them to a list of packets.
*
* @param uploads The files to send.
* @return The list of packets.
*/
private static List<UploadFileMessage> send(List<FileUpload> uploads) {
List<UploadFileMessage> packets = new ArrayList<>();
UploadFileMessage.send(new FakeContainer(), uploads, packets::add);
return packets;
}
/**
* Write our packets to a buffer and then read them out again.
*
* @param packets The packets to roundtrip.
* @return The
*/
private static List<UploadFileMessage> roundtripPackets(List<UploadFileMessage> packets) {
return packets.stream().map(packet -> {
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
packet.toBytes(buffer);
// We include things like file size in the packet, but not in the count, so grant a slightly larger threshold.
assertThat("Packet is too large", buffer.writerIndex(), lessThanOrEqualTo(MAX_PACKET_SIZE + 128));
if ((packet.flag & FLAG_LAST) == 0) {
var expectedSize = (packet.flag & FLAG_FIRST) != 0
? MAX_PACKET_SIZE - MAX_FILE_NAME * MAX_FILES
: MAX_PACKET_SIZE;
assertThat(
"Non-final packets should be efficiently packed", buffer.writerIndex(), greaterThanOrEqualTo(expectedSize)
);
}
var result = new UploadFileMessage(buffer);
buffer.release();
assertEquals(0, buffer.refCnt(), "Buffer should have no references");
return result;
}).collect(Collectors.toList());
}
/**
* "Receive" our upload packets.
*
* @param packets The packets to receive. Note that this will clobber the {@link FileUpload}s in the first packet,
* so you may want to copy (or {@linkplain #roundtripPackets(List) roundtrip} first.
* @return The consumed file uploads.
*/
private static List<FileUpload> receive(List<UploadFileMessage> packets) {
var files = packets.get(0).files;
for (var i = 0; i < packets.size(); i++) {
var packet = packets.get(i);
var isFirst = i == 0;
var isLast = i == packets.size() - 1;
assertEquals(isFirst, (packet.flag & FLAG_FIRST) != 0, "FLAG_FIRST");
assertEquals(isLast, (packet.flag & FLAG_LAST) != 0, "FLAG_LAST");
for (var slice : packet.slices) slice.apply(files);
}
return files;
}
@Provide
Arbitrary<FileUpload> fileUpload() {
return Combinators.combine(
Arbitraries.oneOf(Arrays.asList(
// 1.16 doesn't correctly handle unicode file names. We'll be generous in our tests here.
Arbitraries.strings().ofMinLength(1).ascii().ofMaxLength(MAX_FILE_NAME),
Arbitraries.strings().ofMinLength(1).ofMaxLength(MAX_FILE_NAME / 4)
)),
ArbitraryByteBuffer.bytes().ofMaxSize(MAX_SIZE)
).as(UploadFileMessageTest::file);
}
@Provide
Arbitrary<List<FileUpload>> fileUploads() {
return fileUpload().list()
.ofMinSize(1).ofMaxSize(MAX_FILES)
.filter(us -> us.stream().mapToInt(u -> u.getBytes().remaining()).sum() <= MAX_SIZE);
}
private static FileUpload file(String name, ByteBuffer buffer) {
var checksum = FileUpload.getDigest(buffer);
if (checksum == null) throw new IllegalStateException("Failed to compute checksum");
return new FileUpload(name, buffer, checksum);
}
public static Matcher<FileUpload> uploadEqual(FileUpload upload) {
return allOf(
contramap(equalTo(upload.getName()), "name", FileUpload::getName),
contramap(equalTo(upload.getChecksum()), "checksum", FileUpload::getChecksum),
contramap(bufferEqual(upload.getBytes()), "bytes", FileUpload::getBytes)
);
}
}

View File

@@ -0,0 +1,36 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.ObjectLuaTable;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
class DfpwmStateTest {
@Test
public void testEncoder() throws LuaException {
var input = new int[]{ 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3 };
Map<Object, Object> inputTbl = new HashMap<>();
for (var i = 0; i < input.length; i++) inputTbl.put((double) (i + 1), input[i]);
var state = new DfpwmState();
state.pushBuffer(new ObjectLuaTable(inputTbl), input.length, Optional.empty());
var result = state.pullPending(0);
var contents = new byte[result.remaining()];
result.get(contents);
assertArrayEquals(
new byte[]{ 87, 74, 42, -91, -92, -108, 84, -87, -86, 86, -83, 90, -83, -43, 90, -85, -42, 106, -43, -86, 106, -107, 42, -107, 74, -87, 74, -91, 74, -91, -86, -86, 106, 85, 107, -83, 106, -83, -83, 86, -75, -86, 42, 85, -107, 82, 41, -91, 82, 74, 41, -107, -86, -44, -86, 86, -75, 106, -83, -75, -86, -75, 90, -83, -86, -86, -86, 82, -91, 74, -107, -86, 82, -87, 82, 85, 85, 85, -83, 86, -75, -86, -43, 90, -83, 90, 85, 85, -107, 42, -91, 82, -86, 82, 74, 41, 85, -87, -86, -86, 106, -75, 90, -83, 86, -85, 106, -43, 106, 85, 85, 85, 85, -107, 42, 85, -86, 42, -107, -86, -86, -86, -86, 106, -75, -86, 86, -85 },
contents
);
}
}