1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-16 18:19:55 +00:00
CC-Tweaked/projects/core/src/test/java/dan200/computercraft/core/computer/ComputerBootstrap.java
Jonathan Coates 367773e173
Some refactoring of mounts
- Separate FileMount into separate FileMount and WritableFileMount
   classes. This separates the (relatively simple) read-only code from
   the (soon to be even more complex) read/write code.

   It also allows you to create read-only mounts which don't bother with
   filesystem accounting, which is nice.

 - Make openForWrite/openForAppend always return a SeekableFileHandle.
   Appendable files still cannot be seeked within, but that check is now
   done on the FS side.

 - Refactor the various mount tests to live in test contract interfaces,
   allowing us to reuse them between mounts.

 - Clean up our error handling a little better. (Most) file-specific code
   has been moved to FileMount, and ArchiveMount-derived classes now
   throw correct path-localised exceptions.
2022-12-09 22:02:31 +00:00

140 lines
4.9 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.computer;
import dan200.computercraft.api.filesystem.WritableMount;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.ComputerContext;
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.computer.mainthread.MainThread;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.test.core.computer.BasicEnvironment;
import dan200.computercraft.test.core.filesystem.MemoryMount;
import dan200.computercraft.test.core.filesystem.ReadOnlyWritableMount;
import org.junit.jupiter.api.Assertions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Helper class to run a program on a computer.
*/
public class ComputerBootstrap {
private static final Logger LOG = LoggerFactory.getLogger(ComputerBootstrap.class);
private static final int TPS = 20;
public static final int MAX_TIME = 10;
public static void run(String program, Consumer<Computer> setup, int maxTimes) {
var mount = new MemoryMount()
.addFile("test.lua", program)
.addFile("startup.lua", "assertion.assert(pcall(loadfile('test.lua', nil, _ENV))) os.shutdown()");
run(new ReadOnlyWritableMount(mount), setup, maxTimes);
}
public static void run(String program, int maxTimes) {
run(program, x -> {
}, maxTimes);
}
public static void run(WritableMount mount, Consumer<Computer> setup, int maxTicks) {
CoreConfig.maxMainComputerTime = CoreConfig.maxMainGlobalTime = Integer.MAX_VALUE;
var term = new Terminal(51, 19, true);
var mainThread = new MainThread();
var environment = new BasicEnvironment(mount);
var context = new ComputerContext(environment, 1, mainThread);
final var computer = new Computer(context, environment, term, 0);
var api = new AssertApi();
computer.addApi(api);
setup.accept(computer);
try {
computer.turnOn();
var everOn = false;
for (var tick = 0; tick < TPS * maxTicks; tick++) {
var start = System.currentTimeMillis();
computer.tick();
mainThread.tick();
if (api.message != null) {
LOG.debug("Shutting down due to error");
computer.shutdown();
Assertions.fail(api.message);
return;
}
var remaining = (1000 / TPS) - (System.currentTimeMillis() - start);
if (remaining > 0) Thread.sleep(remaining);
// Break if the computer was once on, and is now off.
everOn |= computer.isOn();
if ((everOn || tick > TPS) && !computer.isOn()) break;
}
if (computer.isOn() || !api.didAssert) {
var builder = new StringBuilder().append("Did not correctly [");
if (!api.didAssert) builder.append(" assert");
if (computer.isOn()) builder.append(" shutdown");
builder.append(" ]\n");
for (var line = 0; line < 19; line++) {
builder.append(String.format("%2d | %" + term.getWidth() + "s |\n", line + 1, term.getLine(line)));
}
computer.shutdown();
Assertions.fail(builder.toString());
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
} finally {
try {
context.ensureClosed(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException("Runtime thread was interrupted", e);
}
}
}
public static class AssertApi implements ILuaAPI {
boolean didAssert;
String message;
@Override
public String[] getNames() {
return new String[]{ "assertion" };
}
@LuaFunction
public final void log(IArguments arguments) {
LOG.info("[Computer] {}", Arrays.toString(arguments.getAll()));
}
@LuaFunction("assert")
public final Object[] doAssert(IArguments arguments) throws LuaException {
didAssert = true;
var arg = arguments.get(0);
if (arg == null || arg == Boolean.FALSE) {
message = arguments.optString(1, "Assertion failed");
throw new LuaException(message);
}
return arguments.getAll();
}
}
}