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
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
)
@Slf4j
public class FarmTreeRunPlugin extends Plugin {
public static final String version = "1.2.3";
public static final String version = "1.2.5";
@Inject
private FarmTreeRunConfig config;
@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,11 @@ private boolean handlePatch(FarmTreeRunConfig config, Patch patch) {

// Gagex named actions differently, sometimes it's Pick-fruit and sometimes Pick-banana.
// Also seen "Chop down" and "Chop-down".
List<String> exactTreeActions = Arrays.stream(Rs2GameObject.findObjectComposition(treePatch.getId()).getActions()).collect(Collectors.toList());
ObjectComposition treeComposition = Rs2GameObject.findObjectComposition(treePatch.getId());
if (treeComposition == null || treeComposition.getActions() == null) {
return false;
}
List<String> exactTreeActions = Arrays.stream(treeComposition.getActions()).collect(Collectors.toList());
for (String action : exactTreeActions) {
if (action == null)
continue;
Expand Down Expand Up @@ -954,13 +958,13 @@ private void handleNotingFruit(Patch patch) {
* @return {@code true} if payment was successful, else {@code false}
*/
private boolean handlePayment(FarmTreeRunConfig config, Patch patch, PaymentKind action) {
if (isTreePatch(patch) && !isPatchEmpty(patch) && !shouldProtectTree(config) && action != PaymentKind.CLEAR)
if (isTreePatch(patch) && !shouldProtectTree(config) && action != PaymentKind.CLEAR)
return true;

if (isFruitTreePatch(patch) && !isPatchEmpty(patch) && !shouldProtectFruitTree(config) && action != PaymentKind.CLEAR)
if (isFruitTreePatch(patch) && !shouldProtectFruitTree(config) && action != PaymentKind.CLEAR)
return true;

if (isHardTreePatch(patch) && !isPatchEmpty(patch) && !shouldProtectHardTree(config) && action != PaymentKind.CLEAR)
if (isHardTreePatch(patch) && !shouldProtectHardTree(config) && action != PaymentKind.CLEAR)
return true;

int paymentId = isHardTreePatch(patch) ? config.selectedHardTree().getPaymentId()
Expand Down Expand Up @@ -1207,8 +1211,12 @@ private boolean isCompostEnabled(FarmTreeRunConfig config) {
}

private boolean isPatchEmpty(Patch patch) {
String name = Rs2GameObject.getObjectComposition(patch.getId()).getName().toLowerCase();
return name.endsWith("patch");
return isEmptyPatchComposition(Rs2GameObject.getObjectComposition(patch.getId()));
}

static boolean isEmptyPatchComposition(ObjectComposition composition) {
if (composition == null || composition.getName() == null) return true;
return composition.getName().toLowerCase().endsWith("patch");
}

private Integer getLinkedId(int id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package net.runelite.client.plugins.microbot.farmtreerun;

import net.runelite.api.GameObject;
import net.runelite.api.ObjectComposition;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.Script;
import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;
import net.runelite.client.plugins.microbot.util.npc.Rs2Npc;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.ScheduledFuture;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;

class FarmTreeRunHandlePatchTest {

private static FarmTreeRunScript runningScript(FarmTreeRunConfig config) throws Exception {
FarmTreeRunScript script = new FarmTreeRunScript(mock(FarmTreeRunPlugin.class), config);
ScheduledFuture<?> future = mock(ScheduledFuture.class);
when(future.isDone()).thenReturn(false);
Field field = Script.class.getDeclaredField("mainScheduledFuture");
field.setAccessible(true);
field.set(script, future);
return script;
}

@Test
void nullTreeCompositionSkipsPatchInsteadOfThrowing() throws Exception {
FarmTreeRunConfig config = mock(FarmTreeRunConfig.class);
FarmTreeRunScript script = runningScript(config);
GameObject treePatch = mock(GameObject.class);
when(treePatch.getId()).thenReturn(8392);
try (MockedStatic<Microbot> microbot = mockStatic(Microbot.class);
MockedStatic<Rs2GameObject> gameObject = mockStatic(Rs2GameObject.class)) {
microbot.when(Microbot::isLoggedIn).thenReturn(true);
gameObject.when(() -> Rs2GameObject.findObjectByImposter(anyInt(), anyString(), eq(false))).thenReturn(treePatch);
gameObject.when(() -> Rs2GameObject.findObjectComposition(anyInt())).thenReturn(null);

Method handlePatch = FarmTreeRunScript.class.getDeclaredMethod("handlePatch", FarmTreeRunConfig.class, FarmTreeRunScript.Patch.class);
handlePatch.setAccessible(true);
Object result = handlePatch.invoke(script, config, FarmTreeRunScript.Patch.GNOME_STRONGHOLD_TREE_PATCH);

assertEquals(Boolean.FALSE, result);
}
}

@Test
void nullTreeActionsSkipsPatchInsteadOfThrowing() throws Exception {
FarmTreeRunConfig config = mock(FarmTreeRunConfig.class);
FarmTreeRunScript script = runningScript(config);
GameObject treePatch = mock(GameObject.class);
when(treePatch.getId()).thenReturn(8392);
ObjectComposition composition = mock(ObjectComposition.class);
when(composition.getActions()).thenReturn(null);
try (MockedStatic<Microbot> microbot = mockStatic(Microbot.class);
MockedStatic<Rs2GameObject> gameObject = mockStatic(Rs2GameObject.class)) {
microbot.when(Microbot::isLoggedIn).thenReturn(true);
gameObject.when(() -> Rs2GameObject.findObjectByImposter(anyInt(), anyString(), eq(false))).thenReturn(treePatch);
gameObject.when(() -> Rs2GameObject.findObjectComposition(anyInt())).thenReturn(composition);

Method handlePatch = FarmTreeRunScript.class.getDeclaredMethod("handlePatch", FarmTreeRunConfig.class, FarmTreeRunScript.Patch.class);
handlePatch.setAccessible(true);
Object result = handlePatch.invoke(script, config, FarmTreeRunScript.Patch.GNOME_STRONGHOLD_TREE_PATCH);

assertEquals(Boolean.FALSE, result);
}
}

@Test
void disabledProtectionSkipsPaymentWithoutReadingComposition() throws Exception {
FarmTreeRunConfig config = mock(FarmTreeRunConfig.class);
when(config.protectTrees()).thenReturn(false);
FarmTreeRunScript script = runningScript(config);
try (MockedStatic<Rs2GameObject> gameObject = mockStatic(Rs2GameObject.class);
MockedStatic<Rs2Npc> npc = mockStatic(Rs2Npc.class)) {
gameObject.when(() -> Rs2GameObject.getObjectComposition(anyInt())).thenReturn(null);

Class<?> paymentKind = Class.forName(FarmTreeRunScript.class.getName() + "$PaymentKind");
Object protect = java.util.Arrays.stream(paymentKind.getEnumConstants()).filter(k -> k.toString().equals("PROTECT")).findFirst().orElseThrow();
Method handlePayment = FarmTreeRunScript.class.getDeclaredMethod("handlePayment", FarmTreeRunConfig.class, FarmTreeRunScript.Patch.class, paymentKind);
handlePayment.setAccessible(true);
Object result = handlePayment.invoke(script, config, FarmTreeRunScript.Patch.GNOME_STRONGHOLD_TREE_PATCH, protect);

assertEquals(Boolean.TRUE, result);
gameObject.verify(() -> Rs2GameObject.getObjectComposition(anyInt()), never());
npc.verify(() -> Rs2Npc.getNearestNpcWithAction(anyString()), never());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package net.runelite.client.plugins.microbot.farmtreerun;

import net.runelite.api.ObjectComposition;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class FarmTreeRunPatchEmptyTest {

private static ObjectComposition named(String name) {
ObjectComposition composition = mock(ObjectComposition.class);
when(composition.getName()).thenReturn(name);
return composition;
}

@Test
void nullCompositionIsTreatedAsEmptyInsteadOfThrowing() {
assertTrue(FarmTreeRunScript.isEmptyPatchComposition(null));
}

@Test
void nullNameIsTreatedAsEmpty() {
assertTrue(FarmTreeRunScript.isEmptyPatchComposition(named(null)));
}

@Test
void patchNameIsEmpty() {
assertTrue(FarmTreeRunScript.isEmptyPatchComposition(named("Tree patch")));
assertTrue(FarmTreeRunScript.isEmptyPatchComposition(named("Fruit Tree Patch")));
}

@Test
void grownTreeIsNotEmpty() {
assertFalse(FarmTreeRunScript.isEmptyPatchComposition(named("Magic tree")));
assertFalse(FarmTreeRunScript.isEmptyPatchComposition(named("Papaya tree")));
}
}
Loading