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

Merge branch 'mc-1.20.x' into mc-1.20.y

This commit is contained in:
Jonathan Coates
2024-03-22 21:23:49 +00:00
134 changed files with 3783 additions and 1267 deletions

View File

@@ -4,6 +4,7 @@
package dan200.computercraft.client.sound;
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
import org.junit.jupiter.api.Test;
import java.nio.ByteBuffer;
@@ -16,7 +17,7 @@ public class DfpwmStreamTest {
var stream = new DfpwmStream();
var input = ByteBuffer.wrap(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);
stream.push(new EncodedAudio(0, 0, false, input));
var buffer = stream.read(1024 + 1);
assertEquals(1024, buffer.remaining(), "Must have read 1024 bytes");

View File

@@ -0,0 +1,183 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.impl.network.wired;
import dan200.computercraft.api.network.wired.WiredNetwork;
import dan200.computercraft.impl.network.wired.NetworkTest.NetworkElement;
import dan200.computercraft.shared.util.DirectionUtil;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.core.BlockPos;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class NetworkBenchmark {
private static final int BRUTE_SIZE = 16;
public static void main(String[] args) throws RunnerException {
var opts = new OptionsBuilder()
.include(NetworkBenchmark.class.getName() + "\\..*")
.warmupIterations(2)
.measurementIterations(5)
.forks(1)
.build();
new Runner(opts).run();
}
@Benchmark
@Warmup(time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(time = 2, timeUnit = TimeUnit.SECONDS)
public void removeEveryNode(ConnectedGrid grid) {
grid.grid.forEach((node, pos) -> node.remove());
}
@Benchmark
@Warmup(time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(time = 2, timeUnit = TimeUnit.SECONDS)
public void connectAndDisconnect(SplitGrid connectedGrid) {
WiredNodeImpl left = connectedGrid.left, right = connectedGrid.right;
assertNotEquals(left.getNetwork(), right.getNetwork());
left.connectTo(right);
assertEquals(left.getNetwork(), right.getNetwork());
left.disconnectFrom(right);
assertNotEquals(left.getNetwork(), right.getNetwork());
}
@Benchmark
@Warmup(time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(time = 2, timeUnit = TimeUnit.SECONDS)
public void connectAndRemove(SplitGrid connectedGrid) {
WiredNodeImpl left = connectedGrid.left, right = connectedGrid.right, centre = connectedGrid.centre;
assertNotEquals(left.getNetwork(), right.getNetwork());
centre.connectTo(left);
centre.connectTo(right);
assertEquals(left.getNetwork(), right.getNetwork());
centre.remove();
assertNotEquals(left.getNetwork(), right.getNetwork());
}
/**
* Create a grid where all nodes are connected to their neighbours.
*/
@State(Scope.Thread)
public static class ConnectedGrid {
Grid<WiredNodeImpl> grid;
@Setup(Level.Invocation)
public void setup() {
var grid = this.grid = new Grid<>(BRUTE_SIZE);
grid.map((existing, pos) -> new NetworkElement("n_" + pos, pos.getX() == pos.getY() && pos.getY() == pos.getZ()).getNode());
// Connect every node
grid.forEach((node, pos) -> {
for (var facing : DirectionUtil.FACINGS) {
var other = grid.get(pos.relative(facing));
if (other != null) node.connectTo(other);
}
});
var networks = countNetworks(grid);
if (networks.size() != 1) throw new AssertionError("Expected exactly one network.");
}
}
/**
* Create a grid where the nodes at {@code x < BRUTE_SIZE/2} and {@code x >= BRUTE_SIZE/2} are in separate networks,
* but otherwise connected to their neighbours.
*/
@State(Scope.Thread)
public static class SplitGrid {
Grid<WiredNodeImpl> grid;
WiredNodeImpl left, right, centre;
@Setup
public void setup() {
var grid = this.grid = new Grid<>(BRUTE_SIZE);
grid.map((existing, pos) -> new NetworkElement("n_" + pos, pos.getX() == pos.getY() && pos.getY() == pos.getZ()).getNode());
// Connect every node
grid.forEach((node, 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) node.connectTo(other);
}
}
});
var networks = countNetworks(grid);
if (networks.size() != 2) throw new AssertionError("Expected exactly two networks.");
for (var network : networks.object2IntEntrySet()) {
if (network.getIntValue() != BRUTE_SIZE * BRUTE_SIZE * (BRUTE_SIZE / 2)) {
throw new AssertionError("Network is the wrong size");
}
}
left = Objects.requireNonNull(grid.get(new BlockPos(BRUTE_SIZE / 2 - 1, 0, 0)));
right = Objects.requireNonNull(grid.get(new BlockPos(BRUTE_SIZE / 2, 0, 0)));
centre = new NetworkElement("c", false).getNode();
}
}
private static Object2IntMap<WiredNetwork> countNetworks(Grid<WiredNodeImpl> grid) {
Object2IntMap<WiredNetwork> networks = new Object2IntOpenHashMap<>();
grid.forEach((node, pos) -> networks.put(node.network, networks.getOrDefault(node.network, 0) + 1));
return networks;
}
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));
}
}
}
}
}
}

View File

@@ -9,19 +9,14 @@ import dan200.computercraft.api.network.wired.WiredNetwork;
import dan200.computercraft.api.network.wired.WiredNetworkChange;
import dan200.computercraft.api.network.wired.WiredNode;
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.HashMap;
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.*;
@@ -29,11 +24,11 @@ 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");
aE = new NetworkElement("a"),
bE = new NetworkElement("b"),
cE = new NetworkElement("c");
WiredNode
WiredNodeImpl
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
@@ -42,8 +37,8 @@ public class NetworkTest {
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");
assertTrue(aN.connectTo(bN), "Must be able to add connection");
assertFalse(aN.connectTo(bN), "Cannot add connection twice");
assertEquals(aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal");
assertEquals(Set.of(aN, bN), nodes(aN.getNetwork()), "A's network should be A and B");
@@ -51,7 +46,7 @@ public class NetworkTest {
assertEquals(Set.of("a", "b"), aE.allPeripherals().keySet(), "A's peripheral set should be A, B");
assertEquals(Set.of("a", "b"), bE.allPeripherals().keySet(), "B's peripheral set should be A, B");
aN.getNetwork().connect(aN, cN);
aN.connectTo(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");
@@ -69,20 +64,20 @@ public class NetworkTest {
@Test
public void testDisconnectNoChange() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
aE = new NetworkElement("a"),
bE = new NetworkElement("b"),
cE = new NetworkElement("c");
WiredNode
WiredNodeImpl
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.connectTo(bN);
aN.connectTo(cN);
bN.connectTo(cN);
aN.getNetwork().disconnect(aN, bN);
aN.disconnectFrom(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");
@@ -96,19 +91,19 @@ public class NetworkTest {
@Test
public void testDisconnectLeaf() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
aE = new NetworkElement("a"),
bE = new NetworkElement("b"),
cE = new NetworkElement("c");
WiredNode
WiredNodeImpl
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, bN);
aN.getNetwork().connect(aN, cN);
aN.connectTo(bN);
aN.connectTo(cN);
aN.getNetwork().disconnect(aN, bN);
aN.disconnectFrom(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");
@@ -123,23 +118,23 @@ public class NetworkTest {
@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_");
aE = new NetworkElement("a"),
aaE = new NetworkElement("a_"),
bE = new NetworkElement("b"),
bbE = new NetworkElement("b_");
WiredNode
WiredNodeImpl
aN = aE.getNode(),
aaN = aaE.getNode(),
bN = bE.getNode(),
bbN = bbE.getNode();
aN.getNetwork().connect(aN, aaN);
bN.getNetwork().connect(bN, bbN);
aN.connectTo(aaN);
bN.connectTo(bbN);
aN.getNetwork().connect(aN, bN);
aN.connectTo(bN);
aN.getNetwork().disconnect(aN, bN);
aN.disconnectFrom(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");
@@ -154,7 +149,7 @@ public class NetworkTest {
@Test
public void testRemoveSingle() {
var aE = new NetworkElement(null, null, "a");
var aE = new NetworkElement("a");
var aN = aE.getNode();
var network = aN.getNetwork();
@@ -165,20 +160,20 @@ public class NetworkTest {
@Test
public void testRemoveLeaf() {
NetworkElement
aE = new NetworkElement(null, null, "a"),
bE = new NetworkElement(null, null, "b"),
cE = new NetworkElement(null, null, "c");
aE = new NetworkElement("a"),
bE = new NetworkElement("b"),
cE = new NetworkElement("c");
WiredNode
WiredNodeImpl
aN = aE.getNode(),
bN = bE.getNode(),
cN = cE.getNode();
aN.getNetwork().connect(aN, bN);
aN.getNetwork().connect(aN, cN);
aN.connectTo(bN);
aN.connectTo(cN);
assertTrue(aN.getNetwork().remove(bN), "Must be able to remove node");
assertFalse(aN.getNetwork().remove(bN), "Cannot remove a second time");
assertTrue(bN.remove(), "Must be able to remove node");
assertFalse(bN.remove(), "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");
@@ -194,26 +189,26 @@ public class NetworkTest {
@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");
aE = new NetworkElement("a"),
aaE = new NetworkElement("a_"),
bE = new NetworkElement("b"),
bbE = new NetworkElement("b_"),
cE = new NetworkElement("c");
WiredNode
WiredNodeImpl
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);
aN.connectTo(aaN);
bN.connectTo(bbN);
cN.getNetwork().connect(aN, cN);
cN.getNetwork().connect(bN, cN);
cN.connectTo(aN);
cN.connectTo(bN);
cN.getNetwork().remove(cN);
cN.remove();
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");
@@ -228,96 +223,30 @@ public class NetworkTest {
assertEquals(Set.of(), 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<WiredNode>(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 WiredElement {
private final Level world;
private final Vec3 position;
static final class NetworkElement implements WiredElement {
private final String id;
private final WiredNode node;
private final WiredNodeImpl node;
private final Map<String, IPeripheral> localPeripherals = new HashMap<>();
private final Map<String, IPeripheral> remotePeripherals = new HashMap<>();
private NetworkElement(Level world, Vec3 position, String id) {
this.world = world;
this.position = position;
NetworkElement(String id) {
this(id, true);
}
NetworkElement(String id, boolean peripheral) {
this.id = id;
this.node = new WiredNodeImpl(this);
this.addPeripheral(id);
if (peripheral) addPeripheral(id);
}
@Override
public Level getLevel() {
return world;
throw new IllegalStateException("Unexpected call to getLevel()");
}
@Override
public Vec3 getPosition() {
return position;
throw new IllegalStateException("Unexpected call to getPosition()");
}
@Override
@@ -331,7 +260,7 @@ public class NetworkTest {
}
@Override
public WiredNode getNode() {
public WiredNodeImpl getNode() {
return node;
}
@@ -364,45 +293,6 @@ public class NetworkTest {
}
}
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<WiredNodeImpl> nodes(WiredNetwork network) {
return ((WiredNetworkImpl) network).nodes;
}

View File

@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.command.arguments;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import dan200.computercraft.shared.command.Exceptions;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.test.core.ReplaceUnderscoresDisplayNameGenerator;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import java.util.UUID;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@DisplayNameGeneration(ReplaceUnderscoresDisplayNameGenerator.class)
class ComputerSelectorTest {
@ParameterizedTest(name = "{0}")
@MethodSource("getArgumentTestCases")
public void Parse_basic_inputs(String input, ComputerSelector expected) throws CommandSyntaxException {
assertEquals(expected, ComputerSelector.parse(new StringReader(input)));
}
public static Arguments[] getArgumentTestCases() {
return new Arguments[]{
// Legacy selectors
Arguments.of("@some_label", new ComputerSelector("@some_label", OptionalInt.empty(), null, OptionalInt.empty(), "some_label", null, null, null)),
Arguments.of("~normal", new ComputerSelector("~normal", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
Arguments.of("#123", new ComputerSelector("#123", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
Arguments.of("123", new ComputerSelector("123", OptionalInt.of(123), null, OptionalInt.empty(), null, null, null, null)),
// New selectors
Arguments.of("@c[]", new ComputerSelector("@c[]", OptionalInt.empty(), null, OptionalInt.empty(), null, null, null, null)),
Arguments.of("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", new ComputerSelector("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", OptionalInt.empty(), UUID.fromString("5e18f505-62f7-46f8-83f3-792f03224724"), OptionalInt.empty(), null, null, null, null)),
Arguments.of("@c[id=123]", new ComputerSelector("@c[id=123]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
Arguments.of("@c[label=\"foo\"]", new ComputerSelector("@c[label=\"foo\"]", OptionalInt.empty(), null, OptionalInt.empty(), "foo", null, null, null)),
Arguments.of("@c[family=normal]", new ComputerSelector("@c[family=normal]", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
// Complex selectors
Arguments.of("@c[ id = 123 , ]", new ComputerSelector("@c[ id = 123 , ]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
Arguments.of("@c[id=123,family=normal]", new ComputerSelector("@c[id=123,family=normal]", OptionalInt.empty(), null, OptionalInt.of(123), null, ComputerFamily.NORMAL, null, null)),
};
}
@Test
public void Fails_on_repeated_options() {
var error = assertThrows(CommandSyntaxException.class, () -> ComputerSelector.parse(new StringReader("@c[id=1, id=2]")));
assertEquals(Exceptions.ERROR_INAPPLICABLE_OPTION, error.getType());
}
@Test
public void Complete_selector_components() {
assertEquals(List.of(new Suggestion(StringRange.between(0, 1), "@c[")), suggest("@"));
assertThat(suggest("@c["), hasItem(
new Suggestion(StringRange.at(3), "family", ComputerSelector.options().get("family").tooltip())
));
assertEquals(List.of(new Suggestion(StringRange.at(9), "=")), suggest("@c[family"));
assertEquals(List.of(new Suggestion(StringRange.at(16), ",")), suggest("@c[family=normal"));
}
@Test
public void Complete_selector_family() {
assertThat(suggest("@c[family="), containsInAnyOrder(
new Suggestion(StringRange.at(10), "normal"),
new Suggestion(StringRange.at(10), "advanced"),
new Suggestion(StringRange.at(10), "command")
));
assertThat(suggest("@c[family=n"), contains(
new Suggestion(StringRange.between(10, 11), "normal")
));
}
private List<Suggestion> suggest(String input) {
var context = new CommandContext<>(new Object(), "", Map.of(), null, null, List.of(), StringRange.at(0), null, null, false);
return ComputerSelector.suggest(context, new SuggestionsBuilder(input, 0)).getNow(null).getList();
}
}

View File

@@ -23,7 +23,7 @@ class DfpwmStateTest {
var state = new DfpwmState();
state.pushBuffer(new ObjectLuaTable(inputTbl), input.length, Optional.empty());
var result = state.pullPending(0);
var result = state.pullPending(0).audio();
var contents = new byte[result.remaining()];
result.get(contents);

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.test.core.ArbitraryByteBuffer;
import io.netty.buffer.Unpooled;
import net.jqwik.api.*;
import net.minecraft.network.FriendlyByteBuf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
class EncodedAudioTest {
/**
* Sends the audio on a roundtrip, ensuring that its contents are reassembled on the other end.
*
* @param audio The message to send.
*/
@Property
public void testRoundTrip(@ForAll("audio") EncodedAudio audio) {
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
audio.write(buffer);
var converted = EncodedAudio.read(buffer);
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");
assertThat("Messages are equal", converted, equalTo(converted));
}
@Provide
Arbitrary<EncodedAudio> audio() {
return Combinators.combine(
Arbitraries.integers(),
Arbitraries.integers(),
Arbitraries.of(true, false),
ArbitraryByteBuffer.bytes().ofMaxSize(1000)
).as(EncodedAudio::new);
}
}