, Cloneable, IBatchProcess
* Get the number of blocks in the region.
*
* @return number of blocks
+ * @deprecated use {@link Region#getVolume()} to prevent overflows
*/
+ @Deprecated
default int getArea() {
+ return (int) getVolume();
+ }
+
+ /**
+ * Get the number of blocks in the region.
+ *
+ * Note: This method must be overridden.
+ *
+ * @return number of blocks
+ */
+ default long getVolume() {
+ // TODO Remove default status when getArea is removed.
+ try {
+ if (getClass().getMethod("getArea").getDeclaringClass().equals(Region.class)) {
+ throw new IllegalStateException("Class " + getClass().getName() + " must override getVolume.");
+ }
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ return getArea();
+ }
+
+ /* FAWE code for getArea() before merge:
+ default int getArea() {
BlockVector3 min = getMinimumPoint();
BlockVector3 max = getMaximumPoint();
return (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1) * (max.getZ() - min.getZ() + 1);
}
-
+ */
/**
* Get X-size.
*
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java
index e38f2a9c2..f607c5172 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java
@@ -135,8 +135,31 @@ public interface RegionSelector {
* Get the number of blocks inside the region.
*
* @return number of blocks, or -1 if undefined
+ * @deprecated use {@link RegionSelector#getVolume()}
*/
- int getArea();
+ @Deprecated
+ default int getArea() {
+ return (int) getVolume();
+ }
+
+ /**
+ * Get the number of blocks inside the region.
+ *
+ * Note: This method must be overridden.
+ *
+ * @return number of blocks, or -1 if undefined
+ */
+ default long getVolume() {
+ // TODO Remove default once getArea is removed
+ try {
+ if (getClass().getMethod("getArea").getDeclaringClass().equals(RegionSelector.class)) {
+ throw new IllegalStateException("Class " + getClass().getName() + " must override getVolume.");
+ }
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ return getArea();
+ }
/**
* Update the selector with changes to the region.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java
index 911e40b8b..733313421 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java
@@ -113,8 +113,8 @@ public class TransformRegion extends AbstractRegion {
}
@Override
- public int getArea() {
- return region.getArea(); // Cannot transform this
+ public long getVolume() {
+ return region.getVolume(); // Cannot transform this
}
@Override
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java
index e98522623..09156446b 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java
@@ -170,8 +170,8 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion
}
@Override
- public int getArea() {
- return region.getArea();
+ public long getVolume() {
+ return region.getVolume();
}
@Override
@@ -250,7 +250,7 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion
int lastVertexId = -1;
for (BlockVector3 vertex : vertices) {
vertexIds.put(vertex, ++lastVertexId);
- session.dispatchCUIEvent(player, new SelectionPointEvent(lastVertexId, vertex, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(lastVertexId, vertex, getVolume()));
}
for (Triangle triangle : triangles) {
@@ -273,8 +273,8 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion
checkNotNull(session);
if (isDefined()) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getArea()));
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getVolume()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getVolume()));
}
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
index 1a4cecc71..dab4721d8 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
@@ -161,13 +161,13 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.cuboid.explain.primary-area",
TextComponent.of(position1.toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else if (position1 != null) {
player.printInfo(TranslatableComponent.of("worldedit.selection.cuboid.explain.primary", TextComponent.of(position1.toString())));
}
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, pos, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, pos, getVolume()));
}
@Override
@@ -180,13 +180,13 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.cuboid.explain.secondary-area",
TextComponent.of(position2.toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else if (position2 != null) {
player.printInfo(TranslatableComponent.of("worldedit.selection.cuboid.explain.secondary", TextComponent.of(position2.toString())));
}
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, pos, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, pos, getVolume()));
}
@Override
@@ -197,11 +197,11 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
session.dispatchCUIEvent(player, new SelectionShapeEvent(getTypeID()));
if (position1 != null) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getVolume()));
}
if (position2 != null) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getVolume()));
}
}
@@ -268,7 +268,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
}
@Override
- public int getArea() {
+ public long getVolume() {
if (position1 == null) {
return -1;
}
@@ -277,17 +277,17 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion {
return -1;
}
- return region.getArea();
+ return region.getVolume();
}
@Override
public void describeCUI(LocalSession session, Actor player) {
if (position1 != null) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getVolume()));
}
if (position2 != null) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, position2, getVolume()));
}
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java
index 1b477f755..10de56191 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java
@@ -188,7 +188,7 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion {
"worldedit.selection.cylinder.explain.secondary",
TextComponent.of(NUMBER_FORMAT.format(region.getRadius().getX())),
TextComponent.of(NUMBER_FORMAT.format(region.getRadius().getZ())),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else {
player.printError(TranslatableComponent.of("worldedit.selection.cylinder.explain.secondary-missing"));
@@ -260,8 +260,8 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion {
}
@Override
- public int getArea() {
- return region.getArea();
+ public long getVolume() {
+ return region.getVolume();
}
@Override
@@ -273,8 +273,8 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion {
@Override
public void describeLegacyCUI(LocalSession session, Actor player) {
if (isDefined()) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getArea()));
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getVolume()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getVolume()));
}
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java
index 86c9d595a..c42de1fad 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java
@@ -151,7 +151,7 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.ellipsoid.explain.primary-area",
TextComponent.of(region.getCenter().toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else {
player.printInfo(TranslatableComponent.of(
@@ -169,7 +169,7 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.ellipsoid.explain.secondary-area",
TextComponent.of(region.getRadius().toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else {
player.printInfo(TranslatableComponent.of(
@@ -238,8 +238,8 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion {
}
@Override
- public int getArea() {
- return region.getArea();
+ public long getVolume() {
+ return region.getVolume();
}
@Override
@@ -250,8 +250,8 @@ public class EllipsoidRegionSelector implements RegionSelector, CUIRegion {
@Override
public void describeLegacyCUI(LocalSession session, Actor player) {
- session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getArea()));
- session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getArea()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getVolume()));
+ session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getVolume()));
}
@Override
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ExtendingCuboidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ExtendingCuboidRegionSelector.java
index 29fda872d..7a8c7b2ec 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ExtendingCuboidRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ExtendingCuboidRegionSelector.java
@@ -134,7 +134,7 @@ public class ExtendingCuboidRegionSelector extends CuboidRegionSelector {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.extend.explain.primary",
TextComponent.of(pos.toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
explainRegionAdjust(player, session);
@@ -145,7 +145,7 @@ public class ExtendingCuboidRegionSelector extends CuboidRegionSelector {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.extend.explain.secondary",
TextComponent.of(pos.toString()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
explainRegionAdjust(player, session);
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java
index 26b48f2da..3ab10dfc2 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java
@@ -174,7 +174,7 @@ public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion {
player.printInfo(TranslatableComponent.of("worldedit.selection.polygon2d.explain.primary", TextComponent.of(pos.toString())));
session.dispatchCUIEvent(player, new SelectionShapeEvent(getTypeID()));
- session.dispatchCUIEvent(player, new SelectionPoint2DEvent(0, pos, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPoint2DEvent(0, pos, getVolume()));
session.dispatchCUIEvent(player, new SelectionMinMaxEvent(region.getMinimumY(), region.getMaximumY()));
}
@@ -186,7 +186,7 @@ public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion {
TextComponent.of(pos.toString())
));
- session.dispatchCUIEvent(player, new SelectionPoint2DEvent(region.size() - 1, pos, getArea()));
+ session.dispatchCUIEvent(player, new SelectionPoint2DEvent(region.size() - 1, pos, getVolume()));
session.dispatchCUIEvent(player, new SelectionMinMaxEvent(region.getMinimumY(), region.getMaximumY()));
}
@@ -247,8 +247,8 @@ public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion {
}
@Override
- public int getArea() {
- return region.getArea();
+ public long getVolume() {
+ return region.getVolume();
}
/**
@@ -264,7 +264,7 @@ public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion {
public void describeCUI(LocalSession session, Actor player) {
final List points = region.getPoints();
for (int id = 0; id < points.size(); id++) {
- session.dispatchCUIEvent(player, new SelectionPoint2DEvent(id, points.get(id), getArea()));
+ session.dispatchCUIEvent(player, new SelectionPoint2DEvent(id, points.get(id), getVolume()));
}
session.dispatchCUIEvent(player, new SelectionMinMaxEvent(region.getMinimumY(), region.getMaximumY()));
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java
index ae909a077..641262a1a 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java
@@ -93,7 +93,7 @@ public class SphereRegionSelector extends EllipsoidRegionSelector {
player.printInfo(TranslatableComponent.of(
"worldedit.selection.sphere.explain.secondary-defined",
TextComponent.of(region.getRadius().getX()),
- TextComponent.of(region.getArea())
+ TextComponent.of(region.getVolume())
));
} else {
player.printInfo(TranslatableComponent.of("worldedit.selection.sphere.explain.secondary", TextComponent.of(region.getRadius().getX())));
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java
index 9e37e4da0..212f93e19 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java
@@ -78,8 +78,8 @@ public class RequestSelection implements Region {
}
@Override
- public int getArea() {
- return getRegion().getArea();
+ public long getVolume() {
+ return getRegion().getVolume();
}
@Override
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java
index 3074a4aa7..ea9061752 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java
@@ -19,7 +19,6 @@
package com.sk89q.worldedit.util.collection;
-import com.google.common.collect.AbstractIterator;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -36,6 +35,7 @@ import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -43,6 +43,7 @@ import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
+import static com.google.common.base.Preconditions.checkState;
import static com.sk89q.worldedit.math.BitMath.fixSign;
import static com.sk89q.worldedit.math.BitMath.mask;
@@ -51,12 +52,6 @@ import static com.sk89q.worldedit.math.BitMath.mask;
*/
public class BlockMap extends AbstractMap {
- /* =========================
- IF YOU MAKE CHANGES TO THIS CLASS
- Re-run BlockMapTest with the blockmap.fulltesting=true system property.
- Or just temporarily remove the annotation disabling the related tests.
- ========================= */
-
public static BlockMap create() {
return create(() -> new Int2ObjectOpenHashMap<>(64, 0.9f));
}
@@ -245,26 +240,65 @@ public class BlockMap extends AbstractMap {
entrySet = es = new AbstractSet>() {
@Override
public Iterator> iterator() {
- return new AbstractIterator>() {
+ return new Iterator>() {
private final ObjectIterator>> primaryIterator
= Long2ObjectMaps.fastIterator(maps);
- private long currentGroupKey;
+ private Long2ObjectMap.Entry> currentPrimaryEntry;
private ObjectIterator> secondaryIterator;
+ private boolean finished;
+ private LazyEntry next;
@Override
- protected Entry computeNext() {
+ public boolean hasNext() {
+ if (finished) {
+ return false;
+ }
+ if (next == null) {
+ LazyEntry proposedNext = computeNext();
+ if (proposedNext == null) {
+ finished = true;
+ return false;
+ }
+ next = proposedNext;
+ }
+ return true;
+ }
+
+ private LazyEntry computeNext() {
if (secondaryIterator == null || !secondaryIterator.hasNext()) {
if (!primaryIterator.hasNext()) {
- return endOfData();
+ return null;
}
- Long2ObjectMap.Entry> next = primaryIterator.next();
- currentGroupKey = next.getLongKey();
- secondaryIterator = Int2ObjectMaps.fastIterator(next.getValue());
+ currentPrimaryEntry = primaryIterator.next();
+ secondaryIterator = Int2ObjectMaps.fastIterator(currentPrimaryEntry.getValue());
+ // be paranoid
+ checkState(secondaryIterator.hasNext(),
+ "Should not have an empty map entry, it should have been removed!");
}
Int2ObjectMap.Entry next = secondaryIterator.next();
- return new LazyEntry(currentGroupKey, next.getIntKey(), next.getValue());
+ return new LazyEntry(currentPrimaryEntry.getLongKey(), next.getIntKey(), next.getValue());
+ }
+
+ @Override
+ public Entry next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ LazyEntry tmp = next;
+ next = null;
+ return tmp;
+ }
+
+ @Override
+ public void remove() {
+ secondaryIterator.remove();
+ // ensure invariants hold
+ if (currentPrimaryEntry.getValue().isEmpty()) {
+ // the remove call cleared this map. call remove on the primary iter
+ primaryIterator.remove();
+ }
}
};
}
@@ -364,13 +398,14 @@ public class BlockMap extends AbstractMap {
@Override
public V remove(Object key) {
BlockVector3 vec = (BlockVector3) key;
- Int2ObjectMap activeMap = maps.get(toGroupKey(vec));
+ long groupKey = toGroupKey(vec);
+ Int2ObjectMap activeMap = maps.get(groupKey);
if (activeMap == null) {
return null;
}
V removed = activeMap.remove(toInnerKey(vec));
if (activeMap.isEmpty()) {
- maps.remove(toGroupKey(vec));
+ maps.remove(groupKey);
}
return removed;
}
@@ -429,7 +464,9 @@ public class BlockMap extends AbstractMap {
}
if (o instanceof BlockMap) {
// optimize by skipping entry translations:
- return maps.equals(((BlockMap) o).maps);
+ @SuppressWarnings("unchecked")
+ BlockMap other = (BlockMap) o;
+ return maps.equals(other.maps);
}
return super.equals(o);
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java
index 7120c68e2..906f64c86 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java
@@ -91,6 +91,7 @@ class Int2BaseBlockMap extends AbstractInt2ObjectMap {
= Int2IntMaps.fastIterator(commonMap);
private final ObjectIterator> uncommonIter
= Int2ObjectMaps.fastIterator(uncommonMap);
+ private boolean lastNextFromCommon = false;
@Override
public boolean hasNext() {
@@ -101,15 +102,26 @@ class Int2BaseBlockMap extends AbstractInt2ObjectMap {
public Entry next() {
if (commonIter.hasNext()) {
Int2IntMap.Entry e = commonIter.next();
+ lastNextFromCommon = true;
return new BasicEntry<>(
e.getIntKey(), assumeAsBlock(e.getIntValue())
);
}
if (uncommonIter.hasNext()) {
+ lastNextFromCommon = false;
return uncommonIter.next();
}
throw new NoSuchElementException();
}
+
+ @Override
+ public void remove() {
+ if (lastNextFromCommon) {
+ commonIter.remove();
+ } else {
+ uncommonIter.remove();
+ }
+ }
};
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/VectorPositionList.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/VectorPositionList.java
index 6c8d27ddf..1617edf1b 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/VectorPositionList.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/VectorPositionList.java
@@ -30,49 +30,54 @@ import java.util.Iterator;
class VectorPositionList implements PositionList {
- private final IntList delegate = new IntArrayList();
+ private final IntList delegateX = new IntArrayList();
+ private final IntList delegateY = new IntArrayList();
+ private final IntList delegateZ = new IntArrayList();
@Override
public BlockVector3 get(int index) {
- int ri = index * 3;
return BlockVector3.at(
- delegate.getInt(ri),
- delegate.getInt(ri + 1),
- delegate.getInt(ri + 2));
+ delegateX.getInt(index),
+ delegateY.getInt(index),
+ delegateZ.getInt(index));
}
@Override
public void add(BlockVector3 vector) {
- delegate.add(vector.getX());
- delegate.add(vector.getY());
- delegate.add(vector.getZ());
+ delegateX.add(vector.getX());
+ delegateY.add(vector.getY());
+ delegateZ.add(vector.getZ());
}
@Override
public int size() {
- return delegate.size();
+ return delegateX.size();
}
@Override
public void clear() {
- delegate.clear();
+ delegateX.clear();
+ delegateY.clear();
+ delegateZ.clear();
}
@Override
public Iterator iterator() {
return new AbstractIterator() {
- private final IntIterator iterator = delegate.iterator();
+ private final IntIterator iteratorX = delegateX.iterator();
+ private final IntIterator iteratorY = delegateY.iterator();
+ private final IntIterator iteratorZ = delegateZ.iterator();
@Override
protected BlockVector3 computeNext() {
- if (!iterator.hasNext()) {
+ if (!iteratorX.hasNext()) {
return endOfData();
}
return BlockVector3.at(
- iterator.nextInt(),
- iterator.nextInt(),
- iterator.nextInt());
+ iteratorX.nextInt(),
+ iteratorY.nextInt(),
+ iteratorZ.nextInt());
}
};
}
@@ -81,17 +86,19 @@ class VectorPositionList implements PositionList {
public Iterator reverseIterator() {
return new AbstractIterator() {
- private final IntListIterator iterator = delegate.listIterator(delegate.size());
+ private final IntListIterator iteratorX = delegateX.listIterator(delegateX.size());
+ private final IntListIterator iteratorY = delegateY.listIterator(delegateY.size());
+ private final IntListIterator iteratorZ = delegateZ.listIterator(delegateZ.size());
@Override
protected BlockVector3 computeNext() {
- if (!iterator.hasPrevious()) {
+ if (!iteratorX.hasPrevious()) {
return endOfData();
}
return BlockVector3.at(
- iterator.previousInt(),
- iterator.previousInt(),
- iterator.previousInt());
+ iteratorX.previousInt(),
+ iteratorY.previousInt(),
+ iteratorZ.previousInt());
}
};
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/TaskStateComparator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/TaskStateComparator.java
index 38b77e73e..894c102d2 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/TaskStateComparator.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/TaskStateComparator.java
@@ -31,13 +31,7 @@ public class TaskStateComparator implements Comparator> {
public int compare(com.sk89q.worldedit.util.task.Task> o1, Task> o2) {
int ordinal1 = o1.getState().ordinal();
int ordinal2 = o2.getState().ordinal();
- if (ordinal1 < ordinal2) {
- return -1;
- } else if (ordinal1 > ordinal2) {
- return 1;
- } else {
- return 0;
- }
+ return Integer.compare(ordinal1, ordinal2);
}
}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/math/BlockVector3Test.java b/worldedit-core/src/test/java/com/sk89q/worldedit/math/BlockVector3Test.java
new file mode 100644
index 000000000..e47c2432a
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/math/BlockVector3Test.java
@@ -0,0 +1,36 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.math;
+
+import com.sk89q.worldedit.util.test.VariedVectors;
+import org.junit.jupiter.api.DisplayName;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@DisplayName("A 3D block vector")
+public class BlockVector3Test {
+
+ @VariedVectors.Test(capToVanilla = true, divisionsXZ = 50, divisionsY = 50)
+ @DisplayName("survives a round-trip through long-packing")
+ void longPackingRoundTrip(BlockVector3 vec) {
+ assertEquals(vec, BlockVector3.fromLongPackedForm(vec.toLongPackedForm()));
+ }
+
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/VariedVectorsProvider.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/VariedVectorsProvider.java
deleted file mode 100644
index 10a835392..000000000
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/VariedVectorsProvider.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.util;
-
-import com.google.common.collect.AbstractIterator;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Streams;
-import com.sk89q.worldedit.math.BlockVector3;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.ArgumentsProvider;
-import org.junit.jupiter.params.provider.ArgumentsSource;
-import org.junit.jupiter.params.support.AnnotationConsumer;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.stream.Stream;
-
-/**
- * Argument provider for various vectors.
- */
-public final class VariedVectorsProvider implements ArgumentsProvider, AnnotationConsumer {
-
- @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @ArgumentsSource(VariedVectorsProvider.class)
- @ParameterizedTest(name = ParameterizedTest.ARGUMENTS_PLACEHOLDER)
- public @interface Test {
-
- /**
- * If {@code true}, provide a non-matching vector from
- * the existing vectors set as well. This will nearly
- * square the number of tests executed, since it will
- * test every non-matching vector.
- */
- boolean provideNonMatching() default false;
-
- }
-
- private static final int WORLD_XZ_MINMAX = 30_000_000;
- private static final long WORLD_Y_MAX = Integer.MAX_VALUE;
- private static final long WORLD_Y_MIN = Integer.MIN_VALUE;
-
- // For better coverage assurance, increase these values for a local Gradle run.
- // Don't do it for IntelliJ, it'll probably run out of memory.
- private static final int DIVISIONS_XZ = Integer.getInteger("variedvecs.divisions.xz", 5);
- private static final int DIVISIONS_Y = Integer.getInteger("variedvecs.divisions.y", 5);
-
- private static final int XZ_STEP = (WORLD_XZ_MINMAX * 2) / DIVISIONS_XZ;
- private static final long Y_STEP = (WORLD_Y_MAX * 2) / DIVISIONS_Y;
-
- private static final Set ALWAYS_INCLUDE =
- ImmutableSet.of(BlockVector3.ZERO, BlockVector3.ONE,
- BlockVector3.at(-WORLD_XZ_MINMAX, 0, -WORLD_XZ_MINMAX),
- BlockVector3.at(WORLD_XZ_MINMAX, 0, WORLD_XZ_MINMAX),
- BlockVector3.at(-WORLD_XZ_MINMAX, WORLD_Y_MAX, -WORLD_XZ_MINMAX),
- BlockVector3.at(WORLD_XZ_MINMAX, WORLD_Y_MAX, WORLD_XZ_MINMAX));
-
- private boolean provideNonMatching;
-
- @Override
- public void accept(Test test) {
- provideNonMatching = test.provideNonMatching();
- }
-
- @Override
- public Stream extends Arguments> provideArguments(ExtensionContext context) {
- if (provideNonMatching) {
- return makeVectorsStream()
- .flatMap(vec -> makeVectorsStream().filter(v -> !v.equals(vec))
- .map(v -> Arguments.of(vec, v)));
- }
- return makeVectorsStream().map(Arguments::of);
- }
-
- public static Stream makeVectorsStream() {
- return Stream.concat(
- ALWAYS_INCLUDE.stream(),
- Streams.stream(generateVectors()).filter(v -> !ALWAYS_INCLUDE.contains(v))
- );
- }
-
- private static Iterator generateVectors() {
- return new AbstractIterator() {
-
- private int x = -WORLD_XZ_MINMAX + 1;
- private int z = -WORLD_XZ_MINMAX + 1;
- private long y = WORLD_Y_MAX;
-
- @Override
- protected BlockVector3 computeNext() {
- if (x > WORLD_XZ_MINMAX) {
- return endOfData();
- }
- BlockVector3 newVector = BlockVector3.at(x, (int) y, z);
- y += Y_STEP;
- if (y > WORLD_Y_MAX) {
- y = 0;
- z += XZ_STEP;
- if (z > WORLD_XZ_MINMAX) {
- z = -WORLD_XZ_MINMAX;
- x += XZ_STEP;
- }
- }
- return newVector;
- }
- };
- }
-}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java
index cab27786f..6a9fda239 100644
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java
@@ -27,7 +27,8 @@ import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extension.platform.Preference;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.Registry;
-import com.sk89q.worldedit.util.VariedVectorsProvider;
+import com.sk89q.worldedit.util.test.VariedVectorGenerator;
+import com.sk89q.worldedit.util.test.VariedVectors;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
@@ -45,11 +46,11 @@ import org.mockito.MockitoAnnotations;
import java.lang.reflect.Field;
import java.util.AbstractMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
-import java.util.function.Function;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -63,24 +64,23 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@DisplayName("An ordered block map")
class BlockMapTest {
/*
- private static Platform mockedPlatform = mock(Platform.class);
+ private static final Platform MOCKED_PLATFORM = mock(Platform.class);
@BeforeAll
static void setupFakePlatform() {
- when(mockedPlatform.getRegistries()).thenReturn(new BundledRegistries() {
+ when(MOCKED_PLATFORM.getRegistries()).thenReturn(new BundledRegistries() {
});
- when(mockedPlatform.getCapabilities()).thenReturn(ImmutableMap.of(
+ when(MOCKED_PLATFORM.getCapabilities()).thenReturn(ImmutableMap.of(
Capability.WORLD_EDITING, Preference.PREFERRED,
Capability.GAME_HOOKS, Preference.PREFERRED
));
PlatformManager platformManager = WorldEdit.getInstance().getPlatformManager();
- platformManager.register(mockedPlatform);
+ platformManager.register(MOCKED_PLATFORM);
registerBlock("minecraft:air");
registerBlock("minecraft:oak_wood");
@@ -88,7 +88,7 @@ class BlockMapTest {
@AfterAll
static void tearDownFakePlatform() throws Exception {
- WorldEdit.getInstance().getPlatformManager().unregister(mockedPlatform);
+ WorldEdit.getInstance().getPlatformManager().unregister(MOCKED_PLATFORM);
Field map = Registry.class.getDeclaredField("map");
map.setAccessible(true);
((Map, ?>) map.get(BlockType.REGISTRY)).clear();
@@ -98,8 +98,6 @@ class BlockMapTest {
BlockType.REGISTRY.register(id, new BlockType(id));
}
- @Mock
- private Function super BlockVector3, ? extends BaseBlock> function;
@Mock
private BiFunction super BlockVector3, ? super BaseBlock, ? extends BaseBlock> biFunction;
@Mock
@@ -110,7 +108,7 @@ class BlockMapTest {
private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock();
private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock();
- private BlockMap map = BlockMap.createForBaseBlock();
+ private final BlockMap map = BlockMap.createForBaseBlock();
@BeforeEach
void setUp() {
@@ -186,14 +184,14 @@ class BlockMapTest {
@DisplayName("never calls the forEach action")
void neverCallsForEachAction() {
map.forEach(biConsumer);
- verifyZeroInteractions(biConsumer);
+ verifyNoMoreInteractions(biConsumer);
}
@Test
@DisplayName("never calls the replaceAll function")
void neverCallsReplaceAllFunction() {
map.replaceAll(biFunction);
- verifyZeroInteractions(biFunction);
+ verifyNoMoreInteractions(biFunction);
}
@Test
@@ -254,63 +252,62 @@ class BlockMapTest {
assertEquals(air, map.merge(BlockVector3.ZERO, air, mergeFunction));
assertEquals(1, map.size());
assertEquals(air, map.get(BlockVector3.ZERO));
- verifyZeroInteractions(mergeFunction);
+ verifyNoMoreInteractions(mergeFunction);
}
}
@Nested
@DisplayName("after having an entry added")
- @EnabledIfSystemProperty(named = "blockmap.fulltesting", matches = "true")
class AfterEntryAdded {
// Note: This section of tests would really benefit from
// being able to parameterize classes. It's not part of JUnit
// yet though: https://github.com/junit-team/junit5/issues/878
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("has a size of one")
void hasSizeOne(BlockVector3 vec) {
map.put(vec, air);
assertEquals(1, map.size());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("is equal to another map with the same entry")
void isEqualToSimilarMap(BlockVector3 vec) {
map.put(vec, air);
assertEquals(ImmutableMap.of(vec, air), map);
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("is not equal to another map with a different key")
void isNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
assertNotEquals(ImmutableMap.of(nonMatch, air), map);
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("is not equal to another map with a different value")
void isNotEqualToDifferentValueMap(BlockVector3 vec) {
map.put(vec, air);
assertNotEquals(ImmutableMap.of(vec, oakWood), map);
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("is not equal to an empty map")
void isNotEqualToEmptyMap(BlockVector3 vec) {
map.put(vec, air);
assertNotEquals(ImmutableMap.of(), map);
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("has the same hashCode as another map with the same entry")
void isHashCodeEqualToSimilarMap(BlockVector3 vec) {
map.put(vec, air);
assertEquals(ImmutableMap.of(vec, air).hashCode(), map.hashCode());
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("has a different hashCode from another map with a different key")
void isHashCodeNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) {
assumeFalse(vec.hashCode() == nonMatch.hashCode(),
@@ -319,35 +316,35 @@ class BlockMapTest {
assertNotEquals(ImmutableMap.of(nonMatch, air).hashCode(), map.hashCode());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("has a different hashCode from another map with a different value")
void isHashCodeNotEqualToDifferentValueMap(BlockVector3 vec) {
map.put(vec, air);
assertNotEquals(ImmutableMap.of(vec, oakWood).hashCode(), map.hashCode());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("has a different hashCode from an empty map")
void isHashCodeNotEqualToEmptyMap(BlockVector3 vec) {
map.put(vec, air);
assertNotEquals(ImmutableMap.of().hashCode(), map.hashCode());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("returns value from get")
void returnsValueFromGet(BlockVector3 vec) {
map.put(vec, air);
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("returns `null` from get with different key")
void returnsValueFromGet(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
assertNotEquals(air, map.get(nonMatch));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("contains the key")
void containsTheKey(BlockVector3 vec) {
map.put(vec, air);
@@ -356,7 +353,7 @@ class BlockMapTest {
assertTrue(map.containsKey(vec));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("contains the value")
void containsTheValue(BlockVector3 vec) {
map.put(vec, air);
@@ -365,7 +362,7 @@ class BlockMapTest {
assertTrue(map.containsValue(air));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("contains the entry")
void containsTheEntry(BlockVector3 vec) {
map.put(vec, air);
@@ -373,21 +370,21 @@ class BlockMapTest {
assertEquals(new AbstractMap.SimpleImmutableEntry<>(vec, air), map.entrySet().iterator().next());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("returns the provided value from getOrDefault")
void returnsProvidedFromGetOrDefault(BlockVector3 vec) {
map.put(vec, air);
assertEquals(air, map.getOrDefault(vec, oakWood));
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("returns the default value from getOrDefault with a different key")
void returnsDefaultFromGetOrDefaultWrongKey(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
assertEquals(oakWood, map.getOrDefault(nonMatch, oakWood));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("calls the forEach action once")
void neverCallsForEachAction(BlockVector3 vec) {
map.put(vec, air);
@@ -396,7 +393,7 @@ class BlockMapTest {
verifyNoMoreInteractions(biConsumer);
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("replaces value using replaceAll")
void neverCallsReplaceAllFunction(BlockVector3 vec) {
map.put(vec, air);
@@ -404,7 +401,7 @@ class BlockMapTest {
assertEquals(oakWood, map.get(vec));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("does not insert on `putIfAbsent`")
void noInsertOnPutIfAbsent(BlockVector3 vec) {
map.put(vec, air);
@@ -413,7 +410,7 @@ class BlockMapTest {
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("inserts on `putIfAbsent` to a different key")
void insertOnPutIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
@@ -423,7 +420,7 @@ class BlockMapTest {
assertEquals(oakWood, map.get(nonMatch));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("remove(key) returns the old value")
void removeKeyReturnsOldValue(BlockVector3 vec) {
map.put(vec, air);
@@ -431,7 +428,27 @@ class BlockMapTest {
assertEquals(0, map.size());
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test
+ @DisplayName("keySet().remove(key) removes the entry from the map")
+ void keySetRemovePassesThrough(BlockVector3 vec) {
+ map.put(vec, air);
+ assertTrue(map.keySet().remove(vec));
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectors.Test
+ @DisplayName("entrySet().iterator().remove() removes the entry from the map")
+ void entrySetIteratorRemovePassesThrough(BlockVector3 vec) {
+ map.put(vec, air);
+ Iterator> iterator = map.entrySet().iterator();
+ assertTrue(iterator.hasNext());
+ Map.Entry entry = iterator.next();
+ assertEquals(entry.getKey(), vec);
+ iterator.remove();
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("remove(nonMatch) returns null")
void removeNonMatchingKeyReturnsNull(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
@@ -439,7 +456,7 @@ class BlockMapTest {
assertEquals(1, map.size());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("remove(key, value) returns true")
void removeKeyValueReturnsTrue(BlockVector3 vec) {
map.put(vec, air);
@@ -447,7 +464,7 @@ class BlockMapTest {
assertEquals(0, map.size());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("remove(key, value) returns false for wrong value")
void removeKeyValueReturnsFalseWrongValue(BlockVector3 vec) {
map.put(vec, air);
@@ -455,7 +472,7 @@ class BlockMapTest {
assertEquals(1, map.size());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("replaces value at key")
void replacesValueAtKey(BlockVector3 vec) {
map.put(vec, air);
@@ -469,7 +486,7 @@ class BlockMapTest {
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("does not replace value at different key")
void doesNotReplaceAtDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
@@ -483,7 +500,7 @@ class BlockMapTest {
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("does not insert on computeIfAbsent")
void doesNotInsertComputeIfAbsent(BlockVector3 vec) {
map.put(vec, air);
@@ -495,7 +512,7 @@ class BlockMapTest {
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("inserts on computeIfAbsent with different key")
void insertsOnComputeIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
@@ -508,7 +525,7 @@ class BlockMapTest {
assertEquals(oakWood, map.get(nonMatch));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("replaces on compute")
void replaceOnCompute(BlockVector3 vec) {
map.put(vec, air);
@@ -523,7 +540,7 @@ class BlockMapTest {
assertEquals(0, map.size());
}
- @VariedVectorsProvider.Test(provideNonMatching = true)
+ @VariedVectors.Test(provideNonMatching = true)
@DisplayName("inserts on compute with different key")
void insertOnComputeDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
map.put(vec, air);
@@ -540,7 +557,7 @@ class BlockMapTest {
assertEquals(air, map.get(vec));
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("replaces on computeIfPresent")
void replacesOnComputeIfPresent(BlockVector3 vec) {
map.put(vec, air);
@@ -555,7 +572,7 @@ class BlockMapTest {
assertEquals(0, map.size());
}
- @VariedVectorsProvider.Test
+ @VariedVectors.Test
@DisplayName("inserts on merge, with call to merge function")
void insertsOnMerge(BlockVector3 vec) {
map.put(vec, air);
@@ -573,7 +590,9 @@ class BlockMapTest {
@Test
@DisplayName("contains all inserted vectors")
void containsAllInsertedVectors() {
- Set allVectors = VariedVectorsProvider.makeVectorsStream().collect(Collectors.toSet());
+ Set allVectors = new VariedVectorGenerator()
+ .makeVectorsStream()
+ .collect(Collectors.toSet());
for (BlockVector3 vec : allVectors) {
map.put(vec, air);
}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/PositionListTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/PositionListTest.java
new file mode 100644
index 000000000..d287558ef
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/PositionListTest.java
@@ -0,0 +1,98 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.collection;
+
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.util.test.VariedVectors;
+import org.junit.jupiter.api.DisplayName;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+abstract class PositionListTest {
+
+ static class Long extends PositionListTest {
+ protected Long() {
+ super(new LongPositionList());
+ }
+ }
+
+ static class Vector extends PositionListTest {
+ protected Vector() {
+ super(new VectorPositionList());
+ }
+ }
+
+ private final PositionList positionList;
+
+ protected PositionListTest(PositionList positionList) {
+ this.positionList = positionList;
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling add(vec) increases size by 1")
+ void addIncreasesSizeByOne(BlockVector3 vec) {
+ positionList.add(vec);
+ assertEquals(1, positionList.size());
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling get(0) after add(vec) returns vec")
+ void canGetVectorAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ assertEquals(vec, positionList.get(0));
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling iterator().hasNext() after add(vec) returns true")
+ void hasNextAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ assertTrue(positionList.iterator().hasNext());
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling iterator().next() after add(vec) returns vec")
+ void nextAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ assertEquals(vec, positionList.iterator().next());
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling reverseIterator().hasNext() after add(vec) returns true")
+ void reverseHasNextAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ assertTrue(positionList.reverseIterator().hasNext());
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling reverseIterator().next() after add(vec) returns vec")
+ void reverseNextAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ assertEquals(vec, positionList.reverseIterator().next());
+ }
+
+ @VariedVectors.Test(capToVanilla = true)
+ @DisplayName("calling clear() after add(vec) makes the size() zero")
+ void clearAfterAdd(BlockVector3 vec) {
+ positionList.add(vec);
+ positionList.clear();
+ assertEquals(0, positionList.size());
+ }
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectorGenerator.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectorGenerator.java
new file mode 100644
index 000000000..6818c7aa2
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectorGenerator.java
@@ -0,0 +1,100 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.test;
+
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
+import com.sk89q.worldedit.math.BlockVector3;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class VariedVectorGenerator {
+
+ // For better coverage assurance, increase these values for a local Gradle run.
+ // Don't do it for IntelliJ, it'll probably run out of memory.
+ private static final int DEFAULT_DIVISIONS_XZ =
+ Integer.getInteger("variedvecs.divisions.xz", 5);
+ private static final int DEFAULT_DIVISIONS_Y =
+ Integer.getInteger("variedvecs.divisions.y", 5);
+
+ public final int divisionsXZ;
+ public final int divisionsY;
+ public final int maxXZ;
+ public final long maxY;
+ public final int xzStep;
+ public final long yStep;
+ public final Set alwaysInclude;
+
+ public VariedVectorGenerator() {
+ this(false, -1, -1);
+ }
+
+ public VariedVectorGenerator(boolean vanilla, int divisionsXZ, int divisionsY) {
+ this.divisionsXZ = divisionsXZ == -1 ? DEFAULT_DIVISIONS_XZ : divisionsXZ;
+ this.divisionsY = divisionsY == -1 ? DEFAULT_DIVISIONS_Y : divisionsY;
+ maxXZ = 30_000_000;
+ maxY = vanilla ? 255 : Integer.MAX_VALUE;
+ xzStep = (maxXZ * 2) / this.divisionsXZ;
+ yStep = (maxY * 2) / this.divisionsY;
+ alwaysInclude =
+ ImmutableSet.of(BlockVector3.ZERO, BlockVector3.ONE,
+ BlockVector3.at(-maxXZ, 0, -maxXZ),
+ BlockVector3.at(maxXZ, 0, maxXZ),
+ BlockVector3.at(-maxXZ, maxY, -maxXZ),
+ BlockVector3.at(maxXZ, maxY, maxXZ));
+ }
+
+ public Stream makeVectorsStream() {
+ return Stream.concat(
+ alwaysInclude.stream(),
+ Streams.stream(generateVectors()).filter(v -> !alwaysInclude.contains(v))
+ );
+ }
+
+ private Iterator generateVectors() {
+ return new AbstractIterator() {
+
+ private int x = -maxXZ + 1;
+ private int z = -maxXZ + 1;
+ private long y = maxY;
+
+ @Override
+ protected BlockVector3 computeNext() {
+ if (x > maxXZ) {
+ return endOfData();
+ }
+ BlockVector3 newVector = BlockVector3.at(x, (int) y, z);
+ y += yStep;
+ if (y > maxY) {
+ y = 0;
+ z += xzStep;
+ if (z > maxXZ) {
+ z = -maxXZ;
+ x += xzStep;
+ }
+ }
+ return newVector;
+ }
+ };
+ }
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectors.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectors.java
new file mode 100644
index 000000000..daadda021
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/test/VariedVectors.java
@@ -0,0 +1,87 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.test;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.stream.Stream;
+
+/**
+ * Argument provider for various vectors.
+ */
+public final class VariedVectors implements ArgumentsProvider, AnnotationConsumer {
+
+ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @ArgumentsSource(VariedVectors.class)
+ @ParameterizedTest(name = ParameterizedTest.ARGUMENTS_PLACEHOLDER)
+ public @interface Test {
+
+ /**
+ * If {@code true}, provide a non-matching vector from
+ * the existing vectors set as well. This will nearly
+ * square the number of tests executed, since it will
+ * test every non-matching vector.
+ */
+ boolean provideNonMatching() default false;
+
+ /**
+ * If {@code true}, only provide vectors inside the range of Vanilla MC.
+ * This caps the Y value to 255.
+ */
+ boolean capToVanilla() default false;
+
+ int divisionsXZ() default -1;
+
+ int divisionsY() default -1;
+
+ }
+
+ private boolean provideNonMatching;
+ private VariedVectorGenerator generator;
+
+ @Override
+ public void accept(Test test) {
+ provideNonMatching = test.provideNonMatching();
+ generator = new VariedVectorGenerator(test.capToVanilla(), test.divisionsXZ(), test.divisionsY());
+ }
+
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) {
+ if (provideNonMatching) {
+ return generator.makeVectorsStream()
+ .flatMap(vec -> generator.makeVectorsStream().filter(v -> !v.equals(vec))
+ .map(v -> Arguments.of(vec, v)));
+ }
+ return generator.makeVectorsStream().map(Arguments::of);
+ }
+
+}