184 lines
5.7 KiB
Python
184 lines
5.7 KiB
Python
import logging
|
|
import os
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
|
|
def pytest_addoption(parser):
|
|
parser.addoption(
|
|
"--live",
|
|
action="store_true",
|
|
default=False,
|
|
help="run tests against a live ADB device (disable DeviceFacade mocks)",
|
|
)
|
|
|
|
|
|
MagicMock.app_id = "com.instagram.android"
|
|
MagicMock._get_current_app = MagicMock(return_value="com.instagram.android")
|
|
|
|
|
|
class MockArgs:
|
|
def __init__(self, **kwargs):
|
|
for k, v in kwargs.items():
|
|
setattr(self, k, v)
|
|
|
|
|
|
class MockConfigs:
|
|
def __init__(self, args):
|
|
self.args = args
|
|
|
|
|
|
from unittest.mock import MagicMock, create_autospec
|
|
|
|
from GramAddict.core.device_facade import DeviceFacade
|
|
from GramAddict.core.telepathic_engine import TelepathicEngine
|
|
|
|
|
|
def create_mock_device():
|
|
mock = create_autospec(DeviceFacade, instance=True)
|
|
mock.app_id = "com.instagram.android"
|
|
mock.device_id = "test_device"
|
|
|
|
mock.info = {"displayWidth": 1080, "displayHeight": 2400}
|
|
mock.get_info.return_value = {"displayWidth": 1080, "displayHeight": 2400}
|
|
mock.cm_to_pixels.side_effect = lambda cm: int(cm * 10)
|
|
mock.shell.return_value = "" # Ensure SendEventInjector detection gets a string
|
|
import uuid
|
|
|
|
mock.dump_hierarchy.side_effect = (
|
|
lambda: f'<hierarchy><node resource-id="com.instagram.android:id/row_feed_photo_profile_name" bounds="[0,200][1080,260]" text="testuser" /><node resource-id="com.instagram.android:id/row_comment_imageview" bounds="[10,10][20,20]" content-desc="Story" text="following" /><node resource-id="com.instagram.android:id/button_like" bounds="[50,50][60,60]" /><node resource-id="com.instagram.android:id/reel_viewer" /><node sid="{uuid.uuid4()}" /></hierarchy>'
|
|
)
|
|
|
|
return mock
|
|
|
|
|
|
def create_mock_telepathic_engine():
|
|
mock = create_autospec(TelepathicEngine, instance=True)
|
|
mock.find_best_node.return_value = {"x": 500, "y": 500, "confidence": 0.9}
|
|
mock.evaluate_profile_vibe.return_value = {
|
|
"quality_score": 8,
|
|
"matches_niche": True,
|
|
"reason": "Mocked positive vibe",
|
|
}
|
|
mock.evaluate_grid_visuals.return_value = {
|
|
"x": 500,
|
|
"y": 500,
|
|
"score": 0.99,
|
|
"semantic": "Mocked matching grid cell",
|
|
"source": "vlm_grid",
|
|
}
|
|
mock.find_best_node.return_value = {"x": 500, "y": 500, "semantic_string": "dummy node"}
|
|
return mock
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_logger():
|
|
return logging.getLogger("test")
|
|
|
|
|
|
@pytest.fixture
|
|
def device(request):
|
|
if request.config.getoption("--live"):
|
|
import os
|
|
|
|
import yaml
|
|
|
|
from GramAddict.core.device_facade import create_device
|
|
|
|
device_id = "emulator-5554"
|
|
app_id = "com.instagram.android"
|
|
|
|
config_path = "test_config.yml"
|
|
if os.path.exists(config_path):
|
|
try:
|
|
with open(config_path, "r", encoding="utf-8") as f:
|
|
config = yaml.safe_load(f)
|
|
if config:
|
|
device_id = config.get("device", device_id)
|
|
app_id = config.get("app-id", app_id)
|
|
except Exception as e:
|
|
print(f"⚠️ Warning: Could not load {config_path}: {e}")
|
|
|
|
print(f"🚀 Connecting to live device: {device_id} (App: {app_id})")
|
|
return create_device(device_id, app_id)
|
|
return create_mock_device()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_singletons():
|
|
"""Ensure all core engine singletons are fresh for each test."""
|
|
from GramAddict.core.behaviors import PluginRegistry
|
|
from GramAddict.core.goap import GoalExecutor
|
|
from GramAddict.core.physics.biomechanics import PhysicsBody
|
|
from GramAddict.core.physics.sendevent_injector import SendEventInjector
|
|
from GramAddict.core.qdrant_memory import QdrantBase
|
|
from GramAddict.core.situational_awareness import SituationalAwarenessEngine
|
|
from GramAddict.core.telepathic_engine import TelepathicEngine
|
|
|
|
TelepathicEngine.reset()
|
|
GoalExecutor.reset()
|
|
SituationalAwarenessEngine.reset()
|
|
PluginRegistry.reset()
|
|
PhysicsBody.reset()
|
|
SendEventInjector.reset()
|
|
|
|
QdrantBase._connection_failed_logged = False
|
|
|
|
from GramAddict.core.dojo_engine import DojoEngine
|
|
|
|
if hasattr(DojoEngine, "reset"):
|
|
DojoEngine.reset()
|
|
else:
|
|
DojoEngine._instance = None
|
|
|
|
# Aggressively wipe on-disk session files to prevent state leakage in tests
|
|
for f in [
|
|
"telepathic_memory.json",
|
|
"telepathic_blacklist.json",
|
|
"growth_brain_memory.json",
|
|
"gramaddict_nav_map.json",
|
|
"l2_channels_cache.json",
|
|
]:
|
|
if os.path.exists(f):
|
|
try:
|
|
os.remove(f)
|
|
except Exception:
|
|
pass
|
|
yield
|
|
|
|
# Post-test cleanup
|
|
PhysicsBody.reset()
|
|
SendEventInjector.reset()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def telepathic_mock(monkeypatch, request):
|
|
if request.config.getoption("--live"):
|
|
# TelepathicEngine is a singleton, allow it to run natively
|
|
return None
|
|
import GramAddict.core.telepathic_engine
|
|
|
|
engine = create_mock_telepathic_engine()
|
|
monkeypatch.setattr(GramAddict.core.telepathic_engine.TelepathicEngine, "get_instance", lambda: engine)
|
|
return engine
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_cognitive_stack():
|
|
stack = {
|
|
"dopamine": MagicMock(),
|
|
"darwin": MagicMock(),
|
|
"resonance": MagicMock(),
|
|
"active_inference": MagicMock(),
|
|
"growth_brain": MagicMock(),
|
|
"swarm": MagicMock(),
|
|
"radome": MagicMock(),
|
|
"nav_graph": MagicMock(),
|
|
"zero_engine": MagicMock(),
|
|
"crm": MagicMock(),
|
|
"telepathic": create_mock_telepathic_engine(),
|
|
}
|
|
stack["radome"].sanitize_xml.side_effect = lambda x: x
|
|
return stack
|