1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-30 13:13:00 +00:00

Add a couple of tests for pocket computers

- Ensure they're correctly synced to the client. This definitely isn't
   comprehensive, but doing anything further probably involves multiple
   players, which is tricky.

 - Quick rendering test for in-hand computers.
This commit is contained in:
Jonathan Coates
2022-11-21 21:34:35 +00:00
parent 0fc78acd49
commit 3a96aea894
10 changed files with 216 additions and 32 deletions

View File

@@ -49,9 +49,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
private static final String NBT_UPGRADE = "Upgrade";
private static final String NBT_UPGRADE_INFO = "UpgradeInfo";
public static final String NBT_LIGHT = "Light";
private static final String NBT_ON = "On";
public static final String NBT_ON = "On";
private static final String NBT_INSTANCE = "Instanceid";
private static final String NBT_INSTANCE = "InstanceId";
private static final String NBT_SESSION = "SessionId";
private final ComputerFamily family;

View File

@@ -0,0 +1,15 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.gametest.core;
import net.minecraft.client.Minecraft;
/**
* Extensions to {@link Minecraft}, injected via mixin.
*/
public interface MinecraftExtensions {
boolean computercraft$isRenderingStable();
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.mixin.gametest.client;
import dan200.computercraft.gametest.core.MinecraftExtensions;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.LevelRenderer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import javax.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
@Mixin(Minecraft.class)
class MinecraftMixin implements MinecraftExtensions {
@Final
@Shadow
public LevelRenderer levelRenderer;
@Shadow
@Nullable
public ClientLevel level;
@Shadow
@Nullable
public LocalPlayer player;
@Unique
private final AtomicBoolean isStable = new AtomicBoolean(false);
@Inject(method = "runTick", at = @At("TAIL"))
private void updateStable(boolean render, CallbackInfo ci) {
isStable.set(
level != null && player != null &&
levelRenderer.isChunkCompiled(player.blockPosition()) && levelRenderer.countRenderedChunks() > 10 &&
levelRenderer.hasRenderedAllChunks()
);
}
@Override
public boolean computercraft$isRenderingStable() {
return isStable.get();
}
}

View File

@@ -44,7 +44,7 @@ class Inventory_Test {
/**
* Ensures inventory methods check an item is valid before moving it.
*
* @see <https://github.com/cc-tweaked/cc-restitched/issues/121>
* @see <https://github.com/cc-tweaked/cc-restitched/issues/122>
*/
@GameTest
fun Fails_on_full(helper: GameTestHelper) = helper.sequence {

View File

@@ -0,0 +1,100 @@
package dan200.computercraft.gametest
import dan200.computercraft.api.lua.ObjectArguments
import dan200.computercraft.client.pocket.ClientPocketComputers
import dan200.computercraft.core.apis.TermAPI
import dan200.computercraft.gametest.api.*
import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.computer.core.ComputerState
import dan200.computercraft.shared.pocket.items.PocketComputerItem
import dan200.computercraft.test.core.computer.getApi
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.gametest.framework.GameTestSequence
import org.junit.jupiter.api.Assertions.assertEquals
import kotlin.random.Random
class Pocket_Computer_Test {
/**
* Checks pocket computer state is synced to the holding player.
*/
@ClientGameTest(template = Structures.DEFAULT)
fun Sync_state(context: GameTestHelper) = context.sequence {
// We use a unique label for each test run as computers from previous runs may not have been disposed yet.
val unique = java.lang.Long.toHexString(Random.nextLong())
// Give the player a pocket computer.
thenExecute {
context.positionAt(BlockPos(2, 2, 2))
context.givePocketComputer(unique)
}
// Write some text to the computer.
thenOnComputer(unique) { getApi<TermAPI>().write(ObjectArguments("Hello, world!")) }
// And ensure its synced to the client.
thenIdle(4)
thenOnClient {
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
assertEquals(ComputerState.ON, pocketComputer.state)
val term = pocketComputer.terminal
assertEquals("Hello, world!", term.getLine(0).toString().trim(), "Terminal contents is synced")
}
// Update the terminal contents again.
thenOnComputer(unique) {
val term = getApi<TermAPI>()
term.setCursorPos(1, 1)
term.setCursorBlink(true)
term.write(ObjectArguments("Updated text :)"))
}
// And ensure the new computer state and terminal are sent.
thenIdle(4)
thenOnClient {
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
assertEquals(ComputerState.BLINKING, pocketComputer.state)
val term = pocketComputer.terminal
assertEquals("Updated text :)", term.getLine(0).toString().trim(), "Terminal contents is synced")
}
}
/**
* Checks pocket computers are rendered when being held like a map.
*/
@ClientGameTest(template = Structures.DEFAULT)
fun Renders_map_view(context: GameTestHelper) = context.sequence {
// We use a unique label for each test run as computers from previous runs may not have been disposed yet.
val unique = java.lang.Long.toHexString(Random.nextLong())
// Give the player a pocket computer.
thenExecute {
context.positionAt(BlockPos(2, 2, 2), xRot = 90.0f)
context.givePocketComputer(unique)
}
thenOnComputer(unique) {
val terminal = getApi<TermAPI>().terminal
terminal.write("Hello, world!")
terminal.setCursorPos(1, 2)
terminal.textColour = 2
terminal.backgroundColour = 3
terminal.write("Some coloured text")
}
thenIdle(4)
thenScreenshot(showGui = true)
}
/**
* Give the current player a pocket computer, suitable to be controlled by [GameTestSequence.thenOnComputer].
*/
private fun GameTestHelper.givePocketComputer(name: String? = null) {
val player = level.randomPlayer!!
player.inventory.clearContent()
val testName = (this as GameTestHelperAccessor).testInfo.testName
val label = testName + (if (name == null) "" else ".$name")
val item = ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(1, label, -1, null)
item.getOrCreateTag().putBoolean(PocketComputerItem.NBT_ON, true)
player.inventory.setItem(0, item)
}
}

View File

@@ -1,5 +1,6 @@
package dan200.computercraft.gametest.api
import dan200.computercraft.gametest.core.MinecraftExtensions
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
import dan200.computercraft.shared.platform.Registries
import net.minecraft.client.Minecraft
@@ -20,9 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean
/**
* Attempt to guess whether all chunks have been rendered.
*/
fun Minecraft.isRenderingStable(): Boolean = level != null && player != null &&
levelRenderer.isChunkCompiled(player!!.blockPosition()) && levelRenderer.countRenderedChunks() > 10 &&
levelRenderer.hasRenderedAllChunks()
fun Minecraft.isRenderingStable(): Boolean = (this as MinecraftExtensions).`computercraft$isRenderingStable`()
/**
* Run a task on the client.
@@ -44,7 +43,7 @@ fun GameTestSequence.thenOnClient(task: ClientTestHelper.() -> Unit): GameTestSe
/**
* Take a screenshot of the current game state.
*/
fun GameTestSequence.thenScreenshot(name: String? = null): GameTestSequence {
fun GameTestSequence.thenScreenshot(name: String? = null, showGui: Boolean = false): GameTestSequence {
val suffix = if (name == null) "" else "-$name"
val test = (this as GameTestSequenceAccessor).parent
val fullName = "${test.testName}$suffix"
@@ -63,7 +62,7 @@ fun GameTestSequence.thenScreenshot(name: String? = null): GameTestSequence {
// Now disable the GUI, take a screenshot and reenable it. Sleep a little afterwards to ensure the render thread
// has caught up.
thenOnClient { minecraft.options.hideGui = true }
thenOnClient { minecraft.options.hideGui = !showGui }
thenIdle(2)
// Take a screenshot and wait for it to have finished.
@@ -96,12 +95,12 @@ fun GameTestHelper.positionAtArmorStand() {
/**
* Position the player at a given coordinate.
*/
fun GameTestHelper.positionAt(pos: BlockPos) {
fun GameTestHelper.positionAt(pos: BlockPos, yRot: Float = 0.0f, xRot: Float = 0.0f) {
val absolutePos = absolutePos(pos)
val player = level.randomPlayer ?: throw GameTestAssertException("Player does not exist")
player.setupForTest()
player.connection.teleport(absolutePos.x + 0.5, absolutePos.y + 0.5, absolutePos.z + 0.5, 0.0f, 0.0f)
player.connection.teleport(absolutePos.x + 0.5, absolutePos.y + 0.5, absolutePos.z + 0.5, yRot, xRot)
}
/**

View File

@@ -78,6 +78,7 @@ object TestHooks {
Loot_Test::class.java,
Modem_Test::class.java,
Monitor_Test::class.java,
Pocket_Computer_Test::class.java,
Printer_Test::class.java,
Recipe_Test::class.java,
Turtle_Test::class.java,

View File

@@ -13,5 +13,8 @@
"GameTestSequenceMixin",
"SharedConstantsMixin",
"TestCommandAccessor"
],
"client": [
"client.MinecraftMixin"
]
}