Directory structure cleanup and organization overhaul

This commit is contained in:
John McCardle 2025-07-10 22:10:27 -04:00
commit 98fc49a978
119 changed files with 10483 additions and 4042 deletions

View file

@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
Analysis of Issue #78: Middle Mouse Click sends 'C' keyboard event
BUG FOUND in GameEngine::processEvent() at src/GameEngine.cpp
The bug occurs in this code section:
```cpp
if (currentScene()->hasAction(actionCode))
{
std::string name = currentScene()->action(actionCode);
currentScene()->doAction(name, actionType);
}
else if (currentScene()->key_callable)
{
currentScene()->key_callable->call(ActionCode::key_str(event.key.code), actionType);
}
```
ISSUE: When a middle mouse button event occurs and there's no registered action for it,
the code falls through to the key_callable branch. However, it then tries to access
`event.key.code` from what is actually a mouse button event!
Since it's a union, `event.key.code` reads garbage data from the mouse event structure.
The middle mouse button has value 2, which coincidentally matches sf::Keyboard::C (also value 2),
causing the spurious 'C' keyboard event.
SOLUTION: The code should check the event type before accessing event-specific fields:
```cpp
else if (currentScene()->key_callable &&
(event.type == sf::Event::KeyPressed || event.type == sf::Event::KeyReleased))
{
currentScene()->key_callable->call(ActionCode::key_str(event.key.code), actionType);
}
```
TEST STATUS:
- Test Name: automation_click_issue78_test.py
- Method Tested: Middle mouse click behavior
- Pass/Fail: FAIL - Issue #78 confirmed to exist
- Error: Middle mouse clicks incorrectly trigger 'C' keyboard events
- Modifications: None needed - bug is in C++ code, not the test
The test correctly identifies the issue but cannot run in headless mode due to
requiring actual event processing through the game loop.
"""
import mcrfpy
import sys
print(__doc__)
# Demonstrate the issue conceptually
print("\nDemonstration of the bug:")
print("1. Middle mouse button value in SFML: 2")
print("2. Keyboard 'C' value in SFML: 2")
print("3. When processEvent reads event.key.code from a mouse event,")
print(" it gets the value 2, which ActionCode::key_str interprets as 'C'")
print("\nThe fix is simple: add an event type check before accessing key.code")
sys.exit(0)

View file

@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""Test for automation click methods - Related to issue #78 (Middle click sends 'C')"""
import mcrfpy
from datetime import datetime
# Try to import automation, but handle if it doesn't exist
try:
from mcrfpy import automation
HAS_AUTOMATION = True
print("SUCCESS: mcrfpy.automation module imported successfully")
except (ImportError, AttributeError) as e:
HAS_AUTOMATION = False
print(f"WARNING: mcrfpy.automation module not available - {e}")
print("The automation module may not be implemented yet")
# Track events
click_events = []
key_events = []
def click_handler(x, y, button):
"""Track click events"""
click_events.append((x, y, button))
print(f"Click received: ({x}, {y}, button={button})")
def key_handler(key, scancode=None):
"""Track keyboard events"""
key_events.append(key)
print(f"Key received: {key} (scancode: {scancode})")
def test_clicks():
"""Test various click types, especially middle click (Issue #78)"""
if not HAS_AUTOMATION:
print("SKIP - automation module not available")
print("The automation module may not be implemented yet")
return
# Create test scene
mcrfpy.createScene("click_test")
mcrfpy.setScene("click_test")
ui = mcrfpy.sceneUI("click_test")
# Set up keyboard handler to detect Issue #78
mcrfpy.keypressScene(key_handler)
# Create clickable frame
frame = mcrfpy.Frame(50, 50, 300, 200,
fill_color=mcrfpy.Color(100, 100, 200),
outline_color=mcrfpy.Color(255, 255, 255),
outline=2.0)
frame.click = click_handler
ui.append(frame)
caption = mcrfpy.Caption(mcrfpy.Vector(60, 60),
text="Click Test Area",
fill_color=mcrfpy.Color(255, 255, 255))
frame.children.append(caption)
# Test different click types
print("Testing click types...")
# Left click
try:
automation.click(200, 150)
print("✓ Left click sent")
except Exception as e:
print(f"✗ Left click failed: {e}")
# Right click
try:
automation.rightClick(200, 150)
print("✓ Right click sent")
except Exception as e:
print(f"✗ Right click failed: {e}")
# Middle click - This is Issue #78
try:
automation.middleClick(200, 150)
print("✓ Middle click sent")
except Exception as e:
print(f"✗ Middle click failed: {e}")
# Double click
try:
automation.doubleClick(200, 150)
print("✓ Double click sent")
except Exception as e:
print(f"✗ Double click failed: {e}")
# Triple click
try:
automation.tripleClick(200, 150)
print("✓ Triple click sent")
except Exception as e:
print(f"✗ Triple click failed: {e}")
# Click with specific button parameter
try:
automation.click(200, 150, button='middle')
print("✓ Click with button='middle' sent")
except Exception as e:
print(f"✗ Click with button parameter failed: {e}")
# Check results after a delay
def check_results(runtime):
print(f"\nClick events received: {len(click_events)}")
print(f"Keyboard events received: {len(key_events)}")
# Check for Issue #78
if any('C' in str(event) or ord('C') == event for event in key_events):
print("✗ ISSUE #78 CONFIRMED: Middle click sent 'C' keyboard event!")
else:
print("✓ No spurious 'C' keyboard events detected")
# Analyze click events
for event in click_events:
print(f" Click: {event}")
# Take screenshot
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"test_clicks_issue78_{timestamp}.png"
automation.screenshot(filename)
print(f"Screenshot saved: {filename}")
if len(click_events) > 0:
print("PASS - Clicks detected")
else:
print("FAIL - No clicks detected (may be headless limitation)")
mcrfpy.delTimer("check_results")
mcrfpy.setTimer("check_results", check_results, 2000)
# Set up timer to run test
print("Setting up test timer...")
mcrfpy.setTimer("test", test_clicks, 1000)
# Cancel timer after running once
def cleanup():
mcrfpy.delTimer("test")
mcrfpy.delTimer("cleanup")
mcrfpy.setTimer("cleanup", cleanup, 1100)
# Exit after test completes
def exit_test():
print("\nTest completed - exiting")
import sys
sys.exit(0)
mcrfpy.setTimer("exit", exit_test, 5000)
print("Test script initialized, waiting for timers...")

View file

@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Test for mcrfpy.automation.screenshot()"""
import mcrfpy
from mcrfpy import automation
from datetime import datetime
import os
import sys
import time
runs = 0
def test_screenshot(*args):
"""Test screenshot functionality"""
#global runs
#runs += 1
#if runs < 2:
# print("tick")
# return
#print("tock")
#mcrfpy.delTimer("timer1")
# Create a scene with some visual elements
mcrfpy.createScene("screenshot_test")
mcrfpy.setScene("screenshot_test")
ui = mcrfpy.sceneUI("screenshot_test")
# Add some colorful elements
frame1 = mcrfpy.Frame(10, 10, 200, 150,
fill_color=mcrfpy.Color(255, 0, 0),
outline_color=mcrfpy.Color(255, 255, 255),
outline=3.0)
ui.append(frame1)
frame2 = mcrfpy.Frame(220, 10, 200, 150,
fill_color=mcrfpy.Color(0, 255, 0),
outline_color=mcrfpy.Color(0, 0, 0),
outline=2.0)
ui.append(frame2)
caption = mcrfpy.Caption(mcrfpy.Vector(10, 170),
text="Screenshot Test Scene",
fill_color=mcrfpy.Color(255, 255, 0))
caption.size = 24
ui.append(caption)
# Test multiple screenshots
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filenames = []
# Test 1: Basic screenshot
try:
filename1 = f"test_screenshot_basic_{timestamp}.png"
result = automation.screenshot(filename1)
filenames.append(filename1)
print(f"✓ Basic screenshot saved: {filename1} (result: {result})")
except Exception as e:
print(f"✗ Basic screenshot failed: {e}")
print("FAIL")
sys.exit(1)
# Test 2: Screenshot with special characters in filename
try:
filename2 = f"test_screenshot_special_chars_{timestamp}_test.png"
result = automation.screenshot(filename2)
filenames.append(filename2)
print(f"✓ Screenshot with special filename saved: {filename2} (result: {result})")
except Exception as e:
print(f"✗ Special filename screenshot failed: {e}")
# Test 3: Invalid filename (if applicable)
try:
result = automation.screenshot("")
print(f"✗ Empty filename should fail but returned: {result}")
except Exception as e:
print(f"✓ Empty filename correctly rejected: {e}")
# Check files exist immediately
files_found = 0
for filename in filenames:
if os.path.exists(filename):
size = os.path.getsize(filename)
print(f"✓ File exists: {filename} ({size} bytes)")
files_found += 1
else:
print(f"✗ File not found: {filename}")
if files_found == len(filenames):
print("PASS")
sys.exit(0)
else:
print("FAIL")
sys.exit(1)
print("Set callback")
mcrfpy.setTimer("timer1", test_screenshot, 1000)
# Run the test immediately
#test_screenshot()

View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""Simple test for mcrfpy.automation.screenshot()"""
import mcrfpy
from mcrfpy import automation
import os
import sys
# Create a simple scene
mcrfpy.createScene("test")
mcrfpy.setScene("test")
# Take a screenshot immediately
try:
filename = "test_screenshot.png"
result = automation.screenshot(filename)
print(f"Screenshot result: {result}")
# Check if file exists
if os.path.exists(filename):
size = os.path.getsize(filename)
print(f"PASS - Screenshot saved: {filename} ({size} bytes)")
else:
print(f"FAIL - Screenshot file not created: {filename}")
except Exception as e:
print(f"FAIL - Screenshot error: {e}")
import traceback
traceback.print_exc()
# Exit immediately
sys.exit(0)