[1.12] Add support for client & server dependencies for mods (#4403)

This commit is contained in:
mezz 2017-10-02 22:13:30 -07:00 committed by GitHub
parent cf39ff18e1
commit f494117453
7 changed files with 577 additions and 108 deletions

View File

@ -66,8 +66,10 @@ version = getVersionFromJava(file("src/main/java/net/minecraftforge/common/Forge
extractForgeSources { exclude "**/SideOnly.java", "**/Side.java" }
extractForgeResources { exclude "**/log4j2.xml" }
genGradleProjects {
addTestCompileDep "junit:junit:4.12"
genGradleProjects {
addTestCompileDep "junit:junit:4.12" // TODO update unit tests to junit 5 and remove this
addTestCompileDep "org.junit.jupiter:junit-jupiter-api:5.0.0"
addTestCompileDep "org.opentest4j:opentest4j:1.0.0" // needed for junit 5
addTestCompileDep "org.hamcrest:hamcrest-core:1.3"
filter { dep -> !dep.contains("scala") }
}

View File

@ -51,6 +51,7 @@ import net.minecraftforge.fml.common.event.FMLFingerprintViolationEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion;
import net.minecraftforge.fml.common.versioning.DependencyParser;
import net.minecraftforge.fml.common.versioning.VersionParser;
import net.minecraftforge.fml.common.versioning.VersionRange;
import net.minecraftforge.fml.relauncher.Side;
@ -67,14 +68,11 @@ import java.util.function.Function;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
@ -140,19 +138,18 @@ public class FMLModContainer implements ModContainer
String modid = (String)this.descriptor.get("modid");
if (Strings.isNullOrEmpty(modid))
{
throw new IllegalArgumentException("Modid cannot be null or empty");
throw new IllegalArgumentException("The modId is null or empty");
}
if (modid.length() > 64)
{
FMLLog.bigWarning("The modid {} is longer than the recommended maximum of 64 characters. Truncation is enforced in 1.11", modid);
throw new IllegalArgumentException(String.format("The modid %s is longer than the recommended maximum of 64 characters. Truncation is enforced in 1.11", modid));
throw new IllegalArgumentException(String.format("The modId %s is longer than the maximum of 64 characters.", modid));
}
if (!modid.equals(modid.toLowerCase(Locale.ENGLISH)))
{
FMLLog.bigWarning("The modid {} is not the same as it's lowercase version. Lowercasing is enforced in 1.11", modid);
throw new IllegalArgumentException(String.format("The modid %s is not the same as it's lowercase version. Lowercasing will be enforced in 1.11", modid));
throw new IllegalArgumentException(String.format("The modId %s must be all lowercase.", modid));
}
}
private ILanguageAdapter getLanguageAdapter()
{
if (languageAdapter == null)
@ -212,17 +209,15 @@ public class FMLModContainer implements ModContainer
if (overridesMetadata || !modMetadata.useDependencyInformation)
{
Set<ArtifactVersion> requirements = Sets.newHashSet();
List<ArtifactVersion> dependencies = Lists.newArrayList();
List<ArtifactVersion> dependants = Lists.newArrayList();
annotationDependencies = (String)descriptor.get("dependencies");
Loader.instance().computeDependencies(annotationDependencies, requirements, dependencies, dependants);
dependants.addAll(Loader.instance().getInjectedBefore(getModId()));
dependencies.addAll(Loader.instance().getInjectedAfter(getModId()));
modMetadata.requiredMods = requirements;
modMetadata.dependencies = dependencies;
modMetadata.dependants = dependants;
modLog.trace("Parsed dependency info : {} {} {}", requirements, dependencies, dependants);
DependencyParser dependencyParser = new DependencyParser(getModId(), FMLCommonHandler.instance().getSide());
DependencyParser.DependencyInfo info = dependencyParser.parseDependencies(annotationDependencies);
info.dependants.addAll(Loader.instance().getInjectedBefore(getModId()));
info.dependencies.addAll(Loader.instance().getInjectedAfter(getModId()));
modMetadata.requiredMods = info.requirements;
modMetadata.dependencies = info.dependencies;
modMetadata.dependants = info.dependants;
modLog.trace("Parsed dependency info : Requirements: {} After:{} Before:{}", info.requirements, info.dependencies, info.dependants);
}
else
{

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@ -35,6 +34,7 @@ import java.util.Properties;
import java.util.Set;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.ForgeVersion;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.config.ConfigManager;
import net.minecraftforge.common.crafting.CraftingHelper;
@ -52,6 +52,7 @@ import net.minecraftforge.fml.common.toposort.ModSortingException;
import net.minecraftforge.fml.common.toposort.TopologicalSort;
import net.minecraftforge.fml.common.toposort.ModSortingException.SortingExceptionData;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
import net.minecraftforge.fml.common.versioning.DependencyParser;
import net.minecraftforge.fml.common.versioning.VersionParser;
import net.minecraftforge.fml.relauncher.ModListHelper;
import net.minecraftforge.fml.relauncher.Side;
@ -62,7 +63,6 @@ import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Level;
import com.google.common.base.CharMatcher;
import java.util.function.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
@ -124,9 +124,7 @@ import javax.annotation.Nullable;
@SuppressWarnings("unused")
public class Loader
{
public static final String MC_VERSION = net.minecraftforge.common.ForgeVersion.mcVersion;
private static final Splitter DEPENDENCYPARTSPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
private static final Splitter DEPENDENCYSPLITTER = Splitter.on(";").omitEmptyStrings().trimResults();
public static final String MC_VERSION = ForgeVersion.mcVersion;
/**
* The singleton instance
*/
@ -695,77 +693,17 @@ public class Loader
return modClassLoader;
}
/**
* @deprecated use {@link DependencyParser#parseDependencies(String)}
*/
@Deprecated // TODO: remove in 1.13
public void computeDependencies(String dependencyString, Set<ArtifactVersion> requirements, List<ArtifactVersion> dependencies, List<ArtifactVersion> dependants)
{
if (dependencyString == null || dependencyString.length() == 0)
{
return;
}
boolean parseFailure = false;
for (String dep : DEPENDENCYSPLITTER.split(dependencyString))
{
List<String> depparts = Lists.newArrayList(DEPENDENCYPARTSPLITTER.split(dep));
// Need two parts to the string
if (depparts.size() != 2)
{
parseFailure = true;
continue;
}
String instruction = depparts.get(0);
String target = depparts.get(1);
boolean targetIsAll = target.startsWith("*");
// Cannot have an "all" relationship with anything except pure *
if (targetIsAll && target.length() > 1)
{
parseFailure = true;
continue;
}
// If this is a required element, add it to the required list
if ("required-before".equals(instruction) || "required-after".equals(instruction))
{
// You can't require everything
if (!targetIsAll)
{
requirements.add(VersionParser.parseVersionReference(target));
}
else
{
parseFailure = true;
continue;
}
}
// You cannot have a versioned dependency on everything
if (targetIsAll && target.indexOf('@') > -1)
{
parseFailure = true;
continue;
}
// before elements are things we are loaded before (so they are our dependants)
if ("required-before".equals(instruction) || "before".equals(instruction))
{
dependants.add(VersionParser.parseVersionReference(target));
}
// after elements are things that load before we do (so they are out dependencies)
else if ("required-after".equals(instruction) || "after".equals(instruction))
{
dependencies.add(VersionParser.parseVersionReference(target));
}
else
{
parseFailure = true;
}
}
if (parseFailure)
{
FMLLog.log.warn("Unable to parse dependency string {}", dependencyString);
throw new LoaderException(String.format("Unable to parse dependency string %s", dependencyString));
}
DependencyParser dependencyParser = new DependencyParser("unknown", FMLCommonHandler.instance().getSide());
DependencyParser.DependencyInfo info = dependencyParser.parseDependencies(dependencyString);
requirements.addAll(info.requirements);
dependencies.addAll(info.dependencies);
dependants.addAll(info.dependants);
}
public Map<String,ModContainer> getIndexedModList()

View File

@ -101,24 +101,37 @@ public @interface Mod
/**
* A dependency string for this mod, which specifies which mod(s) it depends on in order to run.
*
* A dependency string can start with the following four prefixes:
* before, after, required-before, required-after
* A dependency string must start with a combination of these prefixes, separated by "-":
* [before, after], [required], [client, server]
* At least one "before", "after", or "required" must be specified.
* Then ":" and the mod id.
*
* Optionally, a version range can be specified for the mod by adding "@" and then the version range.
* The version range format is described in the javadoc here: {@link VersionRange#createFromVersionSpec(java.lang.String)}
* Then a version range should be specified for the mod by adding "@" and the version range.
* The version range format is described in the javadoc here:
* {@link VersionRange#createFromVersionSpec(java.lang.String)}
* Then a ";".
*
* If a "required" mod is missing, or a mod exists with a version outside the specified range,
* the game will not start and an error screen will tell the player which versions are required.
*
* Example:
* Our example mod is a dedicated addon to mod1 and has optional integration with mod2.
* It uses new features that were introduced in forge version 14.21.1.2395 and mod2 version 4.7.0
* Our example mod:
* * depends on Forge and uses new features that were introduced in Forge version 14.21.1.2395
* "required:forge@[14.21.1.2395,);"
* * is a dedicated addon to mod1 and has to have its event handlers run after mod1's are run,
* "required-after:mod1;"
* * has optional integration with mod2 which depends on features introduced in mod2 version 4.7.0,
* "after:mod2@[4.7.0,);"
* * depends on a client-side-only rendering library called rendermod
* "required-client:rendermod;"
*
* the dependencies string = "after:forge@[14.21.1.2395,);required-after:mod1;after:mod2@[4.7.0,);"
* The full dependencies string is all of those combined:
* "required:forge@[14.21.1.2395,);required-after:mod1;after:mod2@[4.7.0,);required-client:rendermod;"
*
* This will stop the game and display an error message if any of these is true:
* The installed forge is too old, mod1 is missing, or an old version of mod2 is present.
* The installed forge is too old,
* mod1 is missing,
* an old version of mod2 is present,
* rendermod is missing on the client.
*/
String dependencies() default "";

View File

@ -0,0 +1,221 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.common.versioning;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import net.minecraftforge.fml.common.LoaderException;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public final class DependencyParser
{
public static class DependencyInfo
{
public final Set<ArtifactVersion> requirements = new HashSet<>();
public final List<ArtifactVersion> dependencies = new ArrayList<>();
public final List<ArtifactVersion> dependants = new ArrayList<>();
}
private static final Logger LOGGER = LogManager.getLogger("FML");
private static final ImmutableList<String> DEPENDENCY_INSTRUCTIONS = ImmutableList.of("client", "server", "required", "before", "after");
private static final Splitter DEPENDENCY_INSTRUCTIONS_SPLITTER = Splitter.on("-").omitEmptyStrings().trimResults();
private static final Splitter DEPENDENCY_PART_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
private static final Splitter DEPENDENCY_SPLITTER = Splitter.on(";").omitEmptyStrings().trimResults();
private final String modId;
private final Side side;
public DependencyParser(String modId, Side side)
{
this.modId = modId;
this.side = side;
}
public DependencyInfo parseDependencies(String dependencyString)
{
DependencyInfo info = new DependencyInfo();
if (dependencyString == null || dependencyString.length() == 0)
{
return info;
}
for (String dep : DEPENDENCY_SPLITTER.split(dependencyString))
{
final List<String> depParts = DEPENDENCY_PART_SPLITTER.splitToList(dep);
if (depParts.size() != 2)
{
throw new DependencyParserException(modId, dep, "Dependency string needs 2 parts.");
}
final List<String> instructions = DEPENDENCY_INSTRUCTIONS_SPLITTER.splitToList(depParts.get(0));
final String target = depParts.get(1);
parseDependency(dep, instructions, target, info);
}
return info;
}
private void parseDependency(String dep, List<String> instructions, String target, DependencyInfo info)
{
final boolean targetIsAll = target.startsWith("*");
final boolean targetIsBounded = target.contains("@");
if (targetIsAll)
{
if (target.length() > 1)
{
throw new DependencyParserException(modId, dep, "Cannot have an \"all\" (*) relationship with anything except pure *");
}
else if (targetIsBounded)
{
throw new DependencyParserException(modId, dep, "You cannot have a versioned dependency on everything (*)");
}
}
Side depSide = null;
String depOrder = null;
boolean depRequired = false;
for (String instruction : instructions)
{
if ("client".equals(instruction))
{
if (depSide != null)
{
throw new DependencyParserException(modId, dep, "Up to one side (client or server) can be specified.");
}
depSide = Side.CLIENT;
}
else if ("server".equals(instruction))
{
if (depSide != null)
{
throw new DependencyParserException(modId, dep, "Up to one side (client or server) can be specified.");
}
depSide = Side.SERVER;
}
else if ("required".equals(instruction))
{
if (depRequired)
{
throw new DependencyParserException(modId, dep, "'required' can only be specified once.");
}
if (targetIsAll)
{
throw new DependencyParserException(modId, dep, "You can't 'require' everything (*)");
}
depRequired = true;
}
else if ("before".equals(instruction) || "after".equals(instruction))
{
if (depOrder != null)
{
throw new DependencyParserException(modId, dep, "'before' or 'after' can only be specified once.");
}
depOrder = instruction;
}
else
{
throw new DependencyParserException(modId, dep, String.format("Found invalid instruction '%s'. Only %s are allowed.", instruction, DEPENDENCY_INSTRUCTIONS));
}
}
ArtifactVersion artifactVersion;
try
{
artifactVersion = VersionParser.parseVersionReference(target);
}
catch (RuntimeException e)
{
throw new DependencyParserException(modId, dep, "Could not parse version string.", e);
}
if (!targetIsAll)
{
String depModId = artifactVersion.getLabel();
sanityCheckModId(modId, dep, depModId);
}
if (!depRequired && depOrder == null)
{
throw new DependencyParserException(modId, dep, "'required', 'client', or 'server' must be specified.");
}
if (depSide == null || depSide == this.side)
{
if (depRequired)
{
info.requirements.add(artifactVersion);
}
if ("before".equals(depOrder))
{
info.dependants.add(artifactVersion);
}
else if ("after".equals(depOrder))
{
info.dependencies.add(artifactVersion);
}
}
}
// TODO 1.13: throw these exceptions instead of logging them
/** Based on {@link net.minecraftforge.fml.common.FMLModContainer#sanityCheckModId()} */
private static void sanityCheckModId(String modId, String dep, String depModId)
{
if (Strings.isNullOrEmpty(depModId))
{
LOGGER.error(new DependencyParserException(modId, dep, "The modId is null or empty").getMessage());
}
else if (depModId.length() > 64)
{
LOGGER.error(new DependencyParserException(modId, dep, String.format("The modId '%s' is longer than the maximum of 64 characters.", depModId)).getMessage());
}
else if (!depModId.equals(depModId.toLowerCase(Locale.ENGLISH)))
{
LOGGER.error(new DependencyParserException(modId, dep, String.format("The modId '%s' must be all lowercase.", depModId)).getMessage());
}
}
private static class DependencyParserException extends LoaderException
{
public DependencyParserException(String modId, String dependencyString, String explanation)
{
super(formatMessage(modId, dependencyString, explanation));
}
public DependencyParserException(String modId, String dependencyString, String explanation, Throwable cause)
{
super(formatMessage(modId, dependencyString, explanation), cause);
}
public static String formatMessage(String modId, String dependencyString, String explanation)
{
return String.format("Unable to parse dependency for mod '%s' with dependency string '%s'. %s", modId, dependencyString, explanation);
}
}
}

View File

@ -21,11 +21,8 @@ package net.minecraftforge.fml.common.versioning;
import java.util.List;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.LoaderException;
import org.apache.logging.log4j.Level;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
@ -76,8 +73,7 @@ public class VersionParser
}
catch (InvalidVersionSpecificationException e)
{
FMLLog.log.error("Unable to parse a version range specification successfully {}", range, e);
throw new LoaderException(e);
throw new LoaderException("Unable to parse a version range specification successfully " + range, e);
}
}
}

View File

@ -0,0 +1,304 @@
package net.minecraftforge.fml.test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.ImmutableList;
import net.minecraftforge.fml.common.versioning.DependencyParser;
import net.minecraftforge.fml.relauncher.Side;
import com.google.common.base.Functions;
import com.google.common.collect.Collections2;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.common.LoaderException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class DependencyParserTest
{
private static DependencyParser clientDependencyParser;
private static DependencyParser serverDependencyParser;
private static List<DependencyParser> parsers;
@BeforeAll
public static void beforeAll()
{
clientDependencyParser = new DependencyParser("test", Side.CLIENT);
serverDependencyParser = new DependencyParser("test", Side.SERVER);
parsers = ImmutableList.of(clientDependencyParser, serverDependencyParser);
}
@Test
public void testParsingNothing()
{
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("");
strings.add(";;;;;;;");
strings.add("; ; ; ; ; ; ; ");
for (String string : strings)
{
for (DependencyParser parser : parsers)
{
DependencyParser.DependencyInfo info = parser.parseDependencies(string);
assertTrue(info.requirements.isEmpty());
assertTrue(info.dependants.isEmpty());
assertTrue(info.dependencies.isEmpty());
}
}
}
@Test
public void testParsingRequired()
{
String mod = "supermod3000@[1.2,)";
for (DependencyParser parser : parsers)
{
DependencyParser.DependencyInfo info = parser.parseDependencies("required:" + mod);
assertContainsSameToString(info.requirements, Sets.newHashSet(mod));
assertTrue(info.dependants.isEmpty());
assertTrue(info.dependencies.isEmpty());
}
}
@Test
public void testParsingBefore()
{
String mod = "supermod3000@[1.2,)";
for (DependencyParser parser : parsers)
{
DependencyParser.DependencyInfo info = parser.parseDependencies("before:" + mod);
assertTrue(info.requirements.isEmpty());
assertContainsSameToString(info.dependants, Sets.newHashSet(mod));
assertTrue(info.dependencies.isEmpty());
}
}
@Test
public void testParsingAfter()
{
String mod = "supermod3000@[1.2,)";
for (DependencyParser parser : parsers)
{
DependencyParser.DependencyInfo info = parser.parseDependencies("after:" + mod);
assertTrue(info.requirements.isEmpty());
assertTrue(info.dependants.isEmpty());
assertContainsSameToString(info.dependencies, Sets.newHashSet(mod));
}
}
@Test
public void testParsingNoPrefix()
{
String mod = "supermod3000@[1.2,)";
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies(mod);
});
}
}
@Test
public void testParsingCombined()
{
for (DependencyParser parser : parsers)
{
String dependencyString = "after:supermod2000@[1.3,);required-before:yetanothermod;required:modw";
DependencyParser.DependencyInfo info = parser.parseDependencies(dependencyString);
assertContainsSameToString(info.requirements, Sets.newHashSet("yetanothermod", "modw"));
assertContainsSameToString(info.dependencies, Sets.newHashSet("supermod2000@[1.3,)"));
assertContainsSameToString(info.dependants, Sets.newHashSet("yetanothermod"));
}
}
@Test
public void testParsingSided()
{
String mod = "testmod@[1.0,2.0)";
{
DependencyParser.DependencyInfo info = clientDependencyParser.parseDependencies("client-after:" + mod);
assertTrue(info.requirements.isEmpty());
assertTrue(info.dependants.isEmpty());
assertContainsSameToString(info.dependencies, Sets.newHashSet(mod));
}
{
DependencyParser.DependencyInfo info = clientDependencyParser.parseDependencies("server-after:testmod@[1.0,2.0);server-required:testmod2;server-after:testmod3;server-before:testmod4");
assertTrue(info.requirements.isEmpty());
assertTrue(info.dependants.isEmpty());
assertTrue(info.dependencies.isEmpty());
}
{
DependencyParser.DependencyInfo info = clientDependencyParser.parseDependencies("client-before:testmod@[1.0,2.0);server-required:testmod2;server-after:testmod3;server-before:testmod4");
assertTrue(info.requirements.isEmpty());
assertContainsSameToString(info.dependants, Sets.newHashSet(mod));
assertTrue(info.dependencies.isEmpty());
}
{
DependencyParser.DependencyInfo info = serverDependencyParser.parseDependencies("server-before:" + mod);
assertTrue(info.requirements.isEmpty());
assertContainsSameToString(info.dependants, Sets.newHashSet(mod));
assertTrue(info.dependencies.isEmpty());
}
{
DependencyParser.DependencyInfo info = serverDependencyParser.parseDependencies("client-before:testmod@[1.0,2.0);client-required:testmod2;client-after:testmod3;client-before:testmod4");
assertTrue(info.requirements.isEmpty());
assertTrue(info.dependants.isEmpty());
assertTrue(info.dependencies.isEmpty());
}
{
DependencyParser.DependencyInfo info = serverDependencyParser.parseDependencies("server-before:testmod@[1.0,2.0);client-required:testmod2;client-after:testmod3;client-before:testmod4");
assertTrue(info.requirements.isEmpty());
assertContainsSameToString(info.dependants, Sets.newHashSet(mod));
assertTrue(info.dependencies.isEmpty());
}
}
@Test
public void testParsingInvalidDependencyInstructions()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("gibberishtext:amod");
});
}
}
@Test
public void testParsingInvalidDependencyVersionClient()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("amod@[10");
});
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("client:amod@[10");
});
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("server:amod@[10");
});
}
}
// TODO: enable this in 1.13
// @Test(expected = LoaderException.class)
// public void testParsingUppercaseModId()
// {
// for (DependencyParser parser : parsers)
// {
// assertThrows(LoaderException.class, () -> {
// parser.parseDependencies("Forge@[1.0]");
// });
// assertThrows(LoaderException.class, () -> {
// parser.parseDependencies("client:Forge@[1.0]");
// });
// assertThrows(LoaderException.class, () -> {
// parser.parseDependencies("server:Forge@[1.0]");
// });
// }
// }
@Test
public void testParsingSoftDepWithNoVersion()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("amod");
});
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("client:amod");
});
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("server:amod");
});
}
}
@Test
public void testParsingDepAfterAll()
{
for (DependencyParser parser : parsers)
{
parser.parseDependencies("after:*");
}
}
@Test
public void testParsingDepBeforeAll()
{
for (DependencyParser parser : parsers)
{
parser.parseDependencies("before:*");
}
}
@Test
public void testParsingDepOnAll()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("*");
});
}
}
@Test
public void testParsingSidedDepOnAll()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("client:*");
});
}
}
@Test
public void testParsingRequireAll()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("required:*");
});
}
}
@Test
public void testParsingVersionedAll()
{
for (DependencyParser parser : parsers)
{
assertThrows(LoaderException.class, () -> {
parser.parseDependencies("*@[1.0]");
});
}
}
public static void assertContainsSameToString(Collection<?> c1, Collection<String> expected)
{
Collection<String> transformedToString = Collections2.transform(c1, Functions.toStringFunction());
assertContainsSame(transformedToString, expected);
}
public static <T> void assertContainsSame(Collection<T> c1, Collection<T> c2)
{
if (!c1.containsAll(c2) || !c2.containsAll(c1))
{
fail(c1 + " does not contain the same as " + c2);
}
}
}