mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-06 04:46:40 +00:00
Merge remote-tracking branch 'upstream/master' into merge
This commit is contained in:
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Common setup code for expression tests.
|
||||
*/
|
||||
class BaseExpressionTest {
|
||||
|
||||
static double readSlot(Expression expr, String name) {
|
||||
return expr.getSlots().getSlotValue(name).orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
private Platform mockPlat = mock(Platform.class);
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
|
||||
@Override
|
||||
public void load() {
|
||||
}
|
||||
});
|
||||
WorldEdit.getInstance().getPlatformManager().register(mockPlat);
|
||||
}
|
||||
|
||||
double simpleEval(String expressionString) throws ExpressionException {
|
||||
final Expression expression = compile(expressionString);
|
||||
|
||||
expression.setEnvironment(new ExpressionEnvironment() {
|
||||
@Override
|
||||
public int getBlockType(double x, double y, double z) {
|
||||
return (int) x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockData(double x, double y, double z) {
|
||||
return (int) y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTypeAbs(double x, double y, double z) {
|
||||
return (int) x*10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockDataAbs(double x, double y, double z) {
|
||||
return (int) y*10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTypeRel(double x, double y, double z) {
|
||||
return (int) x*100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockDataRel(double x, double y, double z) {
|
||||
return (int) y*100;
|
||||
}
|
||||
});
|
||||
|
||||
return expression.evaluate();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
WorldEdit.getInstance().getPlatformManager().unregister(mockPlat);
|
||||
}
|
||||
|
||||
Expression compile(String expressionString, String... variableNames) throws ExpressionException {
|
||||
final Expression expression = Expression.compile(expressionString, variableNames);
|
||||
expression.optimize();
|
||||
return expression;
|
||||
}
|
||||
}
|
@ -19,17 +19,13 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.internal.expression.lexer.LexerException;
|
||||
import com.sk89q.worldedit.internal.expression.parser.ParserException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
|
||||
|
||||
import java.time.Duration;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.ExpressionTimeoutException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.lang.Math.atan2;
|
||||
import static java.lang.Math.sin;
|
||||
@ -40,20 +36,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ExpressionTest {
|
||||
class ExpressionTest extends BaseExpressionTest {
|
||||
|
||||
private Platform mockPlat = mock(Platform.class);
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
|
||||
@Override
|
||||
public void load() {
|
||||
}
|
||||
});
|
||||
WorldEdit.getInstance().getPlatformManager().register(mockPlat);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
WorldEdit.getInstance().getPlatformManager().unregister(mockPlat);
|
||||
@ -77,73 +63,78 @@ public class ExpressionTest {
|
||||
assertEquals(8, compile("foo+bar", "foo", "bar").evaluate(5D, 3D), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTightTokenization() {
|
||||
assertEquals(4, simpleEval("3+1"), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrors() {
|
||||
assertAll(
|
||||
// test lexer errors
|
||||
() -> {
|
||||
LexerException e = assertThrows(LexerException.class,
|
||||
() -> compile("#"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
},
|
||||
// test parser errors
|
||||
() -> {
|
||||
ParserException e = assertThrows(ParserException.class,
|
||||
() -> compile("x"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
},
|
||||
() -> {
|
||||
ParserException e = assertThrows(ParserException.class,
|
||||
() -> compile("x()"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
},
|
||||
() -> assertThrows(ParserException.class,
|
||||
() -> compile("(")),
|
||||
() -> assertThrows(ParserException.class,
|
||||
() -> compile("x(")),
|
||||
// test overloader errors
|
||||
() -> {
|
||||
ParserException e = assertThrows(ParserException.class,
|
||||
() -> compile("atan2(1)"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
},
|
||||
() -> {
|
||||
ParserException e = assertThrows(ParserException.class,
|
||||
() -> compile("atan2(1, 2, 3)"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
},
|
||||
() -> {
|
||||
ParserException e = assertThrows(ParserException.class,
|
||||
() -> compile("rotate(1, 2, 3)"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
);
|
||||
// test lexer errors
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("#"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
// test parser errors
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("x"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("x()"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
assertThrows(ExpressionException.class,
|
||||
() -> compile("("));
|
||||
assertThrows(ExpressionException.class,
|
||||
() -> compile("x("));
|
||||
// test overloader errors
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("atan2(1)"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("atan2(1, 2, 3)"));
|
||||
assertEquals(0, e.getPosition(), "Error position");
|
||||
}
|
||||
{
|
||||
ExpressionException e = assertThrows(ExpressionException.class,
|
||||
() -> compile("rotate(1, 2, 3)"));
|
||||
e.printStackTrace();
|
||||
assertEquals(7, e.getPosition(), "Error position");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssign() throws ExpressionException {
|
||||
Expression foo = compile("{a=x} b=y; c=z", "x", "y", "z", "a", "b", "c");
|
||||
foo.evaluate(2D, 3D, 5D);
|
||||
assertEquals(2, foo.getVariable("a", false).getValue(), 0);
|
||||
assertEquals(3, foo.getVariable("b", false).getValue(), 0);
|
||||
assertEquals(5, foo.getVariable("c", false).getValue(), 0);
|
||||
assertEquals(2, foo.getSlots().getSlotValue("a").orElse(-1), 0);
|
||||
assertEquals(3, foo.getSlots().getSlotValue("b").orElse(-1), 0);
|
||||
assertEquals(5, foo.getSlots().getSlotValue("c").orElse(-1), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIf() throws ExpressionException {
|
||||
assertEquals(40, simpleEval("if (1) x=4; else y=5; x*10+y;"), 0);
|
||||
assertEquals(5, simpleEval("if (0) x=4; else y=5; x*10+y;"), 0);
|
||||
assertEquals(40, simpleEval("y=0; if (1) x=4; else y=5; x*10+y;"), 0);
|
||||
assertEquals(5, simpleEval("x=0; if (0) x=4; else y=5; x*10+y;"), 0);
|
||||
|
||||
// test 'dangling else'
|
||||
final Expression expression1 = compile("if (1) if (0) x=4; else y=5;", "x", "y");
|
||||
expression1.evaluate(1D, 2D);
|
||||
assertEquals(1, expression1.getVariable("x", false).getValue(), 0);
|
||||
assertEquals(5, expression1.getVariable("y", false).getValue(), 0);
|
||||
assertEquals(1, expression1.getSlots().getSlotValue("x").orElse(-1), 0);
|
||||
assertEquals(5, expression1.getSlots().getSlotValue("y").orElse(-1), 0);
|
||||
|
||||
// test if the if construct is correctly recognized as a statement
|
||||
final Expression expression2 = compile("if (0) if (1) x=5; y=4;", "x", "y");
|
||||
expression2.evaluate(1D, 2D);
|
||||
assertEquals(4, expression2.getVariable("y", false).getValue(), 0);
|
||||
assertEquals(4, expression2.getSlots().getSlotValue("y").orElse(-1), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -182,53 +173,12 @@ public class ExpressionTest {
|
||||
|
||||
@Test
|
||||
public void testTimeout() {
|
||||
ExpressionTimeoutException e = assertThrows(ExpressionTimeoutException.class,
|
||||
() -> simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}"),
|
||||
"Loop was not stopped.");
|
||||
ExpressionTimeoutException e = assertTimeoutPreemptively(Duration.ofSeconds(10), () ->
|
||||
assertThrows(ExpressionTimeoutException.class,
|
||||
() -> simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}"),
|
||||
"Loop was not stopped.")
|
||||
);
|
||||
assertTrue(e.getMessage().contains("Calculations exceeded time limit"));
|
||||
}
|
||||
|
||||
private double simpleEval(String expressionString) throws ExpressionException {
|
||||
final Expression expression = compile(expressionString);
|
||||
|
||||
expression.setEnvironment(new ExpressionEnvironment() {
|
||||
@Override
|
||||
public int getBlockType(double x, double y, double z) {
|
||||
return (int) x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockData(double x, double y, double z) {
|
||||
return (int) y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTypeAbs(double x, double y, double z) {
|
||||
return (int) x*10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockDataAbs(double x, double y, double z) {
|
||||
return (int) y*10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTypeRel(double x, double y, double z) {
|
||||
return (int) x*100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockDataRel(double x, double y, double z) {
|
||||
return (int) y*100;
|
||||
}
|
||||
});
|
||||
|
||||
return expression.evaluate();
|
||||
}
|
||||
|
||||
private Expression compile(String expressionString, String... variableNames) throws ExpressionException, EvaluationException {
|
||||
final Expression expression = Expression.compile(expressionString, variableNames);
|
||||
expression.optimize();
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.internal.expression;
|
||||
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Test class for various real-world expressions.
|
||||
*/
|
||||
class RealExpressionTest extends BaseExpressionTest {
|
||||
|
||||
private static final class TestCase {
|
||||
|
||||
final Vector3 loc;
|
||||
final double result;
|
||||
final Consumer<Expression> postChecks;
|
||||
|
||||
private TestCase(Vector3 loc, double result, Consumer<Expression> postChecks) {
|
||||
this.loc = loc;
|
||||
this.result = result;
|
||||
this.postChecks = postChecks;
|
||||
}
|
||||
|
||||
TestCase withData(int expectedData) {
|
||||
return new TestCase(loc, result, expr -> {
|
||||
postChecks.accept(expr);
|
||||
double data = readSlot(expr, "data");
|
||||
assertEquals(expectedData, (int) data,
|
||||
"Test case " + this + " failed (data)");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return loc + " -> " + result;
|
||||
}
|
||||
}
|
||||
|
||||
private static TestCase testCase(Vector3 loc, double result) {
|
||||
return testCase(loc, result, e -> {
|
||||
});
|
||||
}
|
||||
|
||||
private static TestCase testCase(Vector3 loc, double result, Consumer<Expression> postChecks) {
|
||||
return new TestCase(loc, result, postChecks);
|
||||
}
|
||||
|
||||
private void checkExpression(String expr, TestCase... cases) {
|
||||
Expression compiled = compile(expr, "x", "y", "z");
|
||||
for (TestCase aCase : cases) {
|
||||
Vector3 loc = aCase.loc;
|
||||
assertEquals(aCase.result, compiled.evaluate(loc.getX(), loc.getY(), loc.getZ()), 0,
|
||||
"Test case " + aCase + " failed (result)");
|
||||
aCase.postChecks.accept(compiled);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void torus() {
|
||||
checkExpression("(0.75-sqrt(x^2+y^2))^2+z^2 < 0.25^2",
|
||||
testCase(Vector3.at(0, 0, 0), 0),
|
||||
testCase(Vector3.at(0.5, 0.5, 0.5), 0),
|
||||
testCase(Vector3.at(1, 0, 0), 0),
|
||||
testCase(Vector3.at(0.5, 0.5, 0), 1),
|
||||
testCase(Vector3.at(0.75, 0.5, 0), 1),
|
||||
testCase(Vector3.at(0.75, 0, 0), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gnarledOakTree() {
|
||||
checkExpression("(0.5+sin(atan2(x,z)*8)*0.2)*(sqrt(x*x+z*z)/0.5)^(-2)-1.2 < y",
|
||||
testCase(Vector3.at(-1, -1, -1), 1),
|
||||
testCase(Vector3.at(-1, 0, 1), 1),
|
||||
testCase(Vector3.at(1, 1, 1), 1),
|
||||
testCase(Vector3.at(0, 0, -1), 1),
|
||||
testCase(Vector3.at(0, 0, 0), 0),
|
||||
testCase(Vector3.at(0, 1, 0), 0),
|
||||
testCase(Vector3.at(0, 0, 0.32274), 0),
|
||||
testCase(Vector3.at(0, 0, 0.32275), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rainbowTorus() {
|
||||
checkExpression("data=(32+15/2/pi*atan2(x,y))%16; (0.75-sqrt(x^2+y^2))^2+z^2 < 0.25^2",
|
||||
testCase(Vector3.at(0, 0, 0), 0),
|
||||
testCase(Vector3.at(0.5, 0.5, 0.5), 0),
|
||||
testCase(Vector3.at(1, 0, 0), 0),
|
||||
testCase(Vector3.at(0.5, 0.5, 0), 1).withData(1),
|
||||
testCase(Vector3.at(0.75, 0.5, 0), 1).withData(2),
|
||||
testCase(Vector3.at(0.75, 0, 0), 1).withData(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rainbowEgg() {
|
||||
TestCase[] testCases = new TestCase[15];
|
||||
for (int i = 0; i < testCases.length; i++) {
|
||||
testCases[i] = testCase(Vector3.at(0, i / 16.0 - 0.5, 0), 1)
|
||||
.withData((i + 9) % 16);
|
||||
}
|
||||
testCases = Stream.concat(Stream.of(testCases), Stream.of(
|
||||
testCase(Vector3.at(0, 1, 0), 0)
|
||||
)).toArray(TestCase[]::new);
|
||||
checkExpression("data=(32+y*16+1)%16; y^2/9+x^2/6*(1/(1-0.4*y))+z^2/6*(1/(1-0.4*y))<0.08",
|
||||
testCases);
|
||||
}
|
||||
|
||||
@Test
|
||||
void heart() {
|
||||
checkExpression("(z/2)^2+x^2+(5*y/4-sqrt(abs(x)))^2<0.6",
|
||||
testCase(Vector3.at(0, 0, -1), 1),
|
||||
testCase(Vector3.at(0, 1, -1), 0),
|
||||
testCase(Vector3.at(-0.5, 1, 0), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sineWave() {
|
||||
checkExpression("sin(x*5)/2<y",
|
||||
testCase(Vector3.at(1, -0.47947, 0), 0),
|
||||
testCase(Vector3.at(1, -0.47946, 0), 1),
|
||||
testCase(Vector3.at(2, -0.27202, 0), 0),
|
||||
testCase(Vector3.at(2, -0.27201, 0), 1),
|
||||
testCase(Vector3.at(3, 0.32513, 0), 0),
|
||||
testCase(Vector3.at(3, 0.32515, 0), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void radialCosine() {
|
||||
checkExpression("cos(sqrt(x^2+z^2)*5)/2<y",
|
||||
testCase(Vector3.at(0, 0.5, 0), 0),
|
||||
testCase(Vector3.at(0, 0.51, 0), 1),
|
||||
testCase(Vector3.at(Math.PI / 5, -0.5, 0), 0),
|
||||
testCase(Vector3.at(Math.PI / 5, -0.49, 0), 1),
|
||||
testCase(Vector3.at(Math.PI / 10, 0, 0), 0),
|
||||
testCase(Vector3.at(Math.PI / 10, 0.1, 0), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void circularHyperboloid() {
|
||||
checkExpression("-(z^2/12)+(y^2/4)-(x^2/12)>-0.03",
|
||||
testCase(Vector3.at(0, 0, 0), 1),
|
||||
testCase(Vector3.at(0, 1, 0), 1),
|
||||
testCase(Vector3.at(0, 1, 1), 1),
|
||||
testCase(Vector3.at(1, 1, 1), 1),
|
||||
testCase(Vector3.at(0, 0, 1), 0),
|
||||
testCase(Vector3.at(1, 0, 1), 0));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user