diff --git a/build.gradle b/build.gradle
index d63f04364..db58217e4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -270,6 +270,11 @@ test {
maxRetries = 3
}
}
+
+ testLogging {
+ // Log everything to the console
+ setEvents(TestLogEvent.values().toList())
+ }
}
// Workaround https://github.com/gradle/gradle/issues/25898
@@ -283,7 +288,7 @@ tasks.withType(Test).configureEach {
}
-import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles
+import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.util.GradleVersion
import org.w3c.dom.Document
import org.w3c.dom.Element
diff --git a/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java b/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java
index f608fea0c..01779a857 100644
--- a/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java
+++ b/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java
@@ -43,6 +43,16 @@
*/
@ApiStatus.Experimental
public abstract non-sealed class ClientProductionRunTask extends AbstractProductionRunTask {
+ /**
+ * Whether to use XVFB to run the game, using a virtual framebuffer. This is useful for CI environments that don't have a display server.
+ *
+ *
Defaults to true only on Linux and when the "CI" environment variable is set.
+ *
+ *
XVFB must be installed, on Debian-based systems you can install it with: apt install -y xvfb
+ */
+ @Input
+ public abstract Property getUseXVFB();
+
// Internal options
@Input
protected abstract Property getAssetsIndex();
@@ -52,6 +62,11 @@ public abstract non-sealed class ClientProductionRunTask extends AbstractProduct
@Inject
public ClientProductionRunTask() {
+ getUseXVFB().convention(getProject().getProviders().environmentVariable("CI")
+ .map(value -> Platform.CURRENT.getOperatingSystem().isLinux())
+ .orElse(false)
+ );
+
getAssetsIndex().set(getExtension().getMinecraftVersion()
.map(minecraftVersion -> getExtension()
.getMinecraftProvider()
@@ -71,6 +86,22 @@ public ClientProductionRunTask() {
dependsOn("downloadAssets");
}
+ @Override
+ protected void configureCommand(ExecSpec exec) {
+ if (getUseXVFB().get()) {
+ if (!Platform.CURRENT.getOperatingSystem().isLinux()) {
+ throw new UnsupportedOperationException("XVFB is only supported on Linux");
+ }
+
+ exec.commandLine("/usr/bin/xvfb-run");
+ exec.args("-a", getJavaLauncher().get().getExecutablePath());
+
+ return;
+ }
+
+ super.configureCommand(exec);
+ }
+
@Override
protected void configureJvmArgs(ExecSpec exec) {
super.configureJvmArgs(exec);
diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy
index 77ee9e503..95b8f0118 100644
--- a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy
+++ b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy
@@ -24,8 +24,11 @@
package net.fabricmc.loom.test.integration
+import java.util.concurrent.TimeUnit
+
import spock.lang.IgnoreIf
import spock.lang.Specification
+import spock.lang.Timeout
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties
@@ -157,19 +160,30 @@ class RunConfigTest extends Specification implements GradleProjectTestTrait {
version << STANDARD_TEST_VERSIONS
}
+ @Timeout(value = 10, unit = TimeUnit.MINUTES)
@Unroll
- @IgnoreIf({ System.getenv("CI") != null }) // This test is disabled on CI because it launches a real client and cannot run headless.
+ @IgnoreIf({ !os.linux }) // XVFB is installed on the CI for this test
def "prod client (gradle #version)"() {
setup:
def gradle = gradleProject(project: "minimalBase", version: version)
gradle.buildGradle << '''
+ configurations {
+ productionMods
+ }
+
dependencies {
minecraft "com.mojang:minecraft:1.21.4"
mappings "net.fabricmc:yarn:1.21.4+build.4:v2"
modImplementation "net.fabricmc:fabric-loader:0.16.9"
+ modImplementation "net.fabricmc.fabric-api:fabric-api:0.114.0+1.21.4"
+
+ productionMods "net.fabricmc.fabric-api:fabric-api:0.114.0+1.21.4"
}
- tasks.register("prodClient", net.fabricmc.loom.task.prod.ClientProductionRunTask)
+ tasks.register("prodClient", net.fabricmc.loom.task.prod.ClientProductionRunTask) {
+ mods.from(configurations.productionMods)
+ jvmArgs.add("-Dfabric.client.gametest")
+ }
'''
when:
def result = gradle.run(task: "prodClient")