mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-03-03 18:25:18 +00:00
Add some tests for the new executor system
And fix a couple of bugs picked up by said tests
This commit is contained in:
parent
4b741739e8
commit
8819f2559d
@ -197,3 +197,9 @@ gradle.projectsEvaluated {
|
|||||||
|
|
||||||
runClient.outputs.upToDateWhen { false }
|
runClient.outputs.upToDateWhen { false }
|
||||||
runServer.outputs.upToDateWhen { false }
|
runServer.outputs.upToDateWhen { false }
|
||||||
|
|
||||||
|
test {
|
||||||
|
testLogging {
|
||||||
|
events "failed", "standardOut", "standardError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -44,10 +44,12 @@ public interface ILuaObject
|
|||||||
* for the possible values and conversion rules.
|
* for the possible values and conversion rules.
|
||||||
* @return An array of objects, representing the values you wish to return to the Lua program. See
|
* @return An array of objects, representing the values you wish to return to the Lua program. See
|
||||||
* {@link MethodResult#of(Object...)} for the valid values and conversion rules.
|
* {@link MethodResult#of(Object...)} for the valid values and conversion rules.
|
||||||
* @throws LuaException If the task could not be queued, or if the task threw an exception.
|
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||||
|
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||||
|
* arguments are supplied to your method.
|
||||||
* @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended,
|
* @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended,
|
||||||
* InterruptedException will be thrown. This exception must not be caught or
|
* InterruptedException will be thrown. This exception must not be caught or
|
||||||
* intercepted, or the computer will leak memory and end up in a broken state.w
|
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||||
* @see IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])
|
* @see IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])
|
||||||
* @deprecated Use {@link #callMethod(ICallContext, int, Object[])} instead.
|
* @deprecated Use {@link #callMethod(ICallContext, int, Object[])} instead.
|
||||||
*/
|
*/
|
||||||
@ -68,12 +70,14 @@ public interface ILuaObject
|
|||||||
* for the possible values and conversion rules.
|
* for the possible values and conversion rules.
|
||||||
* @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or
|
* @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or
|
||||||
* {@link MethodResult#of(Object...)} to return several values.
|
* {@link MethodResult#of(Object...)} to return several values.
|
||||||
* @throws LuaException If the task could not be queued, or if the task threw an exception.
|
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||||
|
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||||
|
* arguments are supplied to your method.
|
||||||
* @see IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])
|
* @see IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])
|
||||||
* @see MethodResult
|
* @see MethodResult
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@SuppressWarnings({ "deprecation" })
|
@SuppressWarnings( { "deprecation" } )
|
||||||
default MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
default MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||||
{
|
{
|
||||||
return MethodResult.withLuaContext( lua -> callMethod( lua, method, arguments ) );
|
return MethodResult.withLuaContext( lua -> callMethod( lua, method, arguments ) );
|
||||||
|
@ -89,16 +89,17 @@ public abstract class MethodResult
|
|||||||
* Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more
|
* Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more
|
||||||
* easily with {@link #pullEvent(String, ILuaFunction)}.
|
* easily with {@link #pullEvent(String, ILuaFunction)}.
|
||||||
*
|
*
|
||||||
|
* @param filter The event name to filter on.
|
||||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||||
* parameters.
|
* parameters.
|
||||||
* @see #pullEvent(String, ILuaFunction)
|
* @see #pullEvent(String, ILuaFunction)
|
||||||
* @see #pullEvent()
|
* @see #pullEvent()
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static MethodResult pullEvent( @Nonnull String event )
|
public static MethodResult pullEvent( @Nonnull String filter )
|
||||||
{
|
{
|
||||||
Preconditions.checkNotNull( event, "event cannot be null" );
|
Preconditions.checkNotNull( filter, "event cannot be null" );
|
||||||
return new OnEvent( false, event );
|
return new OnEvent( false, filter );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,6 +109,7 @@ public abstract class MethodResult
|
|||||||
* If you want to listen to a specific event, it's easier to use {@link #pullEvent(String, ILuaFunction)} rather
|
* If you want to listen to a specific event, it's easier to use {@link #pullEvent(String, ILuaFunction)} rather
|
||||||
* than running until the desired event is found.
|
* than running until the desired event is found.
|
||||||
*
|
*
|
||||||
|
* @param callback The function to call when the event is received.
|
||||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||||
* @see #pullEvent()
|
* @see #pullEvent()
|
||||||
* @see #pullEvent(String, ILuaFunction)
|
* @see #pullEvent(String, ILuaFunction)
|
||||||
@ -123,6 +125,8 @@ public abstract class MethodResult
|
|||||||
* Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method to
|
* Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method to
|
||||||
* {@link #pullEvent(String)} and {@link #then(ILuaFunction)}.
|
* {@link #pullEvent(String)} and {@link #then(ILuaFunction)}.
|
||||||
*
|
*
|
||||||
|
* @param filter The event name to filter on.
|
||||||
|
* @param callback The function to call when the event is received.
|
||||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||||
* @see #pullEvent(String)
|
* @see #pullEvent(String)
|
||||||
* @see #pullEvent(ILuaFunction)
|
* @see #pullEvent(ILuaFunction)
|
||||||
@ -151,19 +155,21 @@ public abstract class MethodResult
|
|||||||
* The same as {@link #pullEvent(String)}, except {@code terminated} events are also passed to the callback, instead
|
* The same as {@link #pullEvent(String)}, except {@code terminated} events are also passed to the callback, instead
|
||||||
* of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
* of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
||||||
*
|
*
|
||||||
|
* @param filter The event name to filter on.
|
||||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||||
* parameters.
|
* parameters.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static MethodResult pullEventRaw( @Nonnull String event )
|
public static MethodResult pullEventRaw( @Nonnull String filter )
|
||||||
{
|
{
|
||||||
return new OnEvent( true, event );
|
return new OnEvent( true, filter );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as {@link #pullEvent(ILuaFunction)}, except {@code terminated} events are also passed to the callback,
|
* The same as {@link #pullEvent(ILuaFunction)}, except {@code terminated} events are also passed to the callback,
|
||||||
* instead of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
* instead of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
||||||
*
|
*
|
||||||
|
* @param callback The function to call when the event is received.
|
||||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -178,6 +184,8 @@ public abstract class MethodResult
|
|||||||
* callback, instead of throwing an error. Only use this if you want to prevent program termination, which is not
|
* callback, instead of throwing an error. Only use this if you want to prevent program termination, which is not
|
||||||
* recommended.
|
* recommended.
|
||||||
*
|
*
|
||||||
|
* @param filter The event name to filter on.
|
||||||
|
* @param callback The function to call when the event is received.
|
||||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -237,6 +245,8 @@ public abstract class MethodResult
|
|||||||
*
|
*
|
||||||
* @param context The context to execute with.
|
* @param context The context to execute with.
|
||||||
* @return The resulting values.
|
* @return The resulting values.
|
||||||
|
* @throws LuaException If an error was thrown while executing one of the methods within this future.
|
||||||
|
* @throws InterruptedException If the user shuts down or reboots the computer while the coroutine is suspended.
|
||||||
* @see #withLuaContext(ILuaContextTask)
|
* @see #withLuaContext(ILuaContextTask)
|
||||||
* @deprecated This should not be used except to interface between the two call systems.
|
* @deprecated This should not be used except to interface between the two call systems.
|
||||||
*/
|
*/
|
||||||
|
@ -968,7 +968,7 @@ public class Computer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Computer computer = this;
|
final Computer computer = this;
|
||||||
ITask task = new ITask() {
|
ITask task = new ITask() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -157,11 +157,14 @@ class CobaltLuaContext extends CobaltCallContext implements ILuaContext
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] resume( LuaState state, CobaltLuaMachine machine, Object[] args ) throws LuaError, UnwindThrowable
|
void resume( Object[] args )
|
||||||
{
|
{
|
||||||
values = args;
|
values = args;
|
||||||
resume.signal();
|
resume.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] await( LuaState state, CobaltLuaMachine machine ) throws LuaError, UnwindThrowable
|
||||||
|
{
|
||||||
if( !done )
|
if( !done )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -170,14 +173,12 @@ class CobaltLuaContext extends CobaltCallContext implements ILuaContext
|
|||||||
}
|
}
|
||||||
catch( InterruptedException e )
|
catch( InterruptedException e )
|
||||||
{
|
{
|
||||||
state.debug.onReturn();
|
|
||||||
throw new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
|
throw new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( done )
|
if( done )
|
||||||
{
|
{
|
||||||
state.debug.onReturn();
|
|
||||||
if( exception != null ) throw exception;
|
if( exception != null ) throw exception;
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
@ -235,6 +235,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
}
|
}
|
||||||
catch( LuaError e )
|
catch( LuaError e )
|
||||||
{
|
{
|
||||||
|
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Main thread crashed", e );
|
||||||
m_mainRoutine.abandon();
|
m_mainRoutine.abandon();
|
||||||
m_mainRoutine = null;
|
m_mainRoutine = null;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.MethodResult;
|
import dan200.computercraft.api.lua.MethodResult;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
import org.squiddev.cobalt.debug.DebugFrame;
|
|
||||||
import org.squiddev.cobalt.debug.DebugHandler;
|
|
||||||
import org.squiddev.cobalt.debug.DebugState;
|
import org.squiddev.cobalt.debug.DebugState;
|
||||||
import org.squiddev.cobalt.function.VarArgFunction;
|
import org.squiddev.cobalt.function.VarArgFunction;
|
||||||
|
|
||||||
@ -62,12 +60,19 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
throw new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
|
throw new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify we've a "well formed" future
|
||||||
if( future == null )
|
if( future == null )
|
||||||
{
|
{
|
||||||
ComputerCraft.log.error( "Null result from " + delegate );
|
ComputerCraft.log.error( "Null result from " + delegate );
|
||||||
throw new LuaError( "Java Exception Thrown: Null result" );
|
throw new LuaError( "Java Exception Thrown: Null result" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast path for immediate results
|
||||||
|
if( future instanceof MethodResult.Immediate )
|
||||||
|
{
|
||||||
|
return machine.toValues( ((MethodResult.Immediate) future).getResult() );
|
||||||
|
}
|
||||||
|
|
||||||
State context = new State();
|
State context = new State();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -75,12 +80,11 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
}
|
}
|
||||||
catch( UnwindThrowable e )
|
catch( UnwindThrowable e )
|
||||||
{
|
{
|
||||||
// Push our state onto the stack if need-be.
|
// Push our state onto the stack if need-be. Normally this wouldn't be safe and we
|
||||||
DebugHandler handler = state.debug;
|
// should do this at the very beginning, but we know that we won't be calling anything
|
||||||
DebugState ds = handler.getDebugState();
|
// else which will push to the resume stack.
|
||||||
DebugFrame di = handler.onCall( ds, this );
|
DebugState ds = state.debug.getDebugState();
|
||||||
di.state = context;
|
state.debug.onCall( ds, this, context );
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,11 +92,10 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
@Override
|
@Override
|
||||||
public Varargs resume( LuaState state, State context, Varargs args ) throws LuaError, UnwindThrowable
|
public Varargs resume( LuaState state, State context, Varargs args ) throws LuaError, UnwindThrowable
|
||||||
{
|
{
|
||||||
|
Varargs result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Varargs result = doResume( state, context, args );
|
result = doResume( state, context, args );
|
||||||
state.debug.onReturn();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
catch( LuaError e )
|
catch( LuaError e )
|
||||||
{
|
{
|
||||||
@ -104,6 +107,9 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
state.debug.onReturn();
|
state.debug.onReturn();
|
||||||
throw new LuaError( e );
|
throw new LuaError( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.debug.onReturn();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Varargs doResume( LuaState state, State context, Varargs args ) throws LuaError, UnwindThrowable
|
private Varargs doResume( LuaState state, State context, Varargs args ) throws LuaError, UnwindThrowable
|
||||||
@ -126,7 +132,7 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
if( args.arg( 3 ).toBoolean() )
|
if( args.arg( 3 ).toBoolean() )
|
||||||
{
|
{
|
||||||
// Extract the return values from the event and return them
|
// Extract the return values from the event and return them
|
||||||
return runCallback( state, context, CobaltLuaMachine.toObjects( args, 4 ) );
|
return runFuture( state, context, context.taskResult );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -142,7 +148,8 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
}
|
}
|
||||||
else if( future instanceof MethodResult.WithLuaContext )
|
else if( future instanceof MethodResult.WithLuaContext )
|
||||||
{
|
{
|
||||||
return runCallback( state, context, context.luaContext.resume( state, machine, CobaltLuaMachine.toObjects( args, 1 ) ) );
|
context.luaContext.resume( CobaltLuaMachine.toObjects( args, 1 ) );
|
||||||
|
return runCallback( state, context, context.luaContext.await( state, machine ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -223,6 +230,7 @@ class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltWr
|
|||||||
context.pending = future;
|
context.pending = future;
|
||||||
CobaltLuaContext luaContext = context.luaContext = new CobaltLuaContext( computer, state );
|
CobaltLuaContext luaContext = context.luaContext = new CobaltLuaContext( computer, state );
|
||||||
luaContext.execute( withContext.getConsumer() );
|
luaContext.execute( withContext.getConsumer() );
|
||||||
|
return runCallback( state, context, luaContext.await( state, machine ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
|
import dan200.computercraft.core.filesystem.FileMount;
|
||||||
|
import net.minecraftforge.fml.common.Loader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class FakeComputerEnvironment implements IComputerEnvironment
|
||||||
|
{
|
||||||
|
private final boolean colour;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public FakeComputerEnvironment( int id, boolean colour )
|
||||||
|
{
|
||||||
|
this.colour = colour;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDay()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getTimeOfDay()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isColour()
|
||||||
|
{
|
||||||
|
return colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int assignNewID()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWritableMount createSaveDirMount( String subPath, long capacity )
|
||||||
|
{
|
||||||
|
return new FileMount( new File( "computer/" + subPath ), capacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMount createResourceMount( String domain, String subPath )
|
||||||
|
{
|
||||||
|
String fullPath = "assets/" + domain + "/" + subPath;
|
||||||
|
URL url = ComputerCraft.class.getProtectionDomain().getCodeSource().getLocation();
|
||||||
|
File file = new File( url.getPath(), fullPath );
|
||||||
|
if( !file.exists() ) file = new File( "src/main/resources", fullPath );
|
||||||
|
|
||||||
|
if( !file.exists() ) throw new RuntimeException( "Cannot find ROM in " + file );
|
||||||
|
|
||||||
|
return new FileMount( file, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream createResourceFile( String domain, String subPath )
|
||||||
|
{
|
||||||
|
String fullPath = "assets/" + domain + "/" + subPath;
|
||||||
|
return ComputerCraft.class.getClassLoader().getResourceAsStream( fullPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getComputerSpaceLimit()
|
||||||
|
{
|
||||||
|
return ComputerCraft.computerSpaceLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHostString()
|
||||||
|
{
|
||||||
|
return "ComputerCraft ${version} (Minecraft " + Loader.MC_VERSION + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.lua.*;
|
||||||
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public final class RunOnComputer
|
||||||
|
{
|
||||||
|
public static final int STARTUP_TIMEOUT = 10;
|
||||||
|
public static final int RUN_TIMEOUT = 100;
|
||||||
|
|
||||||
|
public static void run( String task ) throws Exception
|
||||||
|
{
|
||||||
|
run( task, x -> {
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void run( String task, Consumer<Computer> setup ) throws Exception
|
||||||
|
{
|
||||||
|
if( ComputerCraft.log == null ) ComputerCraft.log = LogManager.getLogger( "computercraft" );
|
||||||
|
ComputerCraft.logPeripheralErrors = true;
|
||||||
|
|
||||||
|
// Setup computer
|
||||||
|
Terminal terminal = new Terminal( ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer );
|
||||||
|
Computer computer = new Computer( new FakeComputerEnvironment( 0, true ), terminal, 0 );
|
||||||
|
|
||||||
|
// Register APIS
|
||||||
|
TestAPI api = new TestAPI( computer );
|
||||||
|
computer.addAPI( api );
|
||||||
|
setup.accept( computer );
|
||||||
|
|
||||||
|
// Setup the startup file
|
||||||
|
try( OutputStream stream = computer.getRootMount().openForWrite( "startup.lua" ) )
|
||||||
|
{
|
||||||
|
String program = "" +
|
||||||
|
"local function exec()\n" +
|
||||||
|
" " + task + "\n" +
|
||||||
|
"end\n" +
|
||||||
|
"test.finish(pcall(exec))\n";
|
||||||
|
stream.write( program.getBytes( StandardCharsets.UTF_8 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn on
|
||||||
|
ComputerThread.start();
|
||||||
|
computer.turnOn();
|
||||||
|
|
||||||
|
// Run until shutdown or we timeout
|
||||||
|
boolean everOn = false;
|
||||||
|
int ticks = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
computer.advance( 0.05 );
|
||||||
|
MainThread.executePendingTasks();
|
||||||
|
|
||||||
|
Thread.sleep( 50 );
|
||||||
|
ticks++;
|
||||||
|
everOn |= computer.isOn();
|
||||||
|
}
|
||||||
|
while( (computer.isOn() || (!everOn && ticks < STARTUP_TIMEOUT)) && ticks <= RUN_TIMEOUT );
|
||||||
|
|
||||||
|
// If we never finished (say, startup errored) then print the terminal. Otherwise throw the error
|
||||||
|
// where needed.
|
||||||
|
if( !api.finished )
|
||||||
|
{
|
||||||
|
int height = terminal.getHeight() - 1;
|
||||||
|
while( height >= 0 && terminal.getLine( height ).toString().trim().isEmpty() ) height--;
|
||||||
|
for( int y = 0; y <= height; y++ )
|
||||||
|
{
|
||||||
|
System.out.printf( "%2d | %s\n", y + 1, terminal.getLine( y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
fail( "Computer never finished" );
|
||||||
|
}
|
||||||
|
else if( api.error != null )
|
||||||
|
{
|
||||||
|
fail( api.error );
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputerThread.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestAPI implements ILuaAPI
|
||||||
|
{
|
||||||
|
private final Computer computer;
|
||||||
|
|
||||||
|
private boolean finished = false;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
private TestAPI( Computer computer )
|
||||||
|
{
|
||||||
|
this.computer = computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "test" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String[] getMethodNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "log", "finish" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
|
{
|
||||||
|
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments )
|
||||||
|
{
|
||||||
|
switch( method )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
ComputerCraft.log.info( Objects.toString( arguments.length <= 0 ? null : arguments[0] ) );
|
||||||
|
return MethodResult.empty();
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
if( arguments.length <= 0 || arguments[0] == null || arguments[0] == Boolean.FALSE )
|
||||||
|
{
|
||||||
|
error = Objects.toString( arguments.length <= 1 ? null : arguments[1] );
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
|
computer.shutdown();
|
||||||
|
return MethodResult.empty();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return MethodResult.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dan200.computercraft.core.lua;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.*;
|
||||||
|
import dan200.computercraft.core.computer.RunOnComputer;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith( Parameterized.class )
|
||||||
|
public class CobaltWrapperFunctionTest
|
||||||
|
{
|
||||||
|
@Parameterized.Parameter( 0 )
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
@Parameterized.Parameter( 1 )
|
||||||
|
public String code;
|
||||||
|
|
||||||
|
@Parameterized.Parameters( name = "{0}" )
|
||||||
|
public static Collection<Object[]> parameters()
|
||||||
|
{
|
||||||
|
return Arrays.stream( new Object[][]{
|
||||||
|
new Object[]{ "empty", "assert(select('#', funcs.empty()) == 0)" },
|
||||||
|
new Object[]{ "identity", "assert(select('#', funcs.identity(1, 2, 3)) == 3)" },
|
||||||
|
|
||||||
|
new Object[]{ "pullEvent", "os.queueEvent('test') assert(funcs.pullEvent() == 'test')" },
|
||||||
|
new Object[]{ "pullEventTerminate", "os.queueEvent('terminate') assert(not pcall(funcs.pullEvent))" },
|
||||||
|
|
||||||
|
new Object[]{ "pullEventRaw", "os.queueEvent('test') assert(funcs.pullEventRaw() == 'test')" },
|
||||||
|
new Object[]{ "pullEventRawTerminate", "os.queueEvent('terminate') assert(funcs.pullEventRaw() == 'terminate')" },
|
||||||
|
|
||||||
|
new Object[]{ "mainThread", "assert(funcs.mainThread() == 1)" },
|
||||||
|
new Object[]{ "mainThreadMany", "for i = 1, 4 do assert(funcs.mainThread() == 1) end" }
|
||||||
|
} ).collect( Collectors.toList() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests executing functions defined through the {@link MethodResult} API.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testMethodResult() throws Exception
|
||||||
|
{
|
||||||
|
RunOnComputer.run( code, c -> c.addAPI( new MethodResultAPI() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests executing functions defined through the {@link MethodResult} API called with the
|
||||||
|
* {@link ILuaContext} evaluator.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testMethodResultEvaluate() throws Exception
|
||||||
|
{
|
||||||
|
RunOnComputer.run( code, c -> c.addAPI( new WrapperAPI( new MethodResultAPI() )
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
|
{
|
||||||
|
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||||
|
}
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests using {@link MethodResult#then(ILuaFunction)} afterwards
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testMethodResultThen() throws Exception
|
||||||
|
{
|
||||||
|
RunOnComputer.run( code, c -> c.addAPI( new WrapperAPI( new MethodResultAPI() ) {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||||
|
{
|
||||||
|
return super.callMethod( context, method, arguments )
|
||||||
|
.then( x -> MethodResult.onMainThread( () -> MethodResult.of( x ).then( MethodResult::of ) ) )
|
||||||
|
.then( MethodResult::of );
|
||||||
|
}
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests executing functions defined through the {@link ILuaContext} API.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testLuaContext() throws Exception
|
||||||
|
{
|
||||||
|
RunOnComputer.run( code, c -> c.addAPI( new LuaContextAPI() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests executing functions defined through the {@link ILuaContext} API called with the
|
||||||
|
* {@link MethodResult} evaluator.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testWithLuaContext() throws Exception
|
||||||
|
{
|
||||||
|
RunOnComputer.run( code, c -> c.addAPI( new WrapperAPI( new LuaContextAPI() )
|
||||||
|
{
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings( "deprecation" )
|
||||||
|
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||||
|
{
|
||||||
|
return MethodResult.withLuaContext( c -> callMethod( c, method, arguments ) );
|
||||||
|
}
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodResultAPI implements ILuaAPI
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String[] getNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "funcs" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String[] getMethodNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "empty", "identity", "pullEvent", "pullEventRaw", "mainThread" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
|
{
|
||||||
|
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments )
|
||||||
|
{
|
||||||
|
switch( method )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return MethodResult.empty();
|
||||||
|
case 1:
|
||||||
|
return MethodResult.of( arguments );
|
||||||
|
case 2:
|
||||||
|
return MethodResult.pullEvent( "test" );
|
||||||
|
case 3:
|
||||||
|
return MethodResult.pullEventRaw( "test" );
|
||||||
|
case 4:
|
||||||
|
return MethodResult.onMainThread( () -> MethodResult.of( 1 ) );
|
||||||
|
default:
|
||||||
|
return MethodResult.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LuaContextAPI implements ILuaAPI
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String[] getNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "funcs" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String[] getMethodNames()
|
||||||
|
{
|
||||||
|
return new String[]{ "empty", "identity", "pullEvent", "pullEventRaw", "mainThread" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
|
{
|
||||||
|
switch( method )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return null;
|
||||||
|
case 1:
|
||||||
|
return arguments;
|
||||||
|
case 2:
|
||||||
|
return context.pullEvent( "test" );
|
||||||
|
case 3:
|
||||||
|
return context.pullEventRaw( "test" );
|
||||||
|
case 4:
|
||||||
|
return context.executeMainThreadTask( () -> new Object[]{ 1 } );
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WrapperAPI implements ILuaAPI
|
||||||
|
{
|
||||||
|
private final ILuaAPI api;
|
||||||
|
|
||||||
|
public WrapperAPI( ILuaAPI api )
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getNames()
|
||||||
|
{
|
||||||
|
return api.getNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String[] getMethodNames()
|
||||||
|
{
|
||||||
|
return api.getMethodNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
|
{
|
||||||
|
return api.callMethod( context, method, arguments );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||||
|
{
|
||||||
|
return api.callMethod( context, method, arguments );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,9 @@ import com.google.common.collect.Maps;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.lua.ICallContext;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.MethodResult;
|
||||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||||
import dan200.computercraft.api.network.wired.IWiredNetwork;
|
import dan200.computercraft.api.network.wired.IWiredNetwork;
|
||||||
import dan200.computercraft.api.network.wired.IWiredNetworkChange;
|
import dan200.computercraft.api.network.wired.IWiredNetworkChange;
|
||||||
@ -249,7 +250,7 @@ public class NetworkTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Takes a long time to run, mostly for stress testing")
|
@Ignore( "Takes a long time to run, mostly for stress testing" )
|
||||||
public void testLarge()
|
public void testLarge()
|
||||||
{
|
{
|
||||||
final int BRUTE_SIZE = 16;
|
final int BRUTE_SIZE = 16;
|
||||||
@ -410,11 +411,19 @@ public class NetworkTest
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
@Deprecated
|
||||||
|
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments )
|
||||||
{
|
{
|
||||||
return new Object[0];
|
return new Object[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments )
|
||||||
|
{
|
||||||
|
return MethodResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( @Nullable IPeripheral other )
|
public boolean equals( @Nullable IPeripheral other )
|
||||||
{
|
{
|
||||||
@ -427,7 +436,7 @@ public class NetworkTest
|
|||||||
private final int size;
|
private final int size;
|
||||||
private final T[] box;
|
private final T[] box;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings( "unchecked" )
|
||||||
public Grid( int size )
|
public Grid( int size )
|
||||||
{
|
{
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user