diff --git a/src/main/java/com/attacktimer/AttackTimerMetronomePlugin.java b/src/main/java/com/attacktimer/AttackTimerMetronomePlugin.java index a22e98a..a70d881 100644 --- a/src/main/java/com/attacktimer/AttackTimerMetronomePlugin.java +++ b/src/main/java/com/attacktimer/AttackTimerMetronomePlugin.java @@ -398,14 +398,15 @@ public boolean isAttackCooldownPending() // Match only the start of the line with `^` and the Pattern.MULTILINE private static final Pattern EAT_MESSAGE = Pattern - .compile("^(" + GENERIC_EAT + "|" + BARBARIAN_POTIONS + "|" + JUG_OF_WINE + ")", Pattern.MULTILINE); + .compile("^(" + GENERIC_EAT + "|" + BARBARIAN_POTIONS + "|" + JUG_OF_WINE + ")", Pattern.MULTILINE & Pattern.CASE_INSENSITIVE); // gnome foods are also fast eats (Note these are not the food names as the wiki lists them, but the name // as written in chat), also pre-made and handmade have the same chat message. private static final String FAST_GNOME_FOOD = "worm hole|tangled toads legs|veg ball|chocolate bomb|worm crunchies|toad crunchies|" + "choc chip crunchies|spicy crunchies|fruit batta|cheese and tomato batta|toad batta|vegetable batta|worm batta"; private static final String FAST_FOOD = "karambwan|halibut"; - private static final Pattern FAST_EAT = Pattern.compile("(" + FAST_FOOD + "|" + FAST_GNOME_FOOD + ")"); + // TODO ^ find out the food messages for https://oldschool.runescape.wiki/w/Crystal_paddlefish and https://oldschool.runescape.wiki/w/Corrupted_paddlefish + private static final Pattern FAST_EAT = Pattern.compile("(" + FAST_FOOD + "|" + FAST_GNOME_FOOD + ")", Pattern.CASE_INSENSITIVE); @Subscribe public void onChatMessage(ChatMessage event) diff --git a/src/test/java/com/attacktimer/EatTest.java b/src/test/java/com/attacktimer/EatTest.java new file mode 100644 index 0000000..c11c70a --- /dev/null +++ b/src/test/java/com/attacktimer/EatTest.java @@ -0,0 +1,78 @@ +package com.attacktimer; + +/* + * Copyright (c) 2026, Lexer747 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import static org.mockito.Mockito.when; +import static org.junit.Assert.assertSame; +import org.junit.Test; + +import com.attacktimer.AttackTimerMetronomePlugin.AttackState; + +import net.runelite.api.ChatMessageType; +import net.runelite.api.Player; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameTick; + +public class EatTest extends IntegrationTests +{ + @Test + public void ExhaustiveEatTest() throws Exception + { + Player mockedPlayer = pluginMockSetup(); + when(mockedClient.getTickCount()).thenReturn(0); + when(mockedPlayer.getAnimation()).thenReturn(AnimationData.MELEE_GENERIC_SLASH.animationId); + underTest.onGameTick(new GameTick()); + assertSame(AttackState.DELAYED_FIRST_TICK, underTest.attackState); + assertSame(3, underTest.attackDelayHoldoffTicks); + String[] fastFoodsToTest = { + // NOTE case sensitivity here is import this should reflect in-game so that the plugin is resilient + // to mixed casing + "Karambwan", "halibut", "choc chip crunchies", + }; + String[] foodsToTest = { + "shark", "meat pizza", "A brand new food message", "sunlight antelope", "moonlight antelope", + "purple sweets" + }; + var curEatDelayTicks = underTest.pendingEatDelayTicks; + for (String food : foodsToTest) + { + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You eat the " + food + ".", "", + 0); + underTest.onChatMessage(chatMessage); + curEatDelayTicks += 3; + assertSame(curEatDelayTicks, underTest.pendingEatDelayTicks); + } + + for (String food : fastFoodsToTest) + { + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You eat the " + food + ".", "", + 0); + underTest.onChatMessage(chatMessage); + curEatDelayTicks += 2; + assertSame(curEatDelayTicks, underTest.pendingEatDelayTicks); + } + } +} diff --git a/src/test/java/com/attacktimer/IntegrationTests.java b/src/test/java/com/attacktimer/IntegrationTests.java index 7fb4d4d..49e2d21 100644 --- a/src/test/java/com/attacktimer/IntegrationTests.java +++ b/src/test/java/com/attacktimer/IntegrationTests.java @@ -81,38 +81,38 @@ public class IntegrationTests { @Mock @Bind - private OverlayManager mockedOverlayManager; + protected OverlayManager mockedOverlayManager; @Mock @Bind - private ConfigManager mockedConfigManager; + protected ConfigManager mockedConfigManager; @Mock @Bind - private AttackTimerMetronomeTileOverlay mockedOverlay; + protected AttackTimerMetronomeTileOverlay mockedOverlay; @Mock @Bind - private AttackTimerBarOverlay mockedBarOverlay; + protected AttackTimerBarOverlay mockedBarOverlay; @Mock @Bind - private AttackTimerMetronomeConfig mockedConfig; + protected AttackTimerMetronomeConfig mockedConfig; @Mock @Bind - private ItemManager mockedItemManager; + protected ItemManager mockedItemManager; @Mock @Bind - private Client mockedClient; + protected Client mockedClient; @Mock @Bind - private NPCManager mockedNpcManager; + protected NPCManager mockedNpcManager; @Inject - private AttackTimerMetronomePlugin underTest; + protected AttackTimerMetronomePlugin underTest; @Before public void setup() @@ -307,7 +307,7 @@ public void eatingFoodTest() throws Exception performStateVerificationOrUpdate(channel, Paths.get(testdata + "eatingFoodTest.txt")); } - private void performStateVerificationOrUpdate(ByteArrayDataOutput channel, Path path) throws IOException + protected void performStateVerificationOrUpdate(ByteArrayDataOutput channel, Path path) throws IOException { var actualBytes = channel.toByteArray(); switch (Update.system()) @@ -330,13 +330,13 @@ private void performStateVerificationOrUpdate(ByteArrayDataOutput channel, Path } } - private void onGameTick(ByteArrayDataOutput file) + protected void onGameTick(ByteArrayDataOutput file) { underTest.onGameTick(new GameTick()); underTest.writeState(file); } - private void writeTestMessage(String message, ByteArrayDataOutput file) + protected void writeTestMessage(String message, ByteArrayDataOutput file) { file.write(testMessagePrefix); file.write(message.getBytes(StandardCharsets.UTF_8));