Merge remote-tracking branch 'upstream/master' into runelite

# Conflicts:
#	cache-client/pom.xml
#	cache-updater/pom.xml
#	cache/pom.xml
#	http-api/pom.xml
#	http-service/pom.xml
#	pom.xml
#	runelite-api/pom.xml
#	runelite-api/src/main/java/net/runelite/api/Client.java
#	runelite-api/src/main/java/net/runelite/api/FarmingTrackerTest.java
#	runelite-client/pom.xml
#	runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
#	runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java
#	runelite-script-assembler-plugin/pom.xml
This commit is contained in:
ThatGamerBlue
2021-02-14 00:31:56 +00:00
81 changed files with 3354 additions and 657 deletions

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.
*/
package net.runelite.client.chat;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.awt.Color;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.MessageNode;
import net.runelite.api.Player;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.config.ChatColorConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ChatMessageManagerTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private ChatColorConfig chatColorConfig;
@Inject
private ChatMessageManager chatMessageManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
chatMessageManager.loadColors();
}
@Test
public void testMessageRecoloring()
{
when(chatColorConfig.opaqueServerMessage()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.GAMEMESSAGE);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getValue()).thenReturn("Your dodgy necklace protects you. It has <col=ff0000>1</col> charge left.");
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setValue("<col=b20000>Your dodgy necklace protects you. It has <col=ff0000>1<col=b20000> charge left.</col>");
}
@Test
public void testPublicFriendUsernameRecolouring()
{
final String localPlayerName = "RuneLite";
final String friendName = "Zezima";
when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
// Setup message
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setName(friendName);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getName()).thenReturn(friendName);
// Setup friend checking
Player localPlayer = mock(Player.class);
when(client.isFriended(friendName, true)).thenReturn(true);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(localPlayer.getName()).thenReturn(localPlayerName);
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setName("<col=b20000>" + friendName + "</col>");
}
@Test
public void testPublicIronmanFriendUsernameRecolouring()
{
final String localPlayerName = "RuneLite";
final String friendName = "<img=3>BuddhaPuck";
final String sanitizedFriendName = "BuddhaPuck";
when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
// Setup message
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setName(friendName);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getName()).thenReturn(friendName);
// Setup friend checking
Player localPlayer = mock(Player.class);
when(client.isFriended(sanitizedFriendName, true)).thenReturn(true);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(localPlayer.getName()).thenReturn(localPlayerName);
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setName("<col=b20000>" + friendName + "</col>");
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
* Copyright (c) 2019 Adam <Adam@sigterm.info>
* 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.
*/
package net.runelite.client.plugins.cluescrolls;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameTick;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.banktags.TagManager;
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdLocation;
import net.runelite.client.ui.overlay.OverlayManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ClueScrollPluginTest
{
@Mock
@Bind
Client client;
@Inject
ClueScrollPlugin plugin;
@Bind
@Named("developerMode")
boolean developerMode;
@Mock
@Bind
ClueScrollConfig config;
@Mock
@Bind
OverlayManager overlayManager;
@Mock
@Bind
ItemManager itemManager;
@Mock
@Bind
TagManager tagManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void getGetMirrorPoint()
{
WorldPoint point, converted;
// Zalcano's entrance portal
point = new WorldPoint(3282, 6058, 0);
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertNotEquals(point, converted);
// Elven Crystal Chest, which is upstairs
point = new WorldPoint(3273, 6082, 2);
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertNotEquals(point, converted);
// Around the area of the Elite coordinate clue
point = new WorldPoint(2185, 3280, 0);
// To overworld
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertEquals(point, converted);
// To real
converted = ClueScrollPlugin.getMirrorPoint(point, false);
assertNotEquals(point, converted);
// Brugsen Bursen, Grand Exchange
point = new WorldPoint(3165, 3477, 0);
converted = ClueScrollPlugin.getMirrorPoint(point, false);
assertEquals(point, converted);
}
@Test
public void testLocationHintArrowCleared()
{
final Widget clueWidget = mock(Widget.class);
when(clueWidget.getText()).thenReturn("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Reldo may have a clue.");
final ChatMessage hotColdMessage = new ChatMessage();
hotColdMessage.setType(ChatMessageType.GAMEMESSAGE);
final Player localPlayer = mock(Player.class);
when(client.getWidget(WidgetInfo.CLUE_SCROLL_TEXT)).thenReturn(clueWidget);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(client.getPlane()).thenReturn(0);
when(client.getCachedNPCs()).thenReturn(new NPC[] {});
when(config.displayHintArrows()).thenReturn(true);
// The hint arrow should be reset each game tick from when the clue is read onward
// This is to verify the arrow is cleared the correct number of times during the clue updating process.
int clueSetupHintArrowClears = 0;
// Initialize a beginner hot-cold clue (which will have an end point of LUMBRIDGE_COW_FIELD)
plugin.onGameTick(new GameTick());
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
// Perform the first hot-cold check in Lumbridge near sheep pen (get 2 possible points: LUMBRIDGE_COW_FIELD and DRAYNOR_WHEAT_FIELD)
when(localPlayer.getWorldLocation()).thenReturn(new WorldPoint(3208, 3254, 0));
hotColdMessage.setMessage("The device is hot.");
plugin.onChatMessage(hotColdMessage);
// Move to SW of DRAYNOR_WHEAT_FIELD (hint arrow should be visible here)
when(localPlayer.getWorldLocation()).thenReturn(new WorldPoint(3105, 3265, 0));
when(client.getBaseX()).thenReturn(3056);
when(client.getBaseY()).thenReturn(3216);
plugin.onGameTick(new GameTick());
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
verify(client).setHintArrow(HotColdLocation.DRAYNOR_WHEAT_FIELD.getWorldPoint());
// Test in that location (get 1 possible location: LUMBRIDGE_COW_FIELD)
hotColdMessage.setMessage("The device is hot, and warmer than last time.");
plugin.onChatMessage(hotColdMessage);
plugin.onGameTick(new GameTick());
// Hint arrow should be cleared and not re-set now as the only remaining location is outside of the current
// scene
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
verify(client, times(1)).setHintArrow(any(WorldPoint.class));
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.
*/
package net.runelite.client.plugins.emojis;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.IndexedSprite;
import net.runelite.api.MessageNode;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.chat.ChatMessageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(MockitoJUnitRunner.class)
public class EmojiPluginTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private ChatMessageManager chatMessageManager;
@Inject
private EmojiPlugin emojiPlugin;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void testOnChatMessage()
{
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
// Trip emoji loading
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGGED_IN);
emojiPlugin.onGameStateChanged(gameStateChanged);
MessageNode messageNode = mock(MessageNode.class);
// With chat recolor, message may be wrapped in col tags
when(messageNode.getValue()).thenReturn("<col=ff0000>:) :) :)</col>");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setMessageNode(messageNode);
emojiPlugin.onChatMessage(chatMessage);
verify(messageNode).setValue("<col=ff0000><img=0> <img=0> <img=0></col>");
}
@Test
public void testGtLt()
{
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
// Trip emoji loading
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGGED_IN);
emojiPlugin.onGameStateChanged(gameStateChanged);
MessageNode messageNode = mock(MessageNode.class);
when(messageNode.getValue()).thenReturn("<gt>:D<lt>");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setMessageNode(messageNode);
emojiPlugin.onChatMessage(chatMessage);
verify(messageNode).setValue("<img=10>");
}
@Test
public void testEmojiUpdateMessage()
{
String PARTY_POPPER = "<img=" + (-1 + Emoji.getEmoji("@@@").ordinal()) + '>';
String OPEN_MOUTH = "<img=" + (-1 + Emoji.getEmoji(":O").ordinal()) + '>';
assertNull(emojiPlugin.updateMessage("@@@@@"));
assertEquals(PARTY_POPPER, emojiPlugin.updateMessage("@@@"));
assertEquals(PARTY_POPPER + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@ @@@"));
assertEquals(PARTY_POPPER + ' ' + OPEN_MOUTH, emojiPlugin.updateMessage("@@@\u00A0:O"));
assertEquals(PARTY_POPPER + ' ' + OPEN_MOUTH + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@\u00A0:O @@@"));
assertEquals(PARTY_POPPER + " Hello World " + PARTY_POPPER, emojiPlugin.updateMessage("@@@\u00A0Hello World\u00A0@@@"));
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2020, Adam <Adam@sigterm.info>
* 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.
*/
package net.runelite.client.plugins.grounditems;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemID;
import net.runelite.api.ItemLayer;
import net.runelite.api.Player;
import net.runelite.api.Tile;
import net.runelite.api.TileItem;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ItemSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.ItemManager;
import net.runelite.client.input.KeyManager;
import net.runelite.client.input.MouseManager;
import net.runelite.client.plugins.grounditems.config.HighlightTier;
import net.runelite.client.ui.overlay.OverlayManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class GroundItemsPluginTest
{
@Inject
private GroundItemsPlugin groundItemsPlugin;
@Mock
@Bind
private MouseManager mouseManager;
@Mock
@Bind
private KeyManager keyManager;
@Mock
@Bind
private Client client;
@Mock
@Bind
private ItemManager itemManager;
@Mock
@Bind
private OverlayManager overlayManager;
@Mock
@Bind
private GroundItemsConfig config;
@Mock
@Bind
private GroundItemsOverlay overlay;
@Mock
@Bind
private Notifier notifier;
@Mock
@Bind
private ScheduledExecutorService executor;
@Before
public void setUp()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
doAnswer(a ->
{
a.<Runnable>getArgument(0).run();
return null;
}).when(executor).execute(any(Runnable.class));
when(client.getLocalPlayer()).thenReturn(mock(Player.class));
when(config.getHiddenItems()).thenReturn("");
}
@Test
public void testNotifyHighlightedItem()
{
when(config.getHighlightItems()).thenReturn("abyssal whip");
when(config.notifyTier()).thenReturn(HighlightTier.OFF);
when(config.notifyHighlightedDrops()).thenReturn(true);
when(itemManager.getItemComposition(ItemID.ABYSSAL_WHIP)).thenAnswer(a ->
{
ItemComposition itemComposition = mock(ItemComposition.class);
when(itemComposition.getName()).thenReturn("Abyssal whip");
return itemComposition;
});
// trigger reload of highlighted items list
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup("grounditems");
groundItemsPlugin.onConfigChanged(configChanged);
// spawn whip
Tile tile = mock(Tile.class);
when(tile.getItemLayer()).thenReturn(mock(ItemLayer.class));
when(tile.getWorldLocation()).thenReturn(new WorldPoint(0, 0, 0));
TileItem tileItem = mock(TileItem.class);
when(tileItem.getId()).thenReturn(ItemID.ABYSSAL_WHIP);
when(tileItem.getQuantity()).thenReturn(1);
groundItemsPlugin.onItemSpawned(new ItemSpawned(tile, tileItem));
verify(notifier).notify("You received a highlighted drop: Abyssal whip");
}
}

View File

@@ -0,0 +1,323 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.
*/
package net.runelite.client.plugins.idlenotifier;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.Actor;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Hitsplat;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Player;
import net.runelite.api.VarPlayer;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.HitsplatApplied;
import net.runelite.api.events.InteractingChanged;
import net.runelite.client.Notifier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.Mock;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class IdleNotifierPluginTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private IdleNotifierConfig config;
@Mock
@Bind
private Notifier notifier;
@Inject
private IdleNotifierPlugin plugin;
@Mock
private NPC monster;
@Mock
private NPC randomEvent;
@Mock
private NPC fishingSpot;
@Mock
private Player player;
@Before
public void setUp()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
// Mock monster
final String[] monsterActions = new String[] { "Attack", "Examine" };
final NPCComposition monsterComp = mock(NPCComposition.class);
when(monsterComp.getActions()).thenReturn(monsterActions);
when(monster.getComposition()).thenReturn(monsterComp);
// Mock random event
final String[] randomEventActions = new String[] { "Talk-to", "Dismiss", "Examine" };
final NPCComposition randomEventComp = mock(NPCComposition.class);
when(randomEventComp.getActions()).thenReturn(randomEventActions);
when(randomEvent.getComposition()).thenReturn(randomEventComp);
// Mock Fishing Spot
final String[] fishingSpotActions = new String[] { "Use-rod", "Examine" };
final NPCComposition fishingSpotComp = mock(NPCComposition.class);
when(fishingSpotComp.getActions()).thenReturn(fishingSpotActions);
when(fishingSpot.getComposition()).thenReturn(fishingSpotComp);
when(fishingSpot.getName()).thenReturn("Fishing spot");
// Mock player
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
when(client.getLocalPlayer()).thenReturn(player);
// Mock config
when(config.logoutIdle()).thenReturn(true);
when(config.animationIdle()).thenReturn(true);
when(config.interactionIdle()).thenReturn(true);
when(config.getIdleNotificationDelay()).thenReturn(0);
when(config.getHitpointsThreshold()).thenReturn(42);
when(config.getPrayerThreshold()).thenReturn(42);
// Mock client
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getKeyboardIdleTicks()).thenReturn(42);
when(client.getMouseLastPressedMillis()).thenReturn(System.currentTimeMillis() - 100_000L);
}
@Test
public void checkAnimationIdle()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now idle!");
}
@Test
public void checkAnimationReset()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.LOOKING_INTO);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkAnimationLogout()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
// Logout
when(client.getGameState()).thenReturn(GameState.LOGIN_SCREEN);
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGIN_SCREEN);
plugin.onGameStateChanged(gameStateChanged);
// Log back in
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
gameStateChanged.setGameState(GameState.LOGGED_IN);
plugin.onGameStateChanged(gameStateChanged);
// Tick
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatIdle()
{
when(player.getInteracting()).thenReturn(monster);
plugin.onInteractingChanged(new InteractingChanged(player, monster));
plugin.onGameTick(new GameTick());
when(player.getInteracting()).thenReturn(null);
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now out of combat!");
}
@Test
public void checkCombatReset()
{
when(player.getInteracting()).thenReturn(mock(Actor.class));
plugin.onInteractingChanged(new InteractingChanged(player, monster));
plugin.onGameTick(new GameTick());
plugin.onInteractingChanged(new InteractingChanged(player, randomEvent));
plugin.onGameTick(new GameTick());
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatLogout()
{
plugin.onInteractingChanged(new InteractingChanged(player, monster));
when(player.getInteracting()).thenReturn(mock(Actor.class));
plugin.onGameTick(new GameTick());
// Logout
when(client.getGameState()).thenReturn(GameState.LOGIN_SCREEN);
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGIN_SCREEN);
plugin.onGameStateChanged(gameStateChanged);
// Log back in
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
gameStateChanged.setGameState(GameState.LOGGED_IN);
plugin.onGameStateChanged(gameStateChanged);
// Tick
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatLogoutIdle()
{
// Player is idle
when(client.getMouseIdleTicks()).thenReturn(80_000);
// But player is being damaged (is in combat)
final HitsplatApplied hitsplatApplied = new HitsplatApplied();
hitsplatApplied.setActor(player);
hitsplatApplied.setHitsplat(new Hitsplat(Hitsplat.HitsplatType.DAMAGE_ME, 0, 0));
plugin.onHitsplatApplied(hitsplatApplied);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void doubleNotifyOnMouseReset()
{
// Player is idle, but in combat so the idle packet is getting set repeatedly
// make sure we are not notifying
when(client.getKeyboardIdleTicks()).thenReturn(80_000);
when(client.getMouseIdleTicks()).thenReturn(14_500);
plugin.onGameTick(new GameTick());
plugin.onGameTick(new GameTick());
verify(notifier, times(1)).notify(any());
}
@Test
public void testSendOneNotificationForAnimationAndInteract()
{
when(player.getInteracting()).thenReturn(fishingSpot);
when(player.getAnimation()).thenReturn(AnimationID.FISHING_POLE_CAST);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onInteractingChanged(new InteractingChanged(player, fishingSpot));
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, never()).notify(anyString());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
lenient().when(player.getInteracting()).thenReturn(null);
plugin.onAnimationChanged(animationChanged);
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now idle!");
}
@Test
public void testSpecRegen()
{
when(config.getSpecEnergyThreshold()).thenReturn(50);
when(client.getVar(eq(VarPlayer.SPECIAL_ATTACK_PERCENT))).thenReturn(400); // 40%
plugin.onGameTick(new GameTick()); // once to set lastSpecEnergy to 400
verify(notifier, never()).notify(any());
when(client.getVar(eq(VarPlayer.SPECIAL_ATTACK_PERCENT))).thenReturn(500); // 50%
plugin.onGameTick(new GameTick());
verify(notifier).notify(eq("You have restored spec energy!"));
}
@Test
public void testMovementIdle()
{
when(config.movementIdle()).thenReturn(true);
when(player.getWorldLocation()).thenReturn(new WorldPoint(0, 0, 0));
plugin.onGameTick(new GameTick());
when(player.getWorldLocation()).thenReturn(new WorldPoint(1, 0, 0));
plugin.onGameTick(new GameTick());
// No movement here
plugin.onGameTick(new GameTick());
verify(notifier).notify(eq("You have stopped moving!"));
}
}

View File

@@ -57,6 +57,7 @@ import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@@ -67,7 +68,8 @@ public class ScreenshotPluginTest
private static final String BARROWS_CHEST = "Your Barrows chest count is <col=ff0000>310</col>";
private static final String CHAMBERS_OF_XERIC_CHEST = "Your completed Chambers of Xeric count is: <col=ff0000>489</col>.";
private static final String THEATRE_OF_BLOOD_CHEST = "Your completed Theatre of Blood count is: <col=ff0000>73</col>.";
private static final String VALUABLE_DROP = "<col=ef1020>Valuable drop: 6 x Bronze arrow (42 coins)</col>";
private static final String NOT_SO_VALUABLE_DROP = "<col=ef1020>Valuable drop: 6 x Bronze arrow (42 coins)</col>";
private static final String VALUABLE_DROP = "<col=ef1020>Valuable drop: Rune scimitar (25,600 coins)</col>";
private static final String UNTRADEABLE_DROP = "<col=ef1020>Untradeable drop: Rusty sword";
private static final String BA_HIGH_GAMBLE_REWARD = "Raw shark (x 300)!<br>High level gamble count: <col=7f0000>100</col>";
private static final String HUNTER_LEVEL_2_TEXT = "<col=000080>Congratulations, you've just advanced a Hunter level.<col=000000><br><br>Your Hunter level is now 2.";
@@ -121,6 +123,7 @@ public class ScreenshotPluginTest
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(screenshotConfig.screenshotLevels()).thenReturn(true);
when(screenshotConfig.screenshotValuableDrop()).thenReturn(true);
when(screenshotConfig.valuableDropThreshold()).thenReturn(1000);
when(screenshotConfig.screenshotUntradeableDrop()).thenReturn(true);
}
@@ -161,10 +164,30 @@ public class ScreenshotPluginTest
assertEquals(73, screenshotPlugin.gettheatreOfBloodNumber());
}
@Test
public void testNotSoValuableDrop()
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", NOT_SO_VALUABLE_DROP, null, 0);
screenshotPlugin.onChatMessage(chatMessageEvent);
verifyNoInteractions(drawManager);
when(screenshotConfig.valuableDropThreshold()).thenReturn(0);
screenshotPlugin.onChatMessage(chatMessageEvent);
verify(drawManager).requestNextFrameListener(any(Consumer.class));
}
@Test
public void testValuableDrop()
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", VALUABLE_DROP, null, 0);
when(screenshotConfig.valuableDropThreshold()).thenReturn(100_000);
screenshotPlugin.onChatMessage(chatMessageEvent);
verifyNoInteractions(drawManager);
when(screenshotConfig.valuableDropThreshold()).thenReturn(1000);
screenshotPlugin.onChatMessage(chatMessageEvent);
verify(drawManager).requestNextFrameListener(any(Consumer.class));

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
package net.runelite.client.plugins.timetracking.farming;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.EnumSet;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneScapeProfile;
import net.runelite.client.config.RuneScapeProfileType;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class FarmingTrackerTest
{
@Inject
private FarmingTracker farmingTracker;
@Mock
@Bind
private Client client;
@Mock
@Bind
private ItemManager itemManager;
@Mock
@Bind
private ConfigManager configManager;
@Mock
@Bind
private TimeTrackingConfig config;
@Mock
@Bind
private FarmingWorld farmingWorld;
@Mock
@Bind
private Notifier notifier;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getWorldType()).thenReturn(EnumSet.noneOf(WorldType.class));
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(client.getLocalPlayer()).thenReturn(player);
}
@Test(expected = IllegalStateException.class)
public void testEmptyNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
);
FarmingPatch patch = region.getPatches()[4];
patch.setRegion(region);
farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch);
}
@Test
public void testHarvestableNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
);
FarmingPatch patch = region.getPatches()[3];
patch.setRegion(region);
farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch);
verify(notifier).notify("Your Ranarr is ready to harvest in Ardougne.");
}
}