mirror of
https://github.com/Qortal/AT.git
synced 2025-01-31 03:12:14 +00:00
454d4bed35
API is now an abstract class instead of an Interface. This is to allow protected methods that give access to package-scoped methods and variables inside MachineState. MachineState now supports a different set of constants based on AT version. MachineState's methods and variables have had their scopes tightened up and getters/setters added where appropriate. MachineState.parseHeader() inlined into the constructor that calls it so it can set public final variables. Some reordering and additional comments in MachineState. OpCode enum entries refactored so that: a) variables provided via MachineState "state" are not explicitly passed as well b) args to each opcode are pre-fetched from the codeByteBuffer before calling and only need to be cast before use. this comes almost "for free" thanks to OpCodeParam.fetch Calling functions from OpCode now cleaner as there's no write-access to programCounter, only changing codeByteBuffer's position. Also in OpCode, state.getProgramCounter() now provides a consistent before-opcode position for branches, jumps, etc. Lots of repeated code refactored out of unit tests into ExecutableTest class. ExecutableTest also provides helper methods for examining post-execution data values, stack positions and entries.
109 lines
3.2 KiB
Java
109 lines
3.2 KiB
Java
import static common.TestUtils.hexToBytes;
|
|
import static org.junit.Assert.*;
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.util.Arrays;
|
|
|
|
import org.ciyam.at.ExecutionException;
|
|
import org.ciyam.at.FunctionCode;
|
|
import org.ciyam.at.MachineState;
|
|
import org.ciyam.at.OpCode;
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
|
|
import common.TestAPI;
|
|
import common.TestLogger;
|
|
|
|
public class SerializationTests {
|
|
|
|
public TestLogger logger;
|
|
public TestAPI api;
|
|
public MachineState state;
|
|
public ByteBuffer codeByteBuffer;
|
|
|
|
@Before
|
|
public void beforeTest() {
|
|
logger = new TestLogger();
|
|
api = new TestAPI();
|
|
codeByteBuffer = ByteBuffer.allocate(256).order(ByteOrder.LITTLE_ENDIAN);
|
|
}
|
|
|
|
@After
|
|
public void afterTest() {
|
|
codeByteBuffer = null;
|
|
api = null;
|
|
logger = null;
|
|
}
|
|
|
|
private byte[] simulate() {
|
|
// version 0003, reserved 0000, code 0100 * 1, data 0020 * 8, call stack 0010 * 4, user stack 0010 * 4
|
|
byte[] headerBytes = hexToBytes("0300" + "0000" + "0001" + "2000" + "1000" + "1000");
|
|
byte[] codeBytes = codeByteBuffer.array();
|
|
byte[] dataBytes = new byte[0];
|
|
|
|
state = new MachineState(api, logger, headerBytes, codeBytes, dataBytes);
|
|
|
|
return executeAndCheck(state);
|
|
}
|
|
|
|
private byte[] continueSimulation(byte[] savedState) {
|
|
state = MachineState.fromBytes(api, logger, savedState);
|
|
|
|
// Pretend we're on next block
|
|
api.bumpCurrentBlockHeight();
|
|
|
|
return executeAndCheck(state);
|
|
}
|
|
|
|
private byte[] executeAndCheck(MachineState state) {
|
|
state.execute();
|
|
|
|
byte[] stateBytes = state.toBytes();
|
|
MachineState restoredState = MachineState.fromBytes(api, logger, stateBytes);
|
|
byte[] restoredStateBytes = restoredState.toBytes();
|
|
|
|
assertTrue("Serialization->Deserialization->Reserialization error", Arrays.equals(stateBytes, restoredStateBytes));
|
|
|
|
return stateBytes;
|
|
}
|
|
|
|
@Test
|
|
public void testPCS2() throws ExecutionException {
|
|
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("0000000011111111"));
|
|
codeByteBuffer.put(OpCode.SET_PCS.value);
|
|
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("0000000022222222"));
|
|
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
|
|
|
simulate();
|
|
|
|
assertEquals(0x0e, (int) state.getOnStopAddress());
|
|
assertTrue(state.getIsFinished());
|
|
assertFalse(state.getHadFatalError());
|
|
}
|
|
|
|
@Test
|
|
public void testStopWithStacks() throws ExecutionException {
|
|
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(100); // 0000
|
|
codeByteBuffer.put(OpCode.SET_PCS.value); // 000d
|
|
codeByteBuffer.put(OpCode.JMP_SUB.value).putInt(0x002a); // 000e
|
|
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(10); // 0013
|
|
codeByteBuffer.put(OpCode.ADD_DAT.value).putInt(0).putInt(1); // 0020
|
|
codeByteBuffer.put(OpCode.STP_IMD.value); // 0029
|
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.ECHO.value).putInt(0); // 002a
|
|
codeByteBuffer.put(OpCode.PSH_DAT.value).putInt(0); // 0031
|
|
codeByteBuffer.put(OpCode.RET_SUB.value); // 0036
|
|
|
|
byte[] savedState = simulate();
|
|
|
|
assertEquals(0x0e, (int) state.getOnStopAddress());
|
|
assertTrue(state.getIsStopped());
|
|
assertFalse(state.getHadFatalError());
|
|
|
|
savedState = continueSimulation(savedState);
|
|
savedState = continueSimulation(savedState);
|
|
}
|
|
|
|
}
|