Add tests for metatag operations.

This commit is contained in:
James Roseborough
2010-08-25 00:43:13 +00:00
parent 7e2c2db59c
commit d5b58107c6
6 changed files with 404 additions and 30 deletions

View File

@@ -21,14 +21,12 @@
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.TwoArgFunction;
import java.lang.reflect.InvocationTargetException;
import junit.framework.TestCase;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* Tests of basic unary and binary operators on main value types.
*/
@@ -212,6 +210,123 @@ public class UnaryBinaryOperatorsTest extends TestCase {
assertEquals(LuaValue.NIL.eq(da),LuaValue.FALSE);
}
private static final TwoArgFunction RETURN_NIL = new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return NIL;
}
};
private static final TwoArgFunction RETURN_ONE = new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return ONE;
}
};
public void testEqualsMetatag() {
LuaValue tru = LuaValue.TRUE;
LuaValue fal = LuaValue.FALSE;
LuaValue zer = LuaValue.ZERO;
LuaValue one = LuaValue.ONE;
LuaValue abc = LuaValue.valueOf("abcdef").substring(0,3);
LuaValue def = LuaValue.valueOf("abcdef").substring(3,6);
LuaValue pi = LuaValue.valueOf(Math.PI);
LuaValue ee = LuaValue.valueOf(Math.E);
LuaValue tbl = new LuaTable();
LuaValue tbl2 = new LuaTable();
LuaValue tbl3 = new LuaTable();
LuaValue nilb = LuaValue.valueOf( LuaValue.NIL.toboolean() );
LuaValue oneb = LuaValue.valueOf( LuaValue.ONE.toboolean() );
assertEquals( LuaValue.FALSE, nilb );
assertEquals( LuaValue.TRUE, oneb );
LuaValue smt = LuaString.s_metatable;
try {
// always return nil0
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } );
LuaNumber.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } );
LuaString.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } );
tbl.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ));
tbl2.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ));
// diff metatag function
tbl3.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ));
// same type, same value, does not invoke metatag op
assertEquals( tru, tru.eq(tru) );
assertEquals( tru, tbl.eq(tbl) );
assertEquals( tru, one.eq(one) );
// same type, different value, same metatag op. comparabile via metatag op
assertEquals( nilb, tbl.eq(tbl2) );
assertEquals( nilb, tbl2.eq(tbl) );
assertEquals( nilb, tru.eq(fal) );
assertEquals( nilb, fal.eq(tru) );
assertEquals( nilb, zer.eq(one) );
assertEquals( nilb, one.eq(zer) );
assertEquals( nilb, pi.eq(ee) );
assertEquals( nilb, ee.eq(pi) );
assertEquals( nilb, pi.eq(one) );
assertEquals( nilb, one.eq(pi) );
assertEquals( nilb, abc.eq(def) );
assertEquals( nilb, def.eq(abc) );
// different types. not comparable
assertEquals( fal, fal.eq(tbl) );
assertEquals( fal, tbl.eq(fal) );
assertEquals( fal, tbl.eq(one) );
assertEquals( fal, one.eq(tbl) );
assertEquals( fal, fal.eq(one) );
assertEquals( fal, one.eq(fal) );
assertEquals( fal, abc.eq(one) );
assertEquals( fal, one.eq(abc) );
// same type, different metatag ops. not comparable
assertEquals( fal, tbl.eq(tbl3) );
assertEquals( fal, tbl3.eq(tbl) );
// always use right argument
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } );
LuaNumber.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } );
LuaString.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } );
tbl.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ));
tbl2.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_ONE, } ));
// diff metatag function
tbl3.setmetatable(LuaValue.tableOf( new LuaValue[] { LuaValue.EQ, RETURN_NIL, } ));
// same type, same value, does not invoke metatag op
assertEquals( tru, tru.eq(tru) );
assertEquals( tru, tbl.eq(tbl) );
assertEquals( tru, one.eq(one) );
// same type, different value, same metatag op. comparabile via metatag op
assertEquals( oneb, tbl.eq(tbl2) );
assertEquals( oneb, tbl2.eq(tbl) );
assertEquals( oneb, tru.eq(fal) );
assertEquals( oneb, fal.eq(tru) );
assertEquals( oneb, zer.eq(one) );
assertEquals( oneb, one.eq(zer) );
assertEquals( oneb, pi.eq(ee) );
assertEquals( oneb, ee.eq(pi) );
assertEquals( oneb, pi.eq(one) );
assertEquals( oneb, one.eq(pi) );
assertEquals( oneb, abc.eq(def) );
assertEquals( oneb, def.eq(abc) );
// different types. not comparable
assertEquals( fal, fal.eq(tbl) );
assertEquals( fal, tbl.eq(fal) );
assertEquals( fal, tbl.eq(one) );
assertEquals( fal, one.eq(tbl) );
assertEquals( fal, fal.eq(one) );
assertEquals( fal, one.eq(fal) );
assertEquals( fal, abc.eq(one) );
assertEquals( fal, one.eq(abc) );
// same type, different metatag ops. not comparable
assertEquals( fal, tbl.eq(tbl3) );
assertEquals( fal, tbl3.eq(tbl) );
} finally {
LuaBoolean.s_metatable = null;
LuaNumber.s_metatable = null;
LuaString.s_metatable = smt;
}
}
public void testAdd() {
LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44);
LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5);
@@ -236,7 +351,7 @@ public class UnaryBinaryOperatorsTest extends TestCase {
assertEquals(133.125,ia.add(sa).todouble());
assertEquals(133.125,sa.add(ia).todouble());
assertEquals(77.375, da.add(sa).todouble());
assertEquals(77.375, sa.add(da).todouble());
assertEquals(77.375, sa.add(da).todouble());
}
public void testSub() {
@@ -338,6 +453,155 @@ public class UnaryBinaryOperatorsTest extends TestCase {
assertEquals(luaMod(1.5,.25), sa.mod(da).todouble());
}
public void testArithErrors() {
LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44);
LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5);
LuaValue sa=LuaValue.valueOf("22.125"), sb=LuaValue.valueOf("7.25");
String[] ops = { "add", "sub", "mul", "div", "mod", "pow" };
LuaValue[] vals = { LuaValue.NIL, LuaValue.TRUE, LuaValue.tableOf() };
LuaValue[] numerics = { LuaValue.valueOf(111), LuaValue.valueOf(55.25), LuaValue.valueOf("22.125") };
for ( int i=0; i<ops.length; i++ ) {
for ( int j=0; j<vals.length; j++ ) {
for ( int k=0; k<numerics.length; k++ ) {
checkArithError( vals[j], numerics[k], ops[i], vals[j].typename() );
checkArithError( numerics[k], vals[j], ops[i], vals[j].typename() );
}
}
}
}
private void checkArithError(LuaValue a, LuaValue b, String op, String type) {
try {
LuaValue.class.getMethod(op, new Class[] { LuaValue.class }).invoke(a, new Object[] { b });
} catch ( InvocationTargetException ite ) {
String actual = ite.getTargetException().getMessage();
if ( (!actual.startsWith("attempt to perform arithmetic")) || actual.indexOf(type)<0 )
fail( "("+a.typename()+","+op+","+b.typename()+") reported '"+actual+"'" );
} catch ( Exception e ) {
fail( "("+a.typename()+","+op+","+b.typename()+") threw "+e );
}
}
private static final TwoArgFunction RETURN_LHS = new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return lhs;
}
};
private static final TwoArgFunction RETURN_RHS = new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return rhs;
}
};
public void testArithMetatag() {
LuaValue tru = LuaValue.TRUE;
LuaValue fal = LuaValue.FALSE;
LuaValue tbl = new LuaTable();
LuaValue tbl2 = new LuaTable();
try {
try { tru.add(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.mul(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.div(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.pow(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.mod(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
// always use left argument
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.ADD, RETURN_LHS, } );
assertEquals( tru, tru.add(fal) );
assertEquals( tru, tru.add(tbl) );
assertEquals( tbl, tbl.add(tru) );
try { tbl.add(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.SUB, RETURN_LHS, } );
assertEquals( tru, tru.sub(fal) );
assertEquals( tru, tru.sub(tbl) );
assertEquals( tbl, tbl.sub(tru) );
try { tbl.sub(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.add(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.MUL, RETURN_LHS, } );
assertEquals( tru, tru.mul(fal) );
assertEquals( tru, tru.mul(tbl) );
assertEquals( tbl, tbl.mul(tru) );
try { tbl.mul(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.DIV, RETURN_LHS, } );
assertEquals( tru, tru.div(fal) );
assertEquals( tru, tru.div(tbl) );
assertEquals( tbl, tbl.div(tru) );
try { tbl.div(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.POW, RETURN_LHS, } );
assertEquals( tru, tru.pow(fal) );
assertEquals( tru, tru.pow(tbl) );
assertEquals( tbl, tbl.pow(tru) );
try { tbl.pow(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.MOD, RETURN_LHS, } );
assertEquals( tru, tru.mod(fal) );
assertEquals( tru, tru.mod(tbl) );
assertEquals( tbl, tbl.mod(tru) );
try { tbl.mod(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
// always use right argument
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.ADD, RETURN_RHS, } );
assertEquals( fal, tru.add(fal) );
assertEquals( tbl, tru.add(tbl) );
assertEquals( tru, tbl.add(tru) );
try { tbl.add(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.SUB, RETURN_RHS, } );
assertEquals( fal, tru.sub(fal) );
assertEquals( tbl, tru.sub(tbl) );
assertEquals( tru, tbl.sub(tru) );
try { tbl.sub(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.add(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.MUL, RETURN_RHS, } );
assertEquals( fal, tru.mul(fal) );
assertEquals( tbl, tru.mul(tbl) );
assertEquals( tru, tbl.mul(tru) );
try { tbl.mul(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.DIV, RETURN_RHS, } );
assertEquals( fal, tru.div(fal) );
assertEquals( tbl, tru.div(tbl) );
assertEquals( tru, tbl.div(tru) );
try { tbl.div(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.POW, RETURN_RHS, } );
assertEquals( fal, tru.pow(fal) );
assertEquals( tbl, tru.pow(tbl) );
assertEquals( tru, tbl.pow(tru) );
try { tbl.pow(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.MOD, RETURN_RHS, } );
assertEquals( fal, tru.mod(fal) );
assertEquals( tbl, tru.mod(tbl) );
assertEquals( tru, tbl.mod(tru) );
try { tbl.mod(tbl2); fail("did not throw error"); } catch ( LuaError le ) { };
try { tru.sub(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
} finally {
LuaBoolean.s_metatable = null;
}
}
public void testCompareStrings() {
// these are lexical compare!
LuaValue sa=LuaValue.valueOf("-1.5");
@@ -459,6 +723,90 @@ public class UnaryBinaryOperatorsTest extends TestCase {
assertEquals(1.5!=.25, sa.neq_b(da));
}
public void testCompareErrors() {
LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44);
LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5);
LuaValue sa=LuaValue.valueOf("22.125"), sb=LuaValue.valueOf("7.25");
String[] ops = { "lt", "lteq", };
LuaValue[] vals = { LuaValue.NIL, LuaValue.TRUE, LuaValue.tableOf() };
LuaValue[] numerics = { LuaValue.valueOf(111), LuaValue.valueOf(55.25), LuaValue.valueOf("22.125") };
for ( int i=0; i<ops.length; i++ ) {
for ( int j=0; j<vals.length; j++ ) {
for ( int k=0; k<numerics.length; k++ ) {
checkCompareError( vals[j], numerics[k], ops[i], vals[j].typename() );
checkCompareError( numerics[k], vals[j], ops[i], vals[j].typename() );
}
}
}
}
private void checkCompareError(LuaValue a, LuaValue b, String op, String type) {
try {
LuaValue.class.getMethod(op, new Class[] { LuaValue.class }).invoke(a, new Object[] { b });
} catch ( InvocationTargetException ite ) {
String actual = ite.getTargetException().getMessage();
if ( (!actual.startsWith("attempt to compare")) || actual.indexOf(type)<0 )
fail( "("+a.typename()+","+op+","+b.typename()+") reported '"+actual+"'" );
} catch ( Exception e ) {
fail( "("+a.typename()+","+op+","+b.typename()+") threw "+e );
}
}
public void testCompareMetatag() {
LuaValue tru = LuaValue.TRUE;
LuaValue fal = LuaValue.FALSE;
LuaValue tbl = new LuaTable();
LuaValue tbl2 = new LuaTable();
LuaValue tbl3 = new LuaTable();
try {
// always use left argument
LuaValue mt = LuaValue.tableOf( new LuaValue[] {
LuaValue.LT, RETURN_LHS,
LuaValue.LE, RETURN_RHS,
} );
LuaBoolean.s_metatable = mt;
tbl.setmetatable(mt);
tbl2.setmetatable(mt);
assertEquals( tru, tru.lt(fal) );
assertEquals( fal, fal.lt(tru) );
assertEquals( tbl, tbl.lt(tbl2) );
assertEquals( tbl2, tbl2.lt(tbl) );
try { tbl.lt(tbl3); fail("did not throw error"); } catch ( LuaError le ) { };
try { tbl3.lt(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
assertEquals( fal, tru.lteq(fal) );
assertEquals( tru, fal.lteq(tru) );
assertEquals( tbl2, tbl.lteq(tbl2) );
assertEquals( tbl, tbl2.lteq(tbl) );
try { tbl.lteq(tbl3); fail("did not throw error"); } catch ( LuaError le ) { };
try { tbl3.lteq(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
// always use right argument
mt = LuaValue.tableOf( new LuaValue[] {
LuaValue.LT, RETURN_RHS,
LuaValue.LE, RETURN_LHS } );
LuaBoolean.s_metatable = mt;
tbl.setmetatable(mt);
tbl2.setmetatable(mt);
assertEquals( fal, tru.lt(fal) );
assertEquals( tru, fal.lt(tru) );
assertEquals( tbl2, tbl.lt(tbl2) );
assertEquals( tbl, tbl2.lt(tbl) );
try { tbl.lt(tbl3); fail("did not throw error"); } catch ( LuaError le ) { };
try { tbl3.lt(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
assertEquals( tru, tru.lteq(fal) );
assertEquals( fal, fal.lteq(tru) );
assertEquals( tbl, tbl.lteq(tbl2) );
assertEquals( tbl2, tbl2.lteq(tbl) );
try { tbl.lteq(tbl3); fail("did not throw error"); } catch ( LuaError le ) { };
try { tbl3.lteq(tbl); fail("did not throw error"); } catch ( LuaError le ) { };
} finally {
LuaBoolean.s_metatable = null;
}
}
public void testAnd() {
LuaValue ia=LuaValue.valueOf(3), ib=LuaValue.valueOf(4);
LuaValue da=LuaValue.valueOf(.25), db=LuaValue.valueOf(.5);
@@ -657,12 +1005,7 @@ public class UnaryBinaryOperatorsTest extends TestCase {
LuaValue uda = new LuaUserdata(new Object());
try {
// always use left argument
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] {
LuaValue.CONCAT, new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return lhs;
}
} } );
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.CONCAT, RETURN_LHS } );
assertEquals( tru, tru.concat(tbl) );
assertEquals( tbl, tbl.concat(tru) );
assertEquals( tru, tru.concat(tbl) );
@@ -679,12 +1022,7 @@ public class UnaryBinaryOperatorsTest extends TestCase {
try { ghi.concat(tbl.concat(def.buffer())).value(); fail("did not throw error"); } catch ( LuaError le ) { };
// always use right argument
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] {
LuaValue.CONCAT, new TwoArgFunction() {
public LuaValue call(LuaValue lhs, LuaValue rhs) {
return rhs;
}
} } );
LuaBoolean.s_metatable = LuaValue.tableOf( new LuaValue[] { LuaValue.CONCAT, RETURN_RHS } );
assertEquals( tbl, tru.concat(tbl) );
assertEquals( tru, tbl.concat(tru) );
assertEquals( tbl, tru.concat(tbl.buffer()).value() );
@@ -702,4 +1040,35 @@ public class UnaryBinaryOperatorsTest extends TestCase {
LuaBoolean.s_metatable = null;
}
}
public void testConcatErrors() {
LuaValue ia=LuaValue.valueOf(111), ib=LuaValue.valueOf(44);
LuaValue da=LuaValue.valueOf(55.25), db=LuaValue.valueOf(3.5);
LuaValue sa=LuaValue.valueOf("22.125"), sb=LuaValue.valueOf("7.25");
String[] ops = { "concat" };
LuaValue[] vals = { LuaValue.NIL, LuaValue.TRUE, LuaValue.tableOf() };
LuaValue[] numerics = { LuaValue.valueOf(111), LuaValue.valueOf(55.25), LuaValue.valueOf("22.125") };
for ( int i=0; i<ops.length; i++ ) {
for ( int j=0; j<vals.length; j++ ) {
for ( int k=0; k<numerics.length; k++ ) {
checkConcatError( vals[j], numerics[k], ops[i], vals[j].typename() );
checkConcatError( numerics[k], vals[j], ops[i], vals[j].typename() );
}
}
}
}
private void checkConcatError(LuaValue a, LuaValue b, String op, String type) {
try {
LuaValue.class.getMethod(op, new Class[] { LuaValue.class }).invoke(a, new Object[] { b });
} catch ( InvocationTargetException ite ) {
String actual = ite.getTargetException().getMessage();
if ( (!actual.startsWith("attempt to concatenate")) || actual.indexOf(type)<0 )
fail( "("+a.typename()+","+op+","+b.typename()+") reported '"+actual+"'" );
} catch ( Exception e ) {
fail( "("+a.typename()+","+op+","+b.typename()+") threw "+e );
}
}
}