Skip to content

Commit

Permalink
Add a test for the scan data
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed May 3, 2024
1 parent 1057046 commit 4698e5b
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 27 deletions.
1 change: 1 addition & 0 deletions loader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ dependencies {
testImplementation("org.mockito:mockito-junit-jupiter:$mockito_version")
testImplementation("org.assertj:assertj-core:3.25.3")
testImplementation("org.junit.jupiter:junit-jupiter-engine:$jupiter_version")
testImplementation('com.google.jimfs:jimfs:1.3.0')
}

spotless {
Expand Down
1 change: 0 additions & 1 deletion loader/src/main/java/net/neoforged/fml/ModLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static void inject(final ModContainer mod, final ModFileScanData scanData
final EnumSet<Dist> sides = getSides(ad.annotationData().get("value"));
final String modId = (String) ad.annotationData().getOrDefault("modid", modids.getOrDefault(ad.clazz().getClassName(), mod.getModId()));
final ModAnnotation.EnumHolder busTargetHolder = (ModAnnotation.EnumHolder) ad.annotationData().getOrDefault("bus", new ModAnnotation.EnumHolder(null, EventBusSubscriber.Bus.GAME.name()));
final EventBusSubscriber.Bus busTarget = EventBusSubscriber.Bus.valueOf(busTargetHolder.getValue());
final EventBusSubscriber.Bus busTarget = EventBusSubscriber.Bus.valueOf(busTargetHolder.value());
if (Objects.equals(mod.getModId(), modId) && sides.contains(FMLEnvironment.dist)) {
try {
IEventBus bus = switch (busTarget) {
Expand All @@ -71,7 +71,7 @@ public static EnumSet<Dist> getSides(Object data) {
if (data == null) {
return EnumSet.allOf(Dist.class);
} else {
return ((List<ModAnnotation.EnumHolder>) data).stream().map(eh -> Dist.valueOf(eh.getValue())).collect(Collectors.toCollection(() -> EnumSet.noneOf(Dist.class)));
return ((List<ModAnnotation.EnumHolder>) data).stream().map(eh -> Dist.valueOf(eh.value())).collect(Collectors.toCollection(() -> EnumSet.noneOf(Dist.class)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@

package net.neoforged.fml.loading;

import com.google.common.collect.ImmutableMap;
import com.google.common.base.Suppliers;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import net.neoforged.fml.loading.moddiscovery.ModFile;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;

@SuppressWarnings("deprecation")
public class StringSubstitutor {
private static final Map<String, String> globals = ImmutableMap.of(
"mcVersion", FMLLoader.versionInfo().mcVersion(),
"neoForgeVersion", FMLLoader.versionInfo().neoForgeVersion());
private static final Supplier<Map<String, String>> globals = Suppliers.memoize(() -> {
final var globals = new HashMap<String, String>();
globals.put("mcVersion", FMLLoader.versionInfo() == null ? null : FMLLoader.versionInfo().mcVersion());
globals.put("neoForgeVersion", FMLLoader.versionInfo() == null ? null : FMLLoader.versionInfo().neoForgeVersion());
return globals;
});

public static String replace(final String in, final ModFile file) {
return new StrSubstitutor(getStringLookup(file)).replace(in);
Expand All @@ -29,7 +34,7 @@ public String lookup(String key) {
if (parts.length == 1) return key;
final String pfx = parts[0];
if ("global".equals(pfx)) {
return globals.get(parts[1]);
return globals.get().get(parts[1]);
} else if ("file".equals(pfx) && file != null) {
return String.valueOf(file.getSubstitutionMap().get().get(parts[1]));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,7 @@ public static ModFileScanData.AnnotationData fromModAnnotation(final Type clazz,
return new ModFileScanData.AnnotationData(annotation.asmType, annotation.type, clazz, annotation.member, annotation.values);
}

public static class EnumHolder {
private final String desc;
private final String value;

public EnumHolder(String desc, String value) {
this.desc = desc;
this.value = value;
}

public String getDesc() {
return desc;
}

public String getValue() {
return value;
}
}
public record EnumHolder(String desc, String value) {}

private final ElementType type;
private final Type asmType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import java.lang.annotation.ElementType;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.objectweb.asm.Type;

Expand Down
81 changes: 81 additions & 0 deletions loader/src/test/java/net/neoforged/fml/test/RuntimeCompiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package net.neoforged.fml.test;

import com.google.errorprone.annotations.CheckReturnValue;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.intellij.lang.annotations.Language;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RuntimeCompiler {
private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeCompiler.class);
private static final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();

private final DiagnosticCollector<JavaFileObject> diagnostics;
private final StandardJavaFileManager manager;

public RuntimeCompiler(FileSystem targetFS) {
this.diagnostics = new DiagnosticCollector<>();
this.manager = COMPILER.getStandardFileManager(diagnostics, Locale.ROOT, StandardCharsets.UTF_8);
try {
manager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, List.of(targetFS.getPath("/")));
} catch (IOException e) {
throw new RuntimeException("Failed to set root output location", e);
}
}

public CompilationBuilder builder() {
return new CompilationBuilder();
}

public class CompilationBuilder {
private final List<JavaFileObject> files = new ArrayList<>();

@CheckReturnValue
public CompilationBuilder addClass(String name, @Language("java") String content) {
if (!content.trim().startsWith("package ")) {
List<String> nameByDot = new ArrayList<>(Arrays.asList(name.split("\\.")));
nameByDot.removeLast();

content = ("package " + String.join(".", nameByDot) + ";\n" + content);
}
this.files.add(new JavaSourceFromString(name, content));
return this;
}

public void compile() {
var task = COMPILER.getTask(null, manager, diagnostics, null, null, files);
if (!task.call()) {
diagnostics.getDiagnostics().forEach(diagnostic -> LOGGER.error("Failed to compile: {}", diagnostic));
throw new RuntimeException("Failed to compile class");
}
}
}

public static class JavaSourceFromString extends SimpleJavaFileObject {
private final String sourceCode;

public JavaSourceFromString(String name, String sourceCode) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.sourceCode = sourceCode;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return sourceCode;
}
}
}
73 changes: 73 additions & 0 deletions loader/src/test/java/net/neoforged/fml/test/TestModFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package net.neoforged.fml.test;

import com.electronwill.nightconfig.toml.TomlFormat;
import com.electronwill.nightconfig.toml.TomlParser;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.google.errorprone.annotations.CheckReturnValue;
import cpw.mods.jarhandling.JarContentsBuilder;
import cpw.mods.jarhandling.SecureJar;
import java.io.IOException;
import java.nio.file.FileSystem;
import net.neoforged.fml.loading.moddiscovery.ModFile;
import net.neoforged.fml.loading.moddiscovery.ModFileInfo;
import net.neoforged.fml.loading.moddiscovery.ModJarMetadata;
import net.neoforged.fml.loading.moddiscovery.NightConfigWrapper;
import net.neoforged.fml.loading.modscan.Scanner;
import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes;
import net.neoforged.neoforgespi.locating.ModFileInfoParser;
import org.intellij.lang.annotations.Language;

public class TestModFile extends ModFile implements AutoCloseable {
private static final TomlParser PARSER = TomlFormat.instance().createParser();

private final FileSystem fileSystem;
private final RuntimeCompiler compiler;

private TestModFile(SecureJar jar, FileSystem fileSystem, ModFileInfoParser parser) {
super(jar, parser, new ModFileDiscoveryAttributes(null, null, null, null));
this.fileSystem = fileSystem;
this.compiler = new RuntimeCompiler(fileSystem);
}

private static TestModFile buildFile(FileSystem fileSystem, ModFileInfoParser parser) {
var jc = new JarContentsBuilder()
.paths(fileSystem.getPath("/"))
.build();
var metadata = new ModJarMetadata(jc);
var sj = SecureJar.from(jc, metadata);
var mod = new TestModFile(sj, fileSystem, parser);
metadata.setModFile(mod);
return mod;
}

@CheckReturnValue
public RuntimeCompiler.CompilationBuilder classBuilder() {
return compiler.builder();
}

public void scan() {
setScanResult(new Scanner(this).scan(), null);
}

@CheckReturnValue
public static TestModFile newInstance(@Language("toml") String modsDotToml) {
final var fs = Jimfs.newFileSystem(Configuration.unix()
.toBuilder()
.setWorkingDirectory("/")
.build());
var wrapper = new NightConfigWrapper(PARSER.parse(modsDotToml));
return buildFile(fs, file -> new ModFileInfo((ModFile) file, wrapper, wrapper::setFile));
}

@Override
public void close() throws IOException {
getSecureJar().getRootPath().getFileSystem().close();
fileSystem.close();
}

@Override
public String getFileName() {
return "dummy mod.jar";
}
}
Loading

0 comments on commit 4698e5b

Please sign in to comment.