Skip to content

Commit

Permalink
Add a subproject with a JUnit session listener (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt authored May 26, 2024
1 parent 272d8d4 commit e87a46a
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 2 deletions.
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,22 @@ allprojects {
subprojects { subProject ->
subProject.version = rootProject.version

jar.doFirst {
jar {
manifest.attributes(
'Git-Commit' : gradleutils.gitInfo.abbreviatedId,
'Build-Number': "${subProject.version}",
'Automatic-Module-Name' : "fml_${subProject.name.replace("-", "_")}",
'FMLModType' : 'LIBRARY',
'Specification-Title' : "FML${subProject.name}",
'Specification-Vendor' : 'NeoForged',
'Specification-Version' : "${subProject.version.toString().split('\\.')[0]}",
'Implementation-Title' : "FML $subProject.name",
'Implementation-Version': "${subProject.version.toString().split('\\.')[0]}.${subProject.version.toString().split('\\.')[1]}",
'Implementation-Vendor' : 'NeoForged'
)

if (subProject.name != 'junit') {
manifest.attributes.put('FMLModType', 'LIBRARY')
}
}

tasks.withType(JavaCompile) {
Expand Down
20 changes: 20 additions & 0 deletions junit/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id 'java-library'
}

archivesBaseName = 'junit-fml'

dependencies {
implementation(platform("org.junit:junit-bom:$jupiter_version"))
implementation('org.junit.platform:junit-platform-launcher')
// BSL should not be exposed and the actual version should be provided by the neo dep
compileOnly("cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}")
}

publishing {
publications {
maven(MavenPublication) {
artifactId = archivesBaseName
}
}
}
14 changes: 14 additions & 0 deletions junit/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

import net.neoforged.fml.junit.JUnitService;
import org.junit.platform.launcher.LauncherSessionListener;

module net.neoforged.fml.junit {
requires org.junit.platform.launcher;
requires cpw.mods.bootstraplauncher;

provides LauncherSessionListener with JUnitService;
}
33 changes: 33 additions & 0 deletions junit/src/main/java/net/neoforged/fml/junit/JUnitService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.junit;

import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.LauncherSessionListener;

/**
* A session listener for JUnit environments that will bootstrap a Minecraft (FML) environment.
*/
public class JUnitService implements LauncherSessionListener {
private ClassLoader oldLoader;

public JUnitService() {}

@Override
public void launcherSessionOpened(LauncherSession session) {
// When the tests are started we want to make sure that they run on the transforming class loader which is set up by
// bootstrapping BSL which will then load the launch target
oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(LaunchWrapper.getTransformingLoader());
}

@Override
public void launcherSessionClosed(LauncherSession session) {
// Reset the loader in case JUnit wants to execute some pre-shutdown commands
// and our custom class loader might throw it off
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
38 changes: 38 additions & 0 deletions junit/src/main/java/net/neoforged/fml/junit/LaunchWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.junit;

import cpw.mods.bootstraplauncher.BootstrapLauncher;
import java.nio.file.Files;
import java.nio.file.Path;

public class LaunchWrapper {
private static ClassLoader transformingCL;

/**
* Lazily get the transforming module class loader used by the game at runtime by loading the game when needed.
* <p>
* Since in a JUnit environment we can't easily get the launch arguments, we will load them from the file specified via the {@code fml.junit.argsfile} system property.
*/
public static synchronized ClassLoader getTransformingLoader() {
if (transformingCL != null) return transformingCL;
final var oldLoader = Thread.currentThread().getContextClassLoader();

try {
final String[] args = Files.readAllLines(Path.of(System.getProperty("fml.junit.argsfile", "mainargs.txt"))).toArray(String[]::new);
BootstrapLauncher.unitTestingMain(args);

transformingCL = Thread.currentThread().getContextClassLoader();
} catch (Exception exception) {
System.err.println("Failed to start Minecraft: " + exception);
throw new RuntimeException(exception);
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}

return transformingCL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
net.neoforged.fml.junit.JUnitService
5 changes: 5 additions & 0 deletions loader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ dependencies {
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')

// Provides the JUnit project as a BOM entry
constraints {
api(project(':junit'))
}
}

spotless {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.loading.targets;

import net.neoforged.api.distmarker.Dist;

/**
* A launch target for bootstrapping a slim Minecraft environment in forgedev, to be used in JUnit tests.
*/
public class JUnitDevLaunchTarget extends CommonDevLaunchHandler {
@Override
public Dist getDist() {
return Dist.DEDICATED_SERVER;
}

@Override
protected void runService(String[] arguments, ModuleLayer gameLayer) throws Throwable {
Class.forName(gameLayer.findModule("neoforge").orElseThrow(), "net.neoforged.neoforge.junit.JUnitMain").getMethod("main", String[].class).invoke(null, (Object) arguments);
}

@Override
public String name() {
return "forgejunitdev";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.fml.loading.targets;

import net.neoforged.api.distmarker.Dist;

/**
* A launch target for bootstrapping a slim Minecraft environment in userdev, to be used in JUnit tests.
*/
public class JUnitUserDevLaunchTarget extends NeoForgeUserdevLaunchHandler {
@Override
public Dist getDist() {
return Dist.DEDICATED_SERVER;
}

@Override
protected void runService(String[] arguments, ModuleLayer gameLayer) throws Throwable {
Class.forName(gameLayer.findModule("neoforge").orElseThrow(), "net.neoforged.neoforge.junit.JUnitMain").getMethod("main", String[].class).invoke(null, (Object) arguments);
}

@Override
public String name() {
return "forgejunituserdev";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ net.neoforged.fml.loading.targets.NeoForgeServerDevLaunchHandler
net.neoforged.fml.loading.targets.NeoForgeServerUserdevLaunchHandler
net.neoforged.fml.loading.targets.NeoForgeDataDevLaunchHandler
net.neoforged.fml.loading.targets.NeoForgeDataUserdevLaunchHandler
net.neoforged.fml.loading.targets.JUnitDevLaunchTarget
net.neoforged.fml.loading.targets.JUnitUserDevLaunchTarget
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ rootProject.name = 'FancyModLoader'

include 'loader'
include 'earlydisplay'
include 'junit'

0 comments on commit e87a46a

Please sign in to comment.