Skip to content

Commit

Permalink
Add DSL to configure Fabric API game tests (#1240)
Browse files Browse the repository at this point in the history
* Add DSL to configure game tests

* Small cleanup

* More work
  • Loading branch information
modmuss50 authored Jan 2, 2025
1 parent 454e32e commit c46e252
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2024 FabricMC
* Copyright (c) 2024-2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -26,6 +26,7 @@

import org.gradle.api.Action;
import org.gradle.api.artifacts.Dependency;
import org.jetbrains.annotations.ApiStatus;

/**
* A gradle extension with specific functionality related to Fabric API.
Expand Down Expand Up @@ -58,4 +59,17 @@ public interface FabricApiExtension {
* @param action An action to configure specific data generation settings. See {@link DataGenerationSettings} for more information.
*/
void configureDataGeneration(Action<DataGenerationSettings> action);

/**
* Configuration of game and client tests using the default settings.
*/
@ApiStatus.Experimental
void configureTests();

/**
* Configuration of game and/or client tests using the specified settings.
* @param action An action to configure specific game test settings. See {@link GameTestSettings} for more information.
*/
@ApiStatus.Experimental
void configureTests(Action<GameTestSettings> action);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package net.fabricmc.loom.api.fabricapi;

import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Optional;
import org.jetbrains.annotations.ApiStatus;

/**
* Represents the settings for game and/or client tests.
*/
@ApiStatus.Experimental
public interface GameTestSettings {
/**
* Contains a boolean property indicating whether a new source set should be created for the tests.
*
* <p>Default: false
*/
Property<Boolean> getCreateSourceSet();

/**
* Contains a string property representing the mod ID associated with the tests.
*
* <p>This must be set when {@link #getCreateSourceSet()} is set.
*/
@Optional
Property<String> getModId();

/**
* Contains a boolean property indicating whether a run configuration will be created for the server side game tests, using Vanilla Game Test framework.
*
* <p>Default: true
*/
Property<Boolean> getEnableGameTests();

/**
* Contains a boolean property indicating whether a run configuration will be created for the client side game tests, using the Fabric API Client Test framework.
*
* <p>Default: true
*/
Property<Boolean> getEnableClientGameTests();

/**
* Contains a boolean property indicating whether the eula has been accepted. By enabling this you agree to the Minecraft EULA located at <a href="https://aka.ms/MinecraftEULA">https://aka.ms/MinecraftEULA</a>.
*
* <p>This only works when {@link #getEnableClientGameTests()} is enabled.
*
* <p>Default: false
*/
Property<Boolean> getEula();

/**
* Contains a boolean property indicating whether the run directories should be cleared before running the tests.
*
* <p>This only works when {@link #getEnableClientGameTests()} is enabled.
*
* <p>Default: true
*/
Property<Boolean> getClearRunDirectory();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2020-2023 FabricMC
* Copyright (c) 2020-2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -32,17 +32,20 @@

import net.fabricmc.loom.api.fabricapi.DataGenerationSettings;
import net.fabricmc.loom.api.fabricapi.FabricApiExtension;
import net.fabricmc.loom.api.fabricapi.GameTestSettings;

public abstract class FabricApiExtensionImpl implements FabricApiExtension {
@Inject
protected abstract ObjectFactory getObjectFactory();

private final FabricApiVersions versions;
private final FabricApiDataGeneration dataGeneration;
private final FabricApiTesting testing;

public FabricApiExtensionImpl() {
versions = getObjectFactory().newInstance(FabricApiVersions.class);
dataGeneration = getObjectFactory().newInstance(FabricApiDataGeneration.class);
testing = getObjectFactory().newInstance(FabricApiTesting.class);
}

@Override
Expand All @@ -64,4 +67,14 @@ public void configureDataGeneration() {
public void configureDataGeneration(Action<DataGenerationSettings> action) {
dataGeneration.configureDataGeneration(action);
}

@Override
public void configureTests() {
configureTests(gameTestSettings -> { });
}

@Override
public void configureTests(Action<GameTestSettings> action) {
testing.configureTests(action);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2025 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package net.fabricmc.loom.configuration.fabricapi;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;

import javax.inject.Inject;

import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.Delete;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskContainer;

import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.fabricapi.GameTestSettings;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.task.AbstractLoomTask;
import net.fabricmc.loom.task.LoomTasks;
import net.fabricmc.loom.util.Constants;

public abstract class FabricApiTesting extends FabricApiAbstractSourceSet {
@Inject
protected abstract Project getProject();

@Inject
public FabricApiTesting() {
}

@Override
protected String getSourceSetName() {
return "gametest";
}

void configureTests(Action<GameTestSettings> action) {
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
final TaskContainer tasks = getProject().getTasks();

GameTestSettings settings = getProject().getObjects().newInstance(GameTestSettings.class);
settings.getCreateSourceSet().convention(false);
settings.getEnableGameTests().convention(true);
settings.getEnableClientGameTests().convention(true);
settings.getEula().convention(false);
settings.getClearRunDirectory().convention(true);

action.execute(settings);

if (settings.getCreateSourceSet().get()) {
configureSourceSet(settings.getModId(), true);
}

Consumer<RunConfigSettings> configureBase = run -> {
if (settings.getCreateSourceSet().get()) {
run.source(getSourceSetName());
}
};

if (settings.getEnableGameTests().get()) {
RunConfigSettings gameTest = extension.getRunConfigs().create("gameTest", run -> {
run.inherit(extension.getRunConfigs().getByName("server"));
run.property("fabric-api.gametest");
run.runDir("build/run/gameTest");
configureBase.accept(run);
});

tasks.named("test", task -> task.dependsOn(LoomTasks.getRunConfigTaskName(gameTest)));
}

if (settings.getEnableClientGameTests().get()) {
RunConfigSettings clientGameTest = extension.getRunConfigs().create("clientGameTest", run -> {
run.inherit(extension.getRunConfigs().getByName("client"));
run.property("fabric.client.gametest");
run.runDir("build/run/clientGameTest");
configureBase.accept(run);
});

if (settings.getClearRunDirectory().get()) {
var deleteGameTestRunDir = tasks.register("deleteGameTestRunDir", Delete.class, task -> {
task.setGroup(Constants.TaskGroup.FABRIC);
task.delete(clientGameTest.getRunDir());
});

tasks.named(LoomTasks.getRunConfigTaskName(clientGameTest), task -> task.dependsOn(deleteGameTestRunDir));
}

if (settings.getEula().get()) {
var acceptEula = tasks.register("acceptGameTestEula", AcceptEulaTask.class, task -> {
task.getEulaFile().set(getProject().file(clientGameTest.getRunDir() + "/eula.txt"));

if (settings.getClearRunDirectory().get()) {
// Ensure that the eula is accepted after the run directory is cleared
task.dependsOn(tasks.named("deleteGameTestRunDir"));
}
});

tasks.named("configureLaunch", task -> task.dependsOn(acceptEula));
}
}
}

public abstract static class AcceptEulaTask extends AbstractLoomTask {
@OutputFile
public abstract RegularFileProperty getEulaFile();

@TaskAction
public void acceptEula() throws IOException {
final Path eula = getEulaFile().get().getAsFile().toPath();

if (Files.notExists(eula)) {
Files.writeString(eula, """
#This file was generated by the Fabric Loom Gradle plugin. As the user opted into accepting the EULA.
eula=true
""");
}
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/net/fabricmc/loom/task/LoomTasks.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void registerIDETasks() {
});
}

private static String getRunConfigTaskName(RunConfigSettings config) {
public static String getRunConfigTaskName(RunConfigSettings config) {
String configName = config.getName();
return "run" + configName.substring(0, 1).toUpperCase() + configName.substring(1);
}
Expand Down
Loading

0 comments on commit c46e252

Please sign in to comment.