Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ minecraft {
property 'forge.logging.console.level', 'debug'
property 'forge.enabledGameTestNamespaces', 'advancedperipherals,advancedperipheralstest'
property 'advancedperipheralstest.sources', file("src/testMod/resources/data/advancedperipheralstest").absolutePath
property 'advancedperipheralstest.tags', 'common,client'

args "--mixin.config=ccgametest.mixins.json"
args "--mixin.config=advancedperipheralstest.mixins.json"
Expand Down Expand Up @@ -667,7 +668,9 @@ publishing {
}

tasks.register('runGameTestClient', ClientJavaExec, { task ->
task.outputs.upToDateWhen { false }

description "Runs client-side gametests with no mods"
setRunConfig(minecraft.runs["testClient"])
setRunConfig(minecraft.runs.testClient)
tags("client")
})
7 changes: 6 additions & 1 deletion buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.getByName
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.process.CommandLineArgumentProvider
import java.io.File
import java.nio.file.Files
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -91,7 +92,11 @@ abstract class ClientJavaExec : JavaExec() {
* Only run tests with the given tags.
*/
fun tags(vararg tags: String) {
systemProperty("advancedperipheralstest.tags", tags.joinToString(","))
jvmArgumentProviders.add(
CommandLineArgumentProvider {
listOf("-Dadvancedperipheralstest.tags=${tags.joinToString(",")}")
}
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dan200.computercraft.gametest.core;

import com.google.common.collect.Lists;
import net.minecraft.client.gui.components.toasts.Toast;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ToastAddEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

import java.util.List;

@Mod.EventBusSubscriber(modid = TestMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
public class ClientTestEvents {
public static final List<Toast> toasts = Lists.newArrayList();

@SubscribeEvent
public static void onToast(ToastAddEvent event) {
toasts.add(event.getToast());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ object ClientTestHooks {
fun onOpenScreen(screen: Screen): Boolean = when {
enabled && !loadedWorld && (screen is TitleScreen || screen is AccessibilityOptionsScreen) -> {
loadedWorld = true
openWorld()
openWorld(screen)
true
}

Expand All @@ -66,7 +66,7 @@ object ClientTestHooks {
/**
* Open or create our test world immediately on game launch.
*/
private fun openWorld() {
private fun openWorld(screen: Screen) {
val minecraft = Minecraft.getInstance()

// Clear some options before we get any further.
Expand All @@ -81,7 +81,7 @@ object ClientTestHooks {

if (minecraft.levelSource.levelExists(LEVEL_NAME)) {
LOG.info("World already exists, opening.")
minecraft.createWorldOpenFlows().loadLevel(minecraft.screen!!, LEVEL_NAME)
minecraft.createWorldOpenFlows().loadLevel(screen, LEVEL_NAME)
} else {
LOG.info("World does not exist, creating it.")
val rules = GameRules()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.srendi.advancedperipherals.test

import net.minecraft.client.Minecraft
import net.minecraft.gametest.framework.GameTestAssertException
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.network.chat.Component
import java.util.function.Predicate

fun GameTestHelper.chatContains(filter: Predicate<Component>) = Minecraft.getInstance().gui.chat.allMessages.any { filter.test(it.content) }

fun GameTestHelper.assertChatContains(component: Component) {
if (!chatContains { it == component }) {
throw GameTestAssertException("Expected chat to contain $component")
}
}

fun GameTestHelper.assertChatContains(filter: Predicate<Component>) {
if (!chatContains(filter)) {
throw GameTestAssertException("Expected chat to contain message matching filter")
}
}

fun GameTestHelper.assertChatNotContains(component: Component) {
if (chatContains { it == component }) {
throw GameTestAssertException("Expected chat to not contain $component")
}
}

fun GameTestHelper.assertChatNotContains(filter: Predicate<Component>) {
if (chatContains(filter)) {
throw GameTestAssertException("Expected chat to not contain message matching filter")
}
}
172 changes: 172 additions & 0 deletions src/testMod/kotlin/de/srendi/advancedperipherals/test/ChatBoxTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package de.srendi.advancedperipherals.test

import dan200.computercraft.gametest.api.*
import dan200.computercraft.gametest.api.Timeouts.SECOND
import dan200.computercraft.gametest.core.ClientTestEvents
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.toasts.SystemToast
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.network.chat.ClickEvent
import net.minecraft.network.chat.Component
import java.util.function.BiPredicate

@GameTestHolder
class ChatBoxTest {

private fun getFormattedMessage(bracketColor: String, openBracket: Char, closeBracket: Char, prefix: String, message: String): Component {
return Component.literal("$bracketColor$openBracket§r")
.append(prefix)
.append("$bracketColor$closeBracket§r ")
.append(
Component.literal("Red ").withStyle(ChatFormatting.RED)
.append(Component.literal("Bold ").withStyle(ChatFormatting.BOLD).withStyle(ChatFormatting.WHITE))
.append(Component.literal("Click ").withStyle(ChatFormatting.UNDERLINE).withStyle(ChatFormatting.WHITE)
.withStyle { it.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, "https://advancedperipherals.madefor.cc/")) })
.append(Component.literal(message).withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.AQUA))
)
}

private fun getFormattedToast(bracketColor: ChatFormatting?, openBracket: Char, closeBracket: Char, prefix: String, message: String): List<Component> {
val prefixComponents = if (bracketColor != null) {
listOf(
Component.literal("$openBracket").withStyle(bracketColor),
Component.literal(prefix),
Component.literal("$closeBracket").withStyle(bracketColor),
Component.literal(" ")
)
} else {
listOf(
Component.literal("$openBracket$prefix$closeBracket ")
)
}

return prefixComponents + listOf(
Component.literal("Red ").withStyle(ChatFormatting.RED),
Component.literal("Bold ").withStyle(ChatFormatting.WHITE).withStyle(ChatFormatting.BOLD),
Component.literal("Click ").withStyle(ChatFormatting.WHITE).withStyle(ChatFormatting.UNDERLINE)
.withStyle { it.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, "https://advancedperipherals.madefor.cc/")) },
Component.literal(message).withStyle(ChatFormatting.AQUA).withStyle(ChatFormatting.ITALIC)
)
}

private fun containsToast(filter: BiPredicate<Component, List<Component>>): Boolean {
return ClientTestEvents.toasts.filterIsInstance<SystemToast>().any {
val sink = ComponentFormattedCharSink()
it.messageLines.forEachIndexed { index, line ->
line.accept(sink)
if (index < it.messageLines.size - 1) {
sink.accept(0, sink.currentStyle!!, ' '.code)
}
}

filter.test(it.title, sink.getComponents())
}
}

private fun assertContainsToast(title: Component, components: List<Component>) {
if (!containsToast { toastTitle, toastComponents -> toastTitle == title && toastComponents == components }) {
throw AssertionError("Toast with title $title and components $components not found")
}
}

private fun assertNotContainsToast(title: Component, components: List<Component>) {
if (containsToast { toastTitle, toastComponents -> toastTitle == title && toastComponents == components }) {
throw AssertionError("Toast with title $title and components $components found")
}
}

private fun assertNotContainsToast(filter: BiPredicate<Component, List<Component>>) {
if (containsToast(filter)) {
throw AssertionError("Toast matching filter found")
}
}

@ClientGameTest(timeoutTicks = 60 * SECOND)
fun chatBox(context: GameTestHelper) = context.sequence {
thenExecute { context.positionAt(BlockPos(2, 6, 2), 90f) }
thenOnClient {
Minecraft.getInstance().gui.chat.clearMessages(false)
ClientTestEvents.toasts.clear()
}
thenComputerOk()

thenOnClient {
// sendMessage
context.assertChatContains(Component.literal("[§r").append("AP").append("]§r ").append("Default message"))
context.assertChatContains(Component.literal("[§r").append("GameTest").append("]§r ").append("Message with prefix"))
context.assertChatContains(Component.literal("<§r").append("GameTest").append(">§r ").append("Message with brackets"))
context.assertChatContains(Component.literal("§a<§r").append("GameTest").append("§a>§r ").append("Message with bracket color"))
context.assertChatNotContains(Component.literal("§a<§r").append("GameTest").append("§a>§r ").append("Message with short range"))
context.assertChatNotContains { it.toString().contains("Message with invalid brackets") }

// sendMessageToPlayer
context.assertChatContains(Component.literal("[§r").append("AP").append("]§r ").append("Default message to player"))
context.assertChatContains(Component.literal("[§r").append("GameTest").append("]§r ").append("Message with prefix to player"))
context.assertChatContains(Component.literal("<§r").append("GameTest").append(">§r ").append("Message with brackets to player"))
context.assertChatContains(Component.literal("§a<§r").append("GameTest").append("§a>§r ").append("Message with bracket color to player"))
context.assertChatNotContains(Component.literal("§a<§r").append("GameTest").append("§a>§r ").append("Message with short range to player"))
context.assertChatNotContains { it.toString().contains("Message with invalid brackets to player") }
context.assertChatNotContains(Component.literal("[§r").append("AP").append("]§r ").append("Default message to invalid player"))

// sendFormattedMessage
context.assertChatContains(getFormattedMessage("", '[', ']', "AP", "Default formatted message"))
context.assertChatContains(getFormattedMessage("", '[', ']', "GameTest", "Formatted message with prefix"))
context.assertChatContains(getFormattedMessage("", '<', '>', "GameTest", "Formatted message with brackets"))
context.assertChatContains(getFormattedMessage("§a", '<', '>', "GameTest", "Formatted message with bracket color"))
context.assertChatNotContains(getFormattedMessage("§a", '<', '>', "GameTest", "Formatted message with short range"))
context.assertChatNotContains { it.toString().contains("Formatted message with invalid brackets") }

// sendFormattedMessageToPlayer
context.assertChatContains(getFormattedMessage("", '[', ']', "AP", "Default formatted message to player"))
context.assertChatContains(getFormattedMessage("", '[', ']', "GameTest", "Formatted message with prefix to player"))
context.assertChatContains(getFormattedMessage("", '<', '>', "GameTest", "Formatted message with brackets to player"))
context.assertChatContains(getFormattedMessage("§a", '<', '>', "GameTest", "Formatted message with bracket color to player"))
context.assertChatNotContains(getFormattedMessage("§a", '<', '>', "GameTest", "Formatted message with short range to player"))
context.assertChatNotContains { it.toString().contains("Formatted message with invalid brackets to player") }
context.assertChatNotContains(getFormattedMessage("", '[', ']', "AP", "Default formatted message to invalid player"))

// sendToastToPlayer
val defaultToastTitle = Component.literal("Toast Title")
assertContainsToast(defaultToastTitle, listOf(Component.literal("[AP] Default toast to player")))
assertContainsToast(defaultToastTitle, listOf(Component.literal("[GameTest] Toast with prefix to player")))
assertContainsToast(defaultToastTitle, listOf(Component.literal("<GameTest> Toast with brackets to player")))
assertContainsToast(defaultToastTitle, listOf(
Component.literal("<").withStyle(ChatFormatting.GREEN),
Component.literal("GameTest"),
Component.literal(">").withStyle(ChatFormatting.GREEN),
Component.literal(" Toast with bracket color to player")
))
assertNotContainsToast(defaultToastTitle, listOf(
Component.literal("<").withStyle(ChatFormatting.GREEN),
Component.literal("GameTest"),
Component.literal(">").withStyle(ChatFormatting.GREEN),
Component.literal(" Toast with short range to player")
))
assertNotContainsToast { title, components -> title == defaultToastTitle && components.any { it.toString().contains("Toast with invalid brackets to player") } }
assertNotContainsToast(defaultToastTitle, listOf(Component.literal("[AP] Default toast to invalid player")))

// sendFormattedToastToPlayer
val formattedToastTitle = Component.literal("Formatted Toast Title").withStyle(ChatFormatting.DARK_PURPLE)
assertContainsToast(formattedToastTitle, getFormattedToast(null, '[', ']', "AP", "Default formatted toast to player"))
assertContainsToast(formattedToastTitle, getFormattedToast(null, '[', ']', "GameTest", "Formatted toast with prefix to player"))
assertContainsToast(formattedToastTitle, getFormattedToast(null, '<', '>', "GameTest", "Formatted toast with brackets to player"))
assertContainsToast(formattedToastTitle, getFormattedToast(ChatFormatting.GREEN, '<', '>', "GameTest", "Formatted toast with bracket color to player"))
assertNotContainsToast(formattedToastTitle, getFormattedToast(ChatFormatting.GREEN, '<', '>', "GameTest", "Formatted toast with short range to player"))
assertNotContainsToast { title, components -> title == formattedToastTitle && components.any { it.toString().contains("Formatted toast with invalid brackets to player") } }
assertNotContainsToast(formattedToastTitle, getFormattedToast(null, '[', ']', "AP", "Default formatted toast to invalid player"))
}
}

@ClientGameTest
fun chatBox_Events(context: GameTestHelper) = context.sequence {
thenIdle(20)
thenOnClient { Minecraft.getInstance().player!!.chatSigned("This is a normal chat message", null) }
thenIdle(20)
thenOnClient { Minecraft.getInstance().player!!.chatSigned("\$This is a hidden chat message", null) }
thenIdle(20)
thenComputerOk()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package de.srendi.advancedperipherals.test

import net.minecraft.network.chat.Component
import net.minecraft.network.chat.Style
import net.minecraft.util.FormattedCharSink

class ComponentFormattedCharSink : FormattedCharSink {
var currentStyle: Style? = null;
private var currentText: String = "";
private val components = mutableListOf<Component>();

override fun accept(pPositionInCurrentSequence: Int, pStyle: Style, pCodePoint: Int): Boolean {
if (currentStyle?.equals(pStyle) == false) {
if (currentText.isNotEmpty()) {
components.add(Component.literal(currentText).withStyle(simplifyStyle(currentStyle!!)))
currentText = ""
}
}

currentStyle = pStyle
currentText += String(Character.toChars(pCodePoint))

return true
}

fun getComponents(): List<Component>{
if (currentText.isNotEmpty()) {
components.add(Component.literal(currentText).withStyle(simplifyStyle(currentStyle!!)))
currentText = ""
}

return components
}

private fun simplifyStyle(style: Style): Style {
return style
.withBold(if (style.isBold) true else null)
.withItalic(if (style.isItalic) true else null)
.withUnderlined(if (style.isUnderlined) true else null)
.withStrikethrough(if (style.isStrikethrough) true else null)
.withObfuscated(if (style.isObfuscated) true else null)
}

}
6 changes: 5 additions & 1 deletion src/testMod/resources/META-INF/accesstransformer.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ public net.minecraft.gametest.framework.GameTestHelper m_177448_()Lnet/minecraft
public net.minecraft.gametest.framework.GameTestHelper f_127595_ # testInfo

public net.minecraft.gametest.framework.GameTestSequence f_127774_ # parent
public net.minecraft.gametest.framework.MultipleTestTracker f_127798_ # tests
public net.minecraft.gametest.framework.MultipleTestTracker f_127798_ # tests

public net.minecraft.client.gui.components.ChatComponent f_93760_ # allMessages
public net.minecraft.client.gui.components.toasts.SystemToast f_94821_ # title
public net.minecraft.client.gui.components.toasts.SystemToast f_94822_ # messageLines
Loading