diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java index b10ef2f57..eead3b5a2 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java @@ -90,7 +90,6 @@ public final class NetworkHandler } /** - * /** * Register packet, and a thread-unsafe handler for it. * * @param The type of the packet to send. diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java b/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java index c3e0e6d66..49ed8a551 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java +++ b/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java @@ -7,12 +7,18 @@ package dan200.computercraft.ingame.mod; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.command.CommandSource; +import net.minecraft.data.NBTToSNBTConverter; import net.minecraft.server.MinecraftServer; -import net.minecraft.test.TestFunctionInfo; -import net.minecraft.test.TestRegistry; +import net.minecraft.test.*; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import javax.annotation.Nonnull; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.nio.file.Paths; import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice; import static net.minecraft.command.Commands.literal; @@ -32,14 +38,24 @@ class CCTestCommand .then( literal( "export" ).executes( context -> { exportFiles( context.getSource().getServer() ); + Path path = Paths.get( StructureHelper.testStructuresDir ); int total = 0; for( TestFunctionInfo function : TestRegistry.getAllTestFunctions() ) { - total += dispatcher.execute( "test import " + function.getTestName(), context.getSource() ); - total += dispatcher.execute( "test export " + function.getTestName(), context.getSource() ); + ResourceLocation resourcelocation = new ResourceLocation( "minecraft", function.getStructureName() ); + Path input = context.getSource().getLevel().getStructureManager().createPathToStructure( resourcelocation, ".nbt" ); + Path output = NBTToSNBTConverter.convertStructure( input, function.getStructureName(), path ); + if( output != null ) total++; } return total; } ) ) + .then( literal( "runall" ).executes( context -> { + TestRegistry.forgetFailedTests(); + TestResultList result = TestHooks.runTests(); + result.addListener( new Callback( context.getSource(), result ) ); + result.addFailureListener( x -> TestRegistry.rememberFailedTest( x.getTestFunction() ) ); + return 0; + } ) ) ); } @@ -66,4 +82,29 @@ class CCTestCommand throw new UncheckedIOException( e ); } } + + private static class Callback implements ITestCallback + { + private final CommandSource source; + private final TestResultList result; + + public Callback( CommandSource source, TestResultList result ) + { + this.source = source; + this.result = result; + } + + @Override + public void testStructureLoaded( @Nonnull TestTracker tracker ) + { + } + + @Override + public void testFailed( @Nonnull TestTracker tracker ) + { + if( !tracker.isDone() ) return; + + source.sendFailure( new StringTextComponent( result.getFailedRequiredCount() + " required tests failed" ).withStyle( TextFormatting.RED ) ); + } + } } diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java b/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java new file mode 100644 index 000000000..d40d108a6 --- /dev/null +++ b/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java @@ -0,0 +1,139 @@ +package dan200.computercraft.ingame.mod; + +import net.minecraft.client.Minecraft; +import net.minecraft.command.CommandSource; +import net.minecraft.server.MinecraftServer; +import net.minecraft.test.*; +import net.minecraft.util.Rotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.GameRules; +import net.minecraft.world.World; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.server.FMLServerStartedEvent; +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.server.ServerLifecycleHooks; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Collection; +import java.util.stream.Collectors; + +@Mod.EventBusSubscriber( modid = TestMod.MOD_ID ) +public class TestHooks +{ + private static final Logger log = LogManager.getLogger( TestHooks.class ); + + private static TestResultList runningTests = null; + private static int countdown = 20; + + @SubscribeEvent + public static void onRegisterCommands( RegisterCommandsEvent event ) + { + log.info( "Starting server, registering command helpers." ); + TestCommand.register( event.getDispatcher() ); + CCTestCommand.register( event.getDispatcher() ); + } + + @SubscribeEvent + public static void onServerStarted( FMLServerStartedEvent event ) + { + MinecraftServer server = event.getServer(); + GameRules rules = server.getGameRules(); + rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server ); + rules.getRule( GameRules.RULE_WEATHER_CYCLE ).set( false, server ); + rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server ); + + ServerWorld world = event.getServer().getLevel( World.OVERWORLD ); + if( world != null ) world.setDayTime( 6000 ); + + log.info( "Cleaning up after last run" ); + CommandSource source = server.createCommandSourceStack(); + TestUtils.clearAllTests( source.getLevel(), getStart( source ), TestCollection.singleton, 200 ); + + log.info( "Importing files" ); + CCTestCommand.importFiles( server ); + } + + @SubscribeEvent + public static void onServerTick( TickEvent.ServerTickEvent event ) + { + if( event.phase != TickEvent.Phase.START ) return; + + // Let the world settle a bit before starting tests. + countdown--; + if( countdown == 0 && System.getProperty( "cctest.run", "false" ).equals( "true" ) ) startTests(); + + TestCollection.singleton.tick(); + MainThread.INSTANCE.tick(); + + if( runningTests != null && runningTests.isDone() ) finishTests(); + } + + public static TestResultList runTests() + { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + CommandSource source = server.createCommandSourceStack(); + Collection tests = TestRegistry.getAllTestFunctions() + .stream() + .filter( x -> FMLLoader.getDist().isClient() | !x.batchName.startsWith( "client" ) ) + .collect( Collectors.toList() ); + + log.info( "Running {} tests...", tests.size() ); + + Collection batches = TestUtils.groupTestsIntoBatches( tests ); + return new TestResultList( TestUtils.runTestBatches( + batches, getStart( source ), Rotation.NONE, source.getLevel(), TestCollection.singleton, 8 + ) ); + } + + private static void startTests() + { + runningTests = runTests(); + } + + private static void finishTests() + { + log.info( "Finished tests - {} were run", runningTests.getTotalCount() ); + if( runningTests.hasFailedRequired() ) + { + log.error( "{} required tests failed", runningTests.getFailedRequiredCount() ); + } + if( runningTests.hasFailedOptional() ) + { + log.warn( "{} optional tests failed", runningTests.getFailedOptionalCount() ); + } + + if( ServerLifecycleHooks.getCurrentServer().isDedicatedServer() ) + { + log.info( "Stopping server." ); + + // We can't exit in the main thread, as Minecraft registers a shutdown hook which results + // in a deadlock. So we do this weird janky thing! + Thread thread = new Thread( () -> System.exit( runningTests.hasFailedRequired() ? 1 : 0 ) ); + thread.setDaemon( true ); + thread.start(); + } + else + { + shutdownClient(); + } + } + + private static BlockPos getStart( CommandSource source ) + { + BlockPos pos = new BlockPos( source.getPosition() ); + return new BlockPos( pos.getX(), source.getLevel().getHeightmapPos( Heightmap.Type.WORLD_SURFACE, pos ).getY(), pos.getZ() + 3 ); + } + + private static void shutdownClient() + { + Minecraft.getInstance().clearLevel(); + Minecraft.getInstance().stop(); + if( runningTests.hasFailedOptional() ) System.exit( 1 ); + } +} diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java b/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java index a386ca48d..11acbc752 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java +++ b/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java @@ -6,38 +6,23 @@ package dan200.computercraft.ingame.mod; import dan200.computercraft.api.ComputerCraftAPI; -import net.minecraft.command.CommandSource; -import net.minecraft.server.MinecraftServer; -import net.minecraft.test.*; -import net.minecraft.util.Rotation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.GameRules; -import net.minecraft.world.gen.Heightmap; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.TickEvent; +import net.minecraft.test.StructureHelper; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.server.FMLServerStartedEvent; -import net.minecraftforge.fml.server.ServerLifecycleHooks; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; @Mod( TestMod.MOD_ID ) public class TestMod { - public static final Path sourceDir = Paths.get( "../../src/testMod/server-files/" ).toAbsolutePath(); + public static final Path sourceDir = Paths.get( "../../src/testMod/server-files" ).normalize().toAbsolutePath(); public static final String MOD_ID = "cctest"; public static final Logger log = LogManager.getLogger( MOD_ID ); - private TestResultList runningTests = null; - private int countdown = 20; - public TestMod() { log.info( "CC: Test initialised" ); @@ -45,81 +30,5 @@ public class TestMod TestLoader.setup(); StructureHelper.testStructuresDir = sourceDir.resolve( "structures" ).toString(); - - MinecraftForge.EVENT_BUS.addListener( ( RegisterCommandsEvent event ) -> { - log.info( "Starting server, registering command helpers." ); - TestCommand.register( event.getDispatcher() ); - CCTestCommand.register( event.getDispatcher() ); - } ); - - MinecraftForge.EVENT_BUS.addListener( ( FMLServerStartedEvent event ) -> { - MinecraftServer server = event.getServer(); - GameRules rules = server.getGameRules(); - rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server ); - rules.getRule( GameRules.RULE_WEATHER_CYCLE ).set( false, server ); - rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server ); - - log.info( "Cleaning up after last run" ); - CommandSource source = server.createCommandSourceStack(); - TestUtils.clearAllTests( source.getLevel(), getStart( source ), TestCollection.singleton, 200 ); - - log.info( "Importing files" ); - CCTestCommand.importFiles( server ); - } ); - - MinecraftForge.EVENT_BUS.addListener( ( TickEvent.ServerTickEvent event ) -> { - if( event.phase != TickEvent.Phase.START ) return; - - // Let the world settle a bit before starting tests. - countdown--; - if( countdown == 0 && System.getProperty( "cctest.run", "false" ).equals( "true" ) ) startTests(); - - TestCollection.singleton.tick(); - MainThread.INSTANCE.tick(); - - if( runningTests != null && runningTests.isDone() ) finishTests(); - } ); - } - - private void startTests() - { - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - CommandSource source = server.createCommandSourceStack(); - Collection tests = TestRegistry.getAllTestFunctions(); - - log.info( "Running {} tests...", tests.size() ); - runningTests = new TestResultList( TestUtils.runTests( - tests, getStart( source ), Rotation.NONE, source.getLevel(), TestCollection.singleton, 8 - ) ); - } - - private void finishTests() - { - log.info( "Finished tests - {} were run", runningTests.getTotalCount() ); - if( runningTests.hasFailedRequired() ) - { - log.error( "{} required tests failed", runningTests.getFailedRequiredCount() ); - } - if( runningTests.hasFailedOptional() ) - { - log.warn( "{} optional tests failed", runningTests.getFailedOptionalCount() ); - } - - if( ServerLifecycleHooks.getCurrentServer().isDedicatedServer() ) - { - log.info( "Stopping server." ); - - // We can't exit in the main thread, as Minecraft registers a shutdown hook which results - // in a deadlock. So we do this weird janky thing! - Thread thread = new Thread( () -> System.exit( runningTests.hasFailedRequired() ? 1 : 0 ) ); - thread.setDaemon( true ); - thread.start(); - } - } - - private BlockPos getStart( CommandSource source ) - { - BlockPos pos = new BlockPos( source.getPosition() ); - return new BlockPos( pos.getX(), source.getLevel().getHeightmapPos( Heightmap.Type.WORLD_SURFACE, pos ).getY(), pos.getZ() + 3 ); } }