Some test harness stuffs. Time to get the registry manager properly tested.

This commit is contained in:
cpw 2016-07-19 19:21:52 -04:00
parent 5f234fdcf8
commit 22394f87d5
7 changed files with 362 additions and 4 deletions

View file

@ -204,7 +204,7 @@ public class Loader
}
modClassLoader = new ModClassLoader(getClass().getClassLoader());
if (!mccversion.equals(MC_VERSION))
if (mccversion !=null && !mccversion.equals(MC_VERSION))
{
FMLLog.severe("This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, MC_VERSION);
throw new LoaderException(String.format("This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, MC_VERSION));
@ -1023,7 +1023,10 @@ public class Loader
public void fireRemapEvent(Map<ResourceLocation, Integer[]> remapBlocks, Map<ResourceLocation, Integer[]> remapItems, boolean isFreezing)
{
modController.propogateStateMessage(new FMLModIdMappingEvent(remapBlocks, remapItems, isFreezing));
if (modController!=null)
{
modController.propogateStateMessage(new FMLModIdMappingEvent(remapBlocks, remapItems, isFreezing));
}
}
public void runtimeDisableMod(String modId)

View file

@ -55,7 +55,10 @@ public class ModClassLoader extends URLClassLoader
public ModClassLoader(ClassLoader parent) {
super(new URL[0], null);
this.mainClassLoader = (LaunchClassLoader)parent;
if (parent instanceof LaunchClassLoader)
{
this.mainClassLoader = (LaunchClassLoader)parent;
}
this.sources = Lists.newArrayList();
}

View file

@ -61,7 +61,7 @@ import com.google.common.collect.Sets.SetView;
@SuppressWarnings("WeakerAccess")
public class PersistentRegistryManager
{
private enum PersistentRegistry
enum PersistentRegistry
{
ACTIVE, VANILLA, FROZEN, STAGING;

View file

@ -54,6 +54,8 @@ public class FMLRelaunchLog {
private static void configureLogging()
{
log.myLog = LogManager.getLogger("FML");
// Default side to client for test harness purposes
if (side == null) side = Side.CLIENT;
ThreadContext.put("side", side.name().toLowerCase(Locale.ENGLISH));
configured = true;

View file

@ -0,0 +1,146 @@
package net.minecraftforge.fml.common.registry;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.Set;
import com.google.common.collect.Sets;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.JUnit4;
import org.junit.runners.model.InitializationError;
/**
* Uses {@code ResettingClassLoader} to load the test class, meaning the
* {@code Quarantine} annotation can be used to ensure certain classes are
* loaded separately.
*
* Use of a separate class loader allows classes to be reloaded for each test
* class, which is handy when you're testing frameworks that make use of static
* members.
*
* The selective quarantining is required because if the test class and its
* 'children' are all loaded by a different class loader, then the {@code Test}
* annotations yield different {@code Class} instances. JUnit then thinks there
* are no runnable methods, because it looks them up by Class.
*
* This is a simplified copy of https://github.com/BinaryTweed/quarantining-test-runner
* tailored for Minecraft use.
*
*/
public class ForgeTestRunner extends Runner
{
private final Object innerRunner;
private final Class<?> innerRunnerClass;
public ForgeTestRunner(Class<?> testFileClass) throws InitializationError
{
Class<? extends Runner> delegateRunningTo = JUnit4.class;
String testFileClassName = testFileClass.getName();
String delegateRunningToClassName = delegateRunningTo.getName();
String[] allPatterns = new String[] {testFileClassName, delegateRunningToClassName};
ResettingClassLoader classLoader = new ResettingClassLoader(allPatterns);
try
{
innerRunnerClass = classLoader.loadClass(delegateRunningToClassName);
Class<?> testClass = classLoader.loadClass(testFileClassName);
innerRunner = innerRunnerClass.cast(innerRunnerClass.getConstructor(Class.class).newInstance(testClass));
}
catch (Exception e)
{
throw new InitializationError(e);
}
}
@Override
public Description getDescription()
{
try
{
return (Description) innerRunnerClass.getMethod("getDescription").invoke(innerRunner);
}
catch (Exception e)
{
throw new RuntimeException("Could not get description", e);
}
}
@Override
public void run(RunNotifier notifier)
{
try
{
innerRunnerClass.getMethod("run", RunNotifier.class).invoke(innerRunner, notifier);
}
catch (Exception e)
{
notifier.fireTestFailure(new Failure(getDescription(), e));
}
}
/**
* If a class name starts with any of the supplied patterns, it is loaded by
* <em>this</em> classloader; otherwise it is loaded by the parent classloader.
*
*/
private class ResettingClassLoader extends URLClassLoader
{
private final Set<String> quarantinedClassNames;
/**
*
* @param quarantinedClassNames prefixes to match against when deciding how to load a class
*/
public ResettingClassLoader(String... quarantinedClassNames)
{
super(((URLClassLoader) getSystemClassLoader()).getURLs());
this.quarantinedClassNames = Sets.newHashSet();
Collections.addAll(this.quarantinedClassNames, quarantinedClassNames);
Collections.addAll(this.quarantinedClassNames, "net.minecraft", "net.minecraftforge");
}
/**
* If a class name starts with any of the supplied patterns, it is loaded by
* <em>this</em> classloader; otherwise it is loaded by the parent classloader.
*
* @param name class to load
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
boolean quarantine = false;
for(String quarantinedPattern : quarantinedClassNames)
{
if(name.startsWith(quarantinedPattern))
{
quarantine = true;
break;
}
}
if(quarantine)
{
try
{
return findClass(name);
}
catch (ClassNotFoundException e)
{
throw e;
}
}
return super.loadClass(name);
}
}
}

View file

@ -0,0 +1,105 @@
package net.minecraftforge.fml.common.registry;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Loader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Created by cpw on 04/07/16.
*/
@RunWith(ForgeTestRunner.class)
public class FreezingTests
{
private static RTest r1;
private static RTest r2;
private static RTest r3;
private static RTest r4;
private static RTest r5;
private static RTest r6;
private static PersistentRegistryManager.GameDataSnapshot ss;
static class RTest extends IForgeRegistryEntry.Impl<RTest> {
public RTest(String name) {
setRegistryName(name);
}
}
public static IForgeRegistry<RTest> registry;
public static ResourceLocation resloc = new ResourceLocation("fmltest:test");
@BeforeClass
public static void setup()
{
Loader.instance();
System.setProperty("fml.queryResult", "confirm");
registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null);
PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null);
PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null);
r1 = new RTest("test1");
r2 = new RTest("test2");
r3 = new RTest("test3");
r4 = new RTest("test4");
r5 = new RTest("test5");
r6 = new RTest("test6");
ss = new PersistentRegistryManager.GameDataSnapshot();
ss.entries.put(PersistentRegistryManager.BLOCKS, new PersistentRegistryManager.GameDataSnapshot.Entry());
ss.entries.put(PersistentRegistryManager.ITEMS, new PersistentRegistryManager.GameDataSnapshot.Entry());
PersistentRegistryManager.findRegistryByType(RTest.class).register(r1);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r2);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r3);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r4);
ss.entries.put(resloc, new PersistentRegistryManager.GameDataSnapshot.Entry(PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class)));
PersistentRegistryManager.PersistentRegistry.ACTIVE.clean();
PersistentRegistryManager.PersistentRegistry.FROZEN.clean();
registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null);
PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null);
PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null);
}
@Test
public void testFreezeCycle()
{
PersistentRegistryManager.findRegistryByType(RTest.class).register(r6);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r5);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r4);
PersistentRegistryManager.findRegistryByType(RTest.class).register(r3);
FMLControlledNamespacedRegistry<RTest> r = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistry(r3);
int r3id = r.getId(r3);
PersistentRegistryManager.freezeData();
RTest q1 = PersistentRegistryManager.PersistentRegistry.FROZEN.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Frozen object is the same", r3, q1);
q1 = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Active object is the same", r3, q1);
PersistentRegistryManager.injectSnapshot(ss, false, false);
assertNotEquals("IDs don't match", r3id, r.getId(r3));
q1 = PersistentRegistryManager.PersistentRegistry.FROZEN.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Frozen object is the same", r3, q1);
q1 = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Active object is the same", r3, q1);
PersistentRegistryManager.revertToFrozen();
assertEquals("IDs match", r3id, r.getId(r3));
q1 = PersistentRegistryManager.PersistentRegistry.FROZEN.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Frozen object is the same", r3, q1);
q1 = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Active object is the same", r3, q1);
PersistentRegistryManager.injectSnapshot(ss, true, true);
assertNotEquals("IDs don't match", r3id, r.getId(r3));
q1 = PersistentRegistryManager.PersistentRegistry.FROZEN.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Frozen object is the same", r3, q1);
q1 = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Active object is the same", r3, q1);
PersistentRegistryManager.revertToFrozen();
assertEquals("IDs match", r3id, r.getId(r3));
q1 = PersistentRegistryManager.PersistentRegistry.FROZEN.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Frozen object is the same", r3, q1);
q1 = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class).getValue(new ResourceLocation("test3"));
assertEquals("Active object is the same", r3, q1);
}
}

View file

@ -0,0 +1,99 @@
package net.minecraftforge.fml.common.registry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.init.Bootstrap;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Loader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Vanilla registry tests
*/
@RunWith(ForgeTestRunner.class)
public class VanillaRegistryTests
{
@BeforeClass
public static void setupHarness()
{
Loader.instance();
Bootstrap.register();
}
@Test
public void testSetup()
{
// All the blocks loaded
assertEquals("We have all the blocks via GameData",219,Block.REGISTRY.getKeys().size());
// All the items loaded
assertEquals("We have all the items via GameData",371,Item.REGISTRY.getKeys().size());
// Our lookups find the same stuff vanilla sees
final IForgeRegistry<Block> blocks = PersistentRegistryManager.findRegistry(Blocks.AIR);
assertEquals("We have the right blocks for a block", blocks, Block.REGISTRY);
// We can look up stuff through our APIs
Block bl = blocks.getValue(new ResourceLocation("minecraft:air"));
assertEquals("We got air when we asked for it", Blocks.AIR, bl);
// Default values work
Block blch = blocks.getValue(new ResourceLocation("minecraft:cheese"));
assertEquals("We got air when we asked for cheese", Blocks.AIR, blch);
// Our lookups find the same stuff vanilla sees
final IForgeRegistry<Item> items = PersistentRegistryManager.findRegistry(Items.BED);
assertEquals("We have the right items for an item", items, Item.REGISTRY);
// We can look up stuff through our APIs
Item it = items.getValue(new ResourceLocation("minecraft:bed"));
assertEquals("We got a bed item when we asked for it", Items.BED, it);
// We find nothing for a non-defaulted registry
Item none = items.getValue(new ResourceLocation("minecraft:cheese"));
assertEquals("We got nothing (items) when we asked for cheese", null, none);
}
@Test
public void testRegistration()
{
Block myBlock = GameRegistry.register(new Block(Material.CAKE) {}, new ResourceLocation("minecraft:testy"));
assertNotNull("Registered my block", myBlock);
// Our lookups find the same stuff vanilla sees
final IForgeRegistry<Block> blocks = PersistentRegistryManager.findRegistry(myBlock);
assertEquals("We have the right blocks for a block", blocks, Block.REGISTRY);
Block found = blocks.getValue(new ResourceLocation("minecraft:testy"));
assertEquals("Registry lookup works", myBlock, found);
}
@Test
public void testRegistryStates()
{
final FMLControlledNamespacedRegistry<Block> blockVanilla = PersistentRegistryManager.PersistentRegistry.VANILLA.getRegistry(Block.class);
final FMLControlledNamespacedRegistry<Block> blockActive = PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(Block.class);
assertNotEquals("Registry states are distinct", blockActive, blockVanilla);
final Block stoneActive = blockActive.getValue(new ResourceLocation("minecraft:stone"));
final Block stoneVanilla = blockVanilla.getValue(new ResourceLocation("minecraft:stone"));
assertEquals("Stone from active and vanilla are the same", stoneActive, stoneVanilla);
int activeId = blockActive.getId(stoneActive);
int vanillaId = blockVanilla.getId(stoneVanilla);
assertEquals("Stone has correct id", 1, activeId);
assertEquals("Stone has correct id", 1, vanillaId);
}
}