mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-02-11 08:30:04 +00:00
Add a test which boots a computer and runs forever
Ideally we'd add a couple more tests in the future, but this'll do for now. The bootstrap class is largely yoinked from CCTweaks-Lua, so is a tad ugly. It works though.
This commit is contained in:
parent
f1d10809d5
commit
c373583723
@ -186,7 +186,7 @@ public class Computer
|
||||
}
|
||||
}
|
||||
|
||||
public void advance()
|
||||
public void tick()
|
||||
{
|
||||
synchronized( this )
|
||||
{
|
||||
@ -568,7 +568,7 @@ public class Computer
|
||||
@SuppressWarnings( "unused" )
|
||||
public void advance( double dt )
|
||||
{
|
||||
advance();
|
||||
tick();
|
||||
}
|
||||
|
||||
public static final String[] s_sideNames = IAPIEnvironment.SIDE_NAMES;
|
||||
|
@ -105,7 +105,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
|
||||
public void update()
|
||||
{
|
||||
super.update();
|
||||
m_computer.advance();
|
||||
m_computer.tick();
|
||||
|
||||
m_changedLastFrame = m_computer.pollAndResetChanged() || m_changed;
|
||||
m_changed = false;
|
||||
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2019. 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 dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.core.filesystem.MemoryMount;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* A very basic environment
|
||||
*/
|
||||
public class BasicEnvironment implements IComputerEnvironment
|
||||
{
|
||||
private final IWritableMount mount;
|
||||
|
||||
public BasicEnvironment()
|
||||
{
|
||||
this( new MemoryMount() );
|
||||
}
|
||||
|
||||
public BasicEnvironment( IWritableMount mount )
|
||||
{
|
||||
this.mount = mount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignNewID()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWritableMount createSaveDirMount( String path, long space )
|
||||
{
|
||||
return mount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDay()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTimeOfDay()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isColour()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getComputerSpaceLimit()
|
||||
{
|
||||
return ComputerCraft.computerSpaceLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostString()
|
||||
{
|
||||
return "ComputerCraft ${version} (Minecraft " + Loader.MC_VERSION + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public IMount createResourceMount( String domain, String subPath )
|
||||
{
|
||||
File file = getContainingFile();
|
||||
|
||||
String path = "assets/" + domain + "/" + subPath;
|
||||
|
||||
if( file.isFile() )
|
||||
{
|
||||
try
|
||||
{
|
||||
return new JarMount( file, path );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new UncheckedIOException( e );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File wholeFile = new File( file, path );
|
||||
|
||||
// If we don't exist, walk up the tree looking for resource folders
|
||||
File baseFile = file;
|
||||
while( baseFile != null && !wholeFile.exists() )
|
||||
{
|
||||
baseFile = baseFile.getParentFile();
|
||||
wholeFile = new File( baseFile, "resources/main/" + path );
|
||||
}
|
||||
|
||||
if( !wholeFile.exists() ) throw new IllegalStateException( "Cannot find ROM mount at " + file );
|
||||
|
||||
return new FileMount( wholeFile, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createResourceFile( String domain, String subPath )
|
||||
{
|
||||
return ComputerCraft.class.getClassLoader().getResourceAsStream( "assets/" + domain + "/" + subPath );
|
||||
}
|
||||
|
||||
private static File getContainingFile()
|
||||
{
|
||||
String path = ComputerCraft.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
int bangIndex = path.indexOf( "!" );
|
||||
|
||||
// Plain old file, so step up from dan200.computercraft.
|
||||
if( bangIndex < 0 ) return new File( path );
|
||||
|
||||
path = path.substring( 0, bangIndex );
|
||||
URL url;
|
||||
try
|
||||
{
|
||||
url = new URL( path );
|
||||
}
|
||||
catch( MalformedURLException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return new File( url.toURI() );
|
||||
}
|
||||
catch( URISyntaxException e )
|
||||
{
|
||||
return new File( url.getPath() );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2019. 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.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.ArgumentHelper;
|
||||
import dan200.computercraft.core.filesystem.MemoryMount;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Helper class to run a program on a computer.
|
||||
*/
|
||||
public class ComputerBootstrap
|
||||
{
|
||||
private static final int TPS = 20;
|
||||
private static final int MAX_TIME = 10;
|
||||
|
||||
public static void run( String program )
|
||||
{
|
||||
run( program, -1 );
|
||||
}
|
||||
|
||||
public static void run( String program, int shutdownAfter )
|
||||
{
|
||||
ComputerCraft.logPeripheralErrors = true;
|
||||
ComputerCraft.log = LogManager.getLogger( ComputerCraft.MOD_ID );
|
||||
|
||||
MemoryMount mount = new MemoryMount()
|
||||
.addFile( "test.lua", program )
|
||||
.addFile( "startup", "assertion.assert(pcall(loadfile('test.lua', _ENV))) os.shutdown()" );
|
||||
|
||||
Terminal term = new Terminal( ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer );
|
||||
final Computer computer = new Computer( new BasicEnvironment( mount ), term, 0 );
|
||||
|
||||
AssertApi api = new AssertApi();
|
||||
computer.addAPI( api );
|
||||
|
||||
try
|
||||
{
|
||||
computer.turnOn();
|
||||
boolean everOn = false;
|
||||
|
||||
for( int tick = 0; tick < TPS * MAX_TIME; tick++ )
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
computer.tick();
|
||||
MainThread.executePendingTasks();
|
||||
|
||||
if( api.message != null )
|
||||
{
|
||||
ComputerCraft.log.debug( "Shutting down due to error" );
|
||||
computer.shutdown();
|
||||
Assert.fail( api.message );
|
||||
return;
|
||||
}
|
||||
|
||||
long 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;
|
||||
|
||||
// Shutdown the computer after a period of time
|
||||
if( shutdownAfter > 0 && tick != 0 && tick % shutdownAfter == 0 )
|
||||
{
|
||||
ComputerCraft.log.info( "Shutting down: shutdown after {}", shutdownAfter );
|
||||
computer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
if( computer.isOn() || !api.didAssert )
|
||||
{
|
||||
StringBuilder builder = new StringBuilder().append( "Did not correctly" );
|
||||
if( !api.didAssert ) builder.append( " assert" );
|
||||
if( computer.isOn() ) builder.append( " shutdown" );
|
||||
builder.append( "\n" );
|
||||
|
||||
for( int line = 0; line < 19; line++ )
|
||||
{
|
||||
builder.append( String.format( "%2d | %" + term.getWidth() + "s |\n", line + 1, term.getLine( line ) ) );
|
||||
}
|
||||
|
||||
computer.shutdown();
|
||||
Assert.fail( builder.toString() );
|
||||
}
|
||||
}
|
||||
catch( InterruptedException ignored )
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ComputerThread.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AssertApi implements ILuaAPI
|
||||
{
|
||||
boolean didAssert;
|
||||
String message;
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
return new String[] { "assertion" };
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] { "assert" };
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0: // assert
|
||||
{
|
||||
didAssert = true;
|
||||
|
||||
Object arg = arguments.length >= 1 ? arguments[0] : null;
|
||||
if( arg == null || arg == Boolean.FALSE )
|
||||
{
|
||||
message = ArgumentHelper.optString( arguments, 1, "Assertion failed" );
|
||||
throw new LuaException( message );
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ComputerTest
|
||||
{
|
||||
@Test( timeout = 20_000 )
|
||||
public void testTimeout()
|
||||
{
|
||||
try
|
||||
{
|
||||
ComputerBootstrap.run( "print('Hello') while true do end" );
|
||||
}
|
||||
catch( AssertionError e )
|
||||
{
|
||||
if( e.getMessage().equals( "test.lua:1: Too long without yielding" ) ) return;
|
||||
throw e;
|
||||
}
|
||||
|
||||
Assert.fail( "Expected computer to timeout" );
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Mounts in memory
|
||||
*/
|
||||
public class MemoryMount implements IWritableMount
|
||||
{
|
||||
private final Map<String, byte[]> files = new HashMap<>();
|
||||
private final Set<String> directories = new HashSet<>();
|
||||
|
||||
public MemoryMount()
|
||||
{
|
||||
directories.add( "" );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void makeDirectory( @Nonnull String path )
|
||||
{
|
||||
File file = new File( path );
|
||||
while( file != null )
|
||||
{
|
||||
directories.add( file.getPath() );
|
||||
file = file.getParentFile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete( @Nonnull String path )
|
||||
{
|
||||
if( files.containsKey( path ) )
|
||||
{
|
||||
files.remove( path );
|
||||
}
|
||||
else
|
||||
{
|
||||
directories.remove( path );
|
||||
for( String file : files.keySet().toArray( new String[0] ) )
|
||||
{
|
||||
if( file.startsWith( path ) )
|
||||
{
|
||||
files.remove( file );
|
||||
}
|
||||
}
|
||||
|
||||
File parent = new File( path ).getParentFile();
|
||||
if( parent != null ) delete( parent.getPath() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public OutputStream openForWrite( @Nonnull final String path )
|
||||
{
|
||||
return new ByteArrayOutputStream()
|
||||
{
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
files.put( path, toByteArray() );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public OutputStream openForAppend( @Nonnull final String path ) throws IOException
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream()
|
||||
{
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
files.put( path, toByteArray() );
|
||||
}
|
||||
};
|
||||
|
||||
byte[] current = files.get( path );
|
||||
if( current != null ) stream.write( current );
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingSpace()
|
||||
{
|
||||
return 1000000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
return files.containsKey( path ) || directories.contains( path );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
return directories.contains( path );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> files )
|
||||
{
|
||||
for( String file : this.files.keySet() )
|
||||
{
|
||||
if( file.startsWith( path ) ) files.add( file );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( String path )
|
||||
{
|
||||
throw new RuntimeException( "Not implemented" );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream openForRead( String path )
|
||||
{
|
||||
return new ByteArrayInputStream( files.get( path ) );
|
||||
}
|
||||
|
||||
public MemoryMount addFile( String file, String contents )
|
||||
{
|
||||
files.put( file, contents.getBytes() );
|
||||
return this;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user