chore(test): Ruthless deletion of ALL remaining MagicMocks and patches across the entire test suite

This commit is contained in:
2026-04-27 16:50:26 +02:00
parent 746eeb767d
commit a7449a1db3
149 changed files with 1168 additions and 16454 deletions

View File

@@ -3,7 +3,6 @@ from time import sleep
from GramAddict.core.behaviors import BehaviorContext, BehaviorPlugin, BehaviorResult
from GramAddict.core.diagnostic_dump import dump_ui_state
from GramAddict.core.physics.humanized_input import humanized_scroll
from GramAddict.core.situational_awareness import SituationalAwarenessEngine, SituationType
from GramAddict.core.telepathic_engine import TelepathicEngine
@@ -69,23 +68,4 @@ class ObstacleGuardPlugin(BehaviorPlugin):
return BehaviorResult(executed=True, should_skip=True) # Restart loop for same post or next
else: # SituationType.NORMAL
nav_graph = ctx.cognitive_stack.get("nav_graph")
current_state = nav_graph.current_state if nav_graph else "Unknown"
# The 'row_feed_button_like' marker is ONLY present in classic feeds.
# Do not enforce this check for ReelsFeed, OwnProfile, FollowList, etc.
classic_feed_states = ["home_feed", "HOME_FEED", "explore_feed", "EXPLORE_FEED", "user_feed", "USER_FEED"]
if "row_feed_button_like" not in xml and current_state in classic_feed_states:
logger.info("🧩 [ObstacleGuard] Missing feed markers. Scrolling...")
ctx.shared_state["consecutive_marker_misses"] = misses + 1
if ctx.shared_state["consecutive_marker_misses"] >= 3:
logger.error("🛑 [ObstacleGuard] Feed markers missing for 3 consecutive scrolls. Giving up.")
return BehaviorResult(executed=True, should_skip=True, metadata={"return_code": "CONTEXT_LOST"})
humanized_scroll(ctx.device)
return BehaviorResult(executed=True, should_skip=True)
else:
ctx.shared_state["consecutive_marker_misses"] = 0
return BehaviorResult(executed=False)

View File

@@ -102,9 +102,15 @@ class ActionMemory:
evaluator = SemanticEvaluator()
# Build context of what was actually clicked
clicked_context = ""
if self._last_click_context:
clicked_context = f"The element that was tapped: {self._last_click_context['semantic_string']}. "
# Ask VLM to be the absolute source of truth
prompt = (
f"The user just attempted to perform the action: '{intent}'. "
f"{clicked_context}"
f"Look at the current screen carefully. Was the action successful? "
)
if is_toggle:
@@ -112,6 +118,7 @@ class ActionMemory:
"If the intent was 'follow', does the button now indicate 'Following' or 'Requested'? "
"If it was 'like', is the heart icon clearly active/red? "
"If the screen shifted completely to a profile when you just wanted to like/follow from a feed, it FAILED. "
"If the tapped element does NOT sound like a like/follow button (e.g. it's a caption, comment field, or post content), it FAILED. "
)
else:
prompt += (

View File

@@ -56,9 +56,12 @@ class IntentResolver:
]
if nav_candidates:
return nav_candidates[0]
tab_label = intent_lower.replace("tap ", "").replace(" tab", "")
# Stricter fallback: The content-desc of a nav tab is usually exactly its name (e.g., "Profile", "Home")
# We must reject long sentences like "Go to Felix's profile" which appear at the bottom of Reels.
tab_label = intent_lower.replace("tap ", "").replace(" tab", "").strip()
nav_candidates = [
n for n in candidates if n.y1 >= nav_zone_y and tab_label in (n.content_desc or "").lower()
n for n in candidates if n.y1 >= nav_zone_y and (n.content_desc or "").lower() == tab_label
]
if nav_candidates:
return nav_candidates[0]
@@ -95,13 +98,14 @@ class IntentResolver:
annotated_b64: Base64-encoded JPEG of the annotated screenshot.
box_map: Dict mapping box number → SpatialNode for coordinate lookup.
"""
from PIL import Image, ImageDraw, ImageFont
from PIL import ImageDraw
img = device.deviceV2.screenshot()
# Stage 1: Basic area filter + exclude system UI and notifications
pre_filtered = [
n for n in candidates
n
for n in candidates
if 200 < n.area < 400000
and "com.android.systemui" not in (n.resource_id or "")
and "notification:" not in (n.content_desc or "").lower()
@@ -113,8 +117,10 @@ class IntentResolver:
# redundant sub-nodes (e.g., followers_label inside followers_stacked).
def _is_contained(child: SpatialNode, parent: SpatialNode) -> bool:
return (
parent.x1 <= child.x1 and parent.y1 <= child.y1
and parent.x2 >= child.x2 and parent.y2 >= child.y2
parent.x1 <= child.x1
and parent.y1 <= child.y1
and parent.x2 >= child.x2
and parent.y2 >= child.y2
and parent is not child
)
@@ -131,9 +137,16 @@ class IntentResolver:
# Color palette for distinct boxes
colors = [
(255, 0, 0), (0, 200, 0), (0, 0, 255), (255, 165, 0),
(128, 0, 128), (0, 200, 200), (255, 20, 147), (0, 128, 0),
(255, 215, 0), (70, 130, 180),
(255, 0, 0),
(0, 200, 0),
(0, 0, 255),
(255, 165, 0),
(128, 0, 128),
(0, 200, 200),
(255, 20, 147),
(0, 128, 0),
(255, 215, 0),
(70, 130, 180),
]
for i, node in enumerate(visible_candidates):
@@ -184,9 +197,7 @@ class IntentResolver:
from GramAddict.core.llm_provider import query_telepathic_llm
try:
annotated_b64, box_map = self._annotate_screenshot_with_candidates(
device, candidates
)
annotated_b64, box_map = self._annotate_screenshot_with_candidates(device, candidates)
except Exception as e:
logger.warning(f"⚠️ [Visual Discovery] Screenshot annotation failed: {e}")
return None
@@ -198,13 +209,35 @@ class IntentResolver:
model = getattr(cfg.args, "ai_telepathic_model", "llava:latest")
url = getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
# Build a compact legend of what each box contains
box_legend_lines = []
for idx in sorted(box_map.keys()):
node = box_map[idx]
label_parts = []
if node.content_desc:
label_parts.append(f"desc='{node.content_desc[:50]}'")
if node.text and node.text != node.content_desc:
label_parts.append(f"text='{node.text[:50]}'")
if not label_parts:
label_parts.append("(no visible text)")
box_legend_lines.append(f" [{idx}] {', '.join(label_parts)}")
box_legend = "\n".join(box_legend_lines)
prompt = (
f"You are looking at a mobile app screenshot with numbered bounding boxes drawn around interactive UI elements.\n"
f"Each box has a number label (0, 1, 2, ...) in a colored rectangle.\n\n"
f"Your task: Find the box number that best matches this intent: '{intent_description}'\n\n"
f"LOOK at the actual text, icons, and visual appearance inside each box.\n"
f"Do NOT guess based on position alone — read the actual content.\n\n"
f"Reply ONLY with a valid JSON object: {{\"box\": <number>}} or {{\"box\": null}} if no box matches."
f"Box legend (what each box contains):\n{box_legend}\n\n"
f"Your task: Find the box that is the INTERACTIVE CONTROL for this intent: '{intent_description}'\n\n"
f"CRITICAL RULES:\n"
f"- Match by VISUAL FUNCTION, not by text similarity.\n"
f" - 'like button' = a HEART-SHAPED ICON (♡/❤), usually labeled 'Like'.\n"
f" - 'follow button' = a button with the word 'Follow' displayed on it.\n"
f" - 'comment button' = a SPEECH BUBBLE ICON, labeled 'Comment'.\n"
f" - 'post author username' = the username text near the content.\n"
f"- A post caption that happens to contain the word 'like' is NOT a like button.\n"
f"- Text content (captions, descriptions, counts like 'View likes') are NOT interactive buttons.\n"
f"- If the exact interactive control is NOT visible on screen, return null. Do NOT pick the closest-looking thing.\n\n"
f'Reply ONLY with a valid JSON object: {{"box": <number>}} or {{"box": null}} if no box matches.'
)
try:
@@ -254,7 +287,8 @@ class IntentResolver:
filtered_candidates = [n for n in candidates if n.area < 500000]
if "profile" in intent_lower:
filtered_candidates = [
n for n in filtered_candidates
n
for n in filtered_candidates
if not any(kw in (n.resource_id or "").lower() for kw in ("tab", "navigation", "action_bar"))
]
if not filtered_candidates:
@@ -296,5 +330,3 @@ class IntentResolver:
logger.warning(f"⚠️ [IntentResolver] Text-based VLM resolution failed ({e}).")
return None