mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +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:
		| @@ -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. | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev