Skip to content

Commit 7346a36

Browse files
committed
Add tests for op, fix inconsistencies with Mindustry
1 parent 3fb2806 commit 7346a36

File tree

2 files changed

+330
-3
lines changed

2 files changed

+330
-3
lines changed

src/logic/vm/instructions.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ impl SimpleInstruction for Op {
302302

303303
LogicOp::Shl => ((x as i64).wrapping_shl(y as i64 as u32)).into(),
304304
LogicOp::Shr => ((x as i64).wrapping_shr(y as i64 as u32)).into(),
305-
LogicOp::Ushr => ((x as i64 as u64).wrapping_shr(y as i64 as u32)).into(),
305+
LogicOp::Ushr => (((x as i64 as u64).wrapping_shr(y as i64 as u32)) as i64).into(),
306306
LogicOp::Or => ((x as i64) | (y as i64)).into(),
307307
LogicOp::And => ((x as i64) & (y as i64)).into(),
308308
LogicOp::Xor => ((x as i64) ^ (y as i64)).into(),
@@ -323,13 +323,15 @@ impl SimpleInstruction for Op {
323323
}
324324
LogicOp::Noise => SIMPLEX.get([x, y]).into(),
325325
LogicOp::Abs => x.abs().into(),
326-
LogicOp::Sign => x.signum().into(),
326+
// https://github.com/rust-lang/rust/issues/57543
327+
LogicOp::Sign => (if x == 0. { 0. } else { x.signum() }).into(),
327328
LogicOp::Log => x.ln().into(),
328329
LogicOp::Logn => x.log(y).into(),
329330
LogicOp::Log10 => x.log10().into(),
330331
LogicOp::Floor => x.floor().into(),
331332
LogicOp::Ceil => x.ceil().into(),
332-
LogicOp::Round => x.round().into(),
333+
// java's Math.round rounds toward +inf, but rust's f64::round rounds away from 0
334+
LogicOp::Round => (x + 0.5).floor().into(),
333335
LogicOp::Sqrt => x.sqrt().into(),
334336
LogicOp::Rand => (rand::random::<f64>() * x).into(),
335337

src/logic/vm/mod.rs

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ mod tests {
327327
set pi_fancy π
328328
set e @e
329329
noop
330+
set a 1e308
331+
set b 1e309
332+
set c -1e308
333+
set d -1e309
330334
"#,
331335
);
332336

@@ -373,6 +377,16 @@ mod tests {
373377
LValue::Number(variables::E.into())
374378
);
375379
});
380+
381+
vm.do_tick(Duration::ZERO);
382+
vm.do_tick(Duration::ZERO);
383+
384+
with_processor(&mut vm, 0, |p| {
385+
assert_eq!(p.state.variables["a"].get(&p.state), LValue::Number(1e308));
386+
assert_eq!(p.state.variables["b"].get(&p.state), LValue::Null);
387+
assert_eq!(p.state.variables["c"].get(&p.state), LValue::Number(-1e308));
388+
assert_eq!(p.state.variables["d"].get(&p.state), LValue::Null);
389+
});
376390
}
377391

378392
#[test]
@@ -810,4 +824,315 @@ mod tests {
810824
);
811825
}
812826
}
827+
828+
#[test]
829+
#[allow(clippy::approx_constant)]
830+
fn test_op_unary() {
831+
for (op, x, want) in [
832+
// not
833+
("not", "0b00", (-1).into()),
834+
("not", "0b01", (-2).into()),
835+
("not", "0b10", (-3).into()),
836+
("not", "0b11", (-4).into()),
837+
("not", "-1", 0.into()),
838+
("not", "-2", 1.into()),
839+
("not", "-3", 2.into()),
840+
("not", "-4", 3.into()),
841+
// abs
842+
("abs", "0", 0.into()),
843+
("abs", "-0", 0.into()),
844+
("abs", "1", 1.into()),
845+
("abs", "-1", 1.into()),
846+
("abs", "1e308", 1e308.into()),
847+
("abs", "1e309", 0.into()),
848+
("abs", "-1e308", 1e308.into()),
849+
("abs", "-1e309", 0.into()),
850+
// sign
851+
("sign", "0", 0.into()),
852+
("sign", "-0", 0.into()),
853+
("sign", "1", 1.into()),
854+
("sign", "-1", (-1).into()),
855+
("sign", "1e308", 1.into()),
856+
("sign", "1e309", 0.into()),
857+
("sign", "-1e308", (-1).into()),
858+
("sign", "-1e309", 0.into()),
859+
// log
860+
("log", "-1", LValue::Null),
861+
("log", "0", LValue::Null),
862+
("log", "@e", 0.99999996963214.into()),
863+
("log", "2", 0.6931471805599453.into()),
864+
// log10
865+
("log10", "-1", LValue::Null),
866+
("log10", "0", LValue::Null),
867+
("log10", "100", 2.into()),
868+
("log10", "101", 2.0043213737826426.into()),
869+
// floor
870+
("floor", "0", 0.into()),
871+
("floor", "1", 1.into()),
872+
("floor", "1.5", 1.into()),
873+
("floor", "-1", (-1).into()),
874+
("floor", "-1.5", (-2).into()),
875+
// ceil
876+
("ceil", "0", 0.into()),
877+
("ceil", "1", 1.into()),
878+
("ceil", "1.5", 2.into()),
879+
("ceil", "-1", (-1).into()),
880+
("ceil", "-1.5", (-1).into()),
881+
// round
882+
("round", "0", 0.into()),
883+
("round", "1", 1.into()),
884+
("round", "1.1", 1.into()),
885+
("round", "1.49", 1.into()),
886+
("round", "1.5", 2.into()),
887+
("round", "1.51", 2.into()),
888+
("round", "1.9", 2.into()),
889+
("round", "-1", (-1).into()),
890+
("round", "-1.1", (-1).into()),
891+
("round", "-1.49", (-1).into()),
892+
("round", "-1.5", (-1).into()),
893+
("round", "-1.51", (-2).into()),
894+
("round", "-1.9", (-2).into()),
895+
// sqrt
896+
("sqrt", "-1", LValue::Null),
897+
("sqrt", "-0.25", LValue::Null),
898+
("sqrt", "0", 0.into()),
899+
("sqrt", "0.25", 0.5.into()),
900+
("sqrt", "1", 1.into()),
901+
("sqrt", "4", 2.into()),
902+
// sin
903+
("sin", "-30", (-0.49999999999999994).into()),
904+
("sin", "0", 0.into()),
905+
("sin", "30", 0.49999999999999994.into()),
906+
("sin", "45", 0.7071067811865476.into()),
907+
("sin", "60", 0.8660254037844386.into()),
908+
("sin", "90", 1.into()),
909+
("sin", "180", 1.2246467991473532e-16.into()),
910+
// cos
911+
("cos", "-30", 0.8660254037844387.into()),
912+
("cos", "0", 1.into()),
913+
("cos", "30", 0.8660254037844387.into()),
914+
("cos", "45", 0.7071067811865476.into()),
915+
("cos", "60", 0.5000000000000001.into()),
916+
("cos", "90", 6.123233995736766e-17.into()),
917+
("cos", "180", (-1).into()),
918+
// tan
919+
("tan", "0", 0.into()),
920+
("tan", "45", 0.9999999999999999.into()),
921+
("tan", "90", 16331239353195370i64.into()),
922+
("tan", "91", (-57.28996163075955).into()),
923+
// asin
924+
("asin", "-0.5", (-30.000000000000004).into()),
925+
("asin", "0", 0.into()),
926+
("asin", "0.5", 30.000000000000004.into()),
927+
// acos
928+
("acos", "-0.5", 120.00000000000001.into()),
929+
("acos", "0", 90.into()),
930+
("acos", "0.5", 60.00000000000001.into()),
931+
// atan
932+
("atan", "-0.5", (-26.56505117707799).into()),
933+
("atan", "0", 0.into()),
934+
("atan", "0.5", 26.56505117707799.into()),
935+
] {
936+
let mut vm = single_processor_vm(
937+
BlockType::HyperProcessor,
938+
&format!(
939+
"
940+
op {op} got {x}
941+
stop
942+
"
943+
),
944+
);
945+
946+
run(&mut vm, 1, true);
947+
948+
let state = take_processor(&mut vm, 0).state;
949+
assert_eq!(state.variables["got"].get(&state), want, "{op} {x}");
950+
}
951+
}
952+
953+
#[test]
954+
fn test_op_binary() {
955+
for (op, x, y, want) in [
956+
// add
957+
("add", "0", "0", 0.into()),
958+
("add", "0", "1", 1.into()),
959+
("add", "1.5", "0.25", 1.75.into()),
960+
("add", "1.0", "-2.0", (-1).into()),
961+
// sub
962+
("sub", "3", "1", 2.into()),
963+
("sub", "3", "-1", 4.into()),
964+
// mul
965+
("mul", "1", "0", 0.into()),
966+
("mul", "1", "1", 1.into()),
967+
("mul", "3", "-4.5", (-13.5).into()),
968+
// div
969+
("div", "5", "2", 2.5.into()),
970+
("div", "-5", "2", (-2.5).into()),
971+
("div", "5", "-2", (-2.5).into()),
972+
("div", "-5", "-2", 2.5.into()),
973+
("div", "1", "0", LValue::Null),
974+
("div", "-1", "0", LValue::Null),
975+
("div", "0", "0", LValue::Null),
976+
("div", "0", "1", 0.into()),
977+
("div", "0", "-1", 0.into()),
978+
// idiv
979+
("idiv", "5", "2", 2.into()),
980+
("idiv", "-5", "2", (-3).into()),
981+
("idiv", "5", "-2", (-3).into()),
982+
("idiv", "-5", "-2", 2.into()),
983+
("idiv", "1", "0", LValue::Null),
984+
("idiv", "-1", "0", LValue::Null),
985+
("idiv", "0", "0", LValue::Null),
986+
("idiv", "0", "1", 0.into()),
987+
("idiv", "0", "-1", 0.into()),
988+
// mod
989+
("mod", "5", "2", 1.into()),
990+
("mod", "-5", "2", (-1).into()),
991+
("mod", "5", "-2", 1.into()),
992+
("mod", "-5", "-2", (-1).into()),
993+
// emod
994+
("emod", "5", "2", 1.into()),
995+
("emod", "-5", "2", 1.into()),
996+
("emod", "5", "-2", (-1).into()),
997+
("emod", "-5", "-2", (-1).into()),
998+
// pow
999+
("pow", "3", "2", 9.into()),
1000+
("pow", "9", "0.5", 3.into()),
1001+
("pow", "16", "-0.5", 0.25.into()),
1002+
("pow", "-3", "2", 9.into()),
1003+
("pow", "-9", "0.5", LValue::Null),
1004+
("pow", "-16", "-0.5", LValue::Null),
1005+
// land
1006+
("land", "false", "false", 0.into()),
1007+
("land", "false", "true", 0.into()),
1008+
("land", "true", "false", 0.into()),
1009+
("land", "true", "true", 1.into()),
1010+
("land", "\"foo\"", "null", 0.into()),
1011+
("land", "\"foo\"", "\"bar\"", 1.into()),
1012+
// shl
1013+
("shl", "2", "0", 2.into()),
1014+
("shl", "2", "1", 4.into()),
1015+
("shl", "2", "62", (-9223372036854775808i64).into()),
1016+
("shl", "2", "63", 0.into()),
1017+
("shl", "2", "64", 2.into()),
1018+
("shl", "2", "-1", 0.into()),
1019+
("shl", "2", "-60", 32.into()),
1020+
("shl", "-2", "0", (-2).into()),
1021+
("shl", "-2", "1", (-4).into()),
1022+
("shl", "-2", "62", (-9223372036854775808i64).into()),
1023+
("shl", "-2", "63", 0.into()),
1024+
("shl", "-2", "64", (-2).into()),
1025+
("shl", "-2", "-1", 0.into()),
1026+
("shl", "-2", "-60", (-32).into()),
1027+
// shr
1028+
("shr", "2", "0", 2.into()),
1029+
("shr", "2", "1", 1.into()),
1030+
("shr", "2", "62", 0.into()),
1031+
("shr", "2", "63", 0.into()),
1032+
("shr", "2", "64", 2.into()),
1033+
("shr", "2", "-1", 0.into()),
1034+
("shr", "2", "-60", 0.into()),
1035+
("shr", "-2", "0", (-2).into()),
1036+
("shr", "-2", "1", (-1).into()),
1037+
("shr", "-2", "62", (-1).into()),
1038+
("shr", "-2", "63", (-1).into()),
1039+
("shr", "-2", "64", (-2).into()),
1040+
("shr", "-2", "-1", (-1).into()),
1041+
("shr", "-2", "-60", (-1).into()),
1042+
// ushr
1043+
("ushr", "2", "0", 2.into()),
1044+
("ushr", "2", "1", 1.into()),
1045+
("ushr", "2", "62", 0.into()),
1046+
("ushr", "2", "63", 0.into()),
1047+
("ushr", "2", "64", 2.into()),
1048+
("ushr", "2", "-1", 0.into()),
1049+
("ushr", "2", "-60", 0.into()),
1050+
("ushr", "-2", "0", (-2).into()),
1051+
("ushr", "-2", "1", 9223372036854775807i64.into()),
1052+
("ushr", "-2", "62", 3.into()),
1053+
("ushr", "-2", "63", 1.into()),
1054+
("ushr", "-2", "64", (-2).into()),
1055+
("ushr", "-2", "-1", 1.into()),
1056+
("ushr", "-2", "-60", 1152921504606846976i64.into()),
1057+
// or
1058+
("or", "0b10", "0b10", 0b10.into()),
1059+
("or", "0b10", "0b11", 0b11.into()),
1060+
("or", "0b11", "0b10", 0b11.into()),
1061+
("or", "0b11", "0b11", 0b11.into()),
1062+
("or", "-1", "0", (-1).into()),
1063+
("or", "-1", "1", (-1).into()),
1064+
// and
1065+
("and", "0b10", "0b10", 0b10.into()),
1066+
("and", "0b10", "0b11", 0b10.into()),
1067+
("and", "0b11", "0b10", 0b10.into()),
1068+
("and", "0b11", "0b11", 0b11.into()),
1069+
("and", "-1", "0", 0.into()),
1070+
("and", "-1", "1", 1.into()),
1071+
// xor
1072+
("xor", "0b10", "0b10", 0b00.into()),
1073+
("xor", "0b10", "0b11", 0b01.into()),
1074+
("xor", "0b11", "0b10", 0b01.into()),
1075+
("xor", "0b11", "0b11", 0b00.into()),
1076+
("xor", "-1", "0", (-1).into()),
1077+
("xor", "-1", "1", (-2).into()),
1078+
// max
1079+
("max", "-1", "1", 1.into()),
1080+
("max", "1", "-1", 1.into()),
1081+
("max", "1", "2", 2.into()),
1082+
("max", "2", "1", 2.into()),
1083+
// min
1084+
("min", "-1", "1", (-1).into()),
1085+
("min", "1", "-1", (-1).into()),
1086+
("min", "1", "2", 1.into()),
1087+
("min", "2", "1", 1.into()),
1088+
// angle
1089+
// mindustry apparently uses a different algorithm that gives 29.999948501586914 instead of 30
1090+
// but this is probably close enough
1091+
("angle", "0.8660254038", "0.5", 30.into()),
1092+
// angleDiff
1093+
("angleDiff", "10", "10", 0.into()),
1094+
("angleDiff", "10", "20", 10.into()),
1095+
("angleDiff", "10", "-10", 20.into()),
1096+
("angleDiff", "10", "350", 20.into()),
1097+
// len
1098+
("len", "3", "4", 5.into()),
1099+
("len", "1", "1", 2f32.sqrt().into()),
1100+
// noise
1101+
("noise", "0", "0", 0.into()),
1102+
// i'm not porting mindustry's noise algorithm. this is not the value you would get ingame
1103+
("noise", "0", "1", (-0.7139277281035279).into()),
1104+
("noise", "1", "0", (-0.3646124062135936).into()),
1105+
// logn
1106+
("logn", "-1", "2", LValue::Null),
1107+
("logn", "0", "2", LValue::Null),
1108+
("logn", "0b1000", "2", 3.into()),
1109+
("logn", "0b1010", "2", 3.3219280948873626.into()),
1110+
("logn", "-1", "10", LValue::Null),
1111+
("logn", "0", "10", LValue::Null),
1112+
("logn", "100", "10", 2.into()),
1113+
("logn", "101", "10", 2.0043213737826426.into()),
1114+
]
1115+
.into_iter()
1116+
.chain(
1117+
CONDITION_TESTS
1118+
.iter()
1119+
.filter(|&(op, ..)| *op != "always")
1120+
.map(|&(op, x, y, want)| (op, x, y, want.into())),
1121+
) {
1122+
let mut vm = single_processor_vm(
1123+
BlockType::HyperProcessor,
1124+
&format!(
1125+
"
1126+
op {op} got {x} {y}
1127+
stop
1128+
"
1129+
),
1130+
);
1131+
1132+
run(&mut vm, 1, true);
1133+
1134+
let state = take_processor(&mut vm, 0).state;
1135+
assert_eq!(state.variables["got"].get(&state), want, "{op} {x} {y}");
1136+
}
1137+
}
8131138
}

0 commit comments

Comments
 (0)